diff --git a/Makefile b/Makefile index 07bc925..8ee19ed 100644 --- a/Makefile +++ b/Makefile @@ -245,8 +245,8 @@ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ HOSTCC = gcc HOSTCXX = g++ -HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -HOSTCXXFLAGS = -O2 +HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O3 -fomit-frame-pointer +HOSTCXXFLAGS = -O3 # Decide whether to build built-in, modular, or both. # Normally, just do built-in. @@ -561,7 +561,7 @@ all: vmlinux ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os else -KBUILD_CFLAGS += -O2 +KBUILD_CFLAGS += -O3 endif include $(srctree)/arch/$(SRCARCH)/Makefile diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index b122adc..10e3bdd 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -18,12 +18,12 @@ config MIPS select HAVE_KRETPROBES select RTC_LIB if !MACH_LOONGSON select GENERIC_ATOMIC64 if !64BIT - select HAVE_DMA_ATTRS - select HAVE_DMA_API_DEBUG select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select HAVE_ARCH_JUMP_LABEL + select HAVE_DMA_ATTRS + select HAVE_DMA_API_DEBUG select IRQ_FORCED_THREADING menu "Machine selection" @@ -250,7 +250,7 @@ config LASAT config MACH_LOONGSON bool "Loongson family of machines" - select SYS_SUPPORTS_ZBOOT + select SYS_SUPPORTS_ZBOOT_UART16550 help This enables the support of Loongson family of machines. @@ -897,6 +897,60 @@ config CSRC_R4K select CSRC_R4K_LIB bool +config MIPS_USER_RDTSC + bool "Emulate rdtsc instruction for MIPS" + depends on CSRC_R4K && MIPS32_O32 + default n + help + This optoin enables the Emulated rdtsc support for MIPS, which allows + the user-space applications read the R4k count directly. Currently, + this only support the CONFIG_MIPS32_O32 and R4K, but future, we may + add support for scall64-{n32,64}.S and scall32-32.S and for the count + registers provided by the other MIPS variants. + + This emulation based on the syscall instruction, by default, the + syscall is encoded as 0x0000000c, except the 0xc, the other parts can + be encoded as specific meaning. when a syscall instruction is issued, + through checking the encoding of the instruction, when the encoding + is the generic 0x000000c, we do the generic syscall work, if + something other is encoded in, we can do relevant things, except for + the light-weight things, such as read a register. herein, we read the + count register whenever there is something encoded in the syscall + instruction. In the future, we may be possible to abstract more + light-weight & frequently-used operations and add a + sys_call_table-like table to store the entries of some light-weight + operations and encode 1,2,3... into the syscall instruction and jump + to respective entry for diffrent numbers, as a result, we get + fast-syscall and which may speed up the user-space applications and + even be possibly improve the determinism. + + *Example* + + #include + #include + + /* + * Currently, our return value is only 32bit, In the long run, + * this should be uint64_t, just like clock_gettime(), but it + * should has high precision/low overhead than clock_gettime() + */ + uint32_t rdtsc(void) + { + /* + * Linux will store the value of the count register into + * the v0 register, which is just the return value of this + * function, so, please ignore the compiling warning. + */ + __asm__ __volatile__ ( + "syscall 1\n" + :::"$2"); + } + + int main(int argc, char *argv[]) + { + return printf("cycles: %u\n", rdtsc()); + } + config CSRC_SB1250 bool @@ -1187,8 +1241,6 @@ config CPU_LOONGSON2F bool "Loongson 2F" depends on SYS_HAS_CPU_LOONGSON2F select CPU_LOONGSON2 - select GENERIC_GPIO - select ARCH_REQUIRE_GPIOLIB help The Loongson 2F processor implements the MIPS III instruction set with many extensions. @@ -1523,7 +1575,8 @@ config CPU_LOONGSON2 bool select CPU_SUPPORTS_32BIT_KERNEL select CPU_SUPPORTS_64BIT_KERNEL - select CPU_SUPPORTS_HIGHMEM + select CPU_SUPPORTS_HIGHMEM if ! EMBEDDED + select ARCH_WANT_OPTIONAL_GPIOLIB config SYS_HAS_CPU_LOONGSON2E bool @@ -2060,7 +2113,7 @@ config SYS_SUPPORTS_SMARTMIPS config ARCH_FLATMEM_ENABLE def_bool y - depends on !NUMA && !CPU_LOONGSON2 + depends on !NUMA && !(CPU_LOONGSON2 && HIBERNATION) config ARCH_DISCONTIGMEM_ENABLE bool diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug index 83ed00a..bdb259d 100644 --- a/arch/mips/Kconfig.debug +++ b/arch/mips/Kconfig.debug @@ -7,9 +7,9 @@ config TRACE_IRQFLAGS_SUPPORT source "lib/Kconfig.debug" config EARLY_PRINTK - bool "Early printk" if EXPERT + bool "Early printk" depends on SYS_HAS_EARLY_PRINTK - default y + default n help This option enables special console drivers which allow the kernel to print messages very early in the bootup process. diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 53e3514..f1bdcc9 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -256,18 +256,19 @@ endif # Other need ECOFF, so we build a 32-bit ELF binary for them which we then # convert to ECOFF using elf2ecoff. # +quiet_cmd_32 = OBJCOPY $@ + cmd_32 = $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@ vmlinux.32: vmlinux - $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@ - - -#obj-$(CONFIG_KPROBES) += kprobes.o + $(call cmd,32) # # The 64-bit ELF tools are pretty broken so at this time we generate 64-bit # ELF files from 32-bit files by conversion. # +quiet_cmd_64 = OBJCOPY $@ + cmd_64 = $(OBJCOPY) -O $(64bit-bfd) $(OBJCOPYFLAGS) $< $@ vmlinux.64: vmlinux - $(OBJCOPY) -O $(64bit-bfd) $(OBJCOPYFLAGS) $< $@ + $(call cmd,64) all: $(all-y) diff --git a/arch/mips/bcm63xx/cpu.c b/arch/mips/bcm63xx/cpu.c index 7c7e4d4..5e5caae 100644 --- a/arch/mips/bcm63xx/cpu.c +++ b/arch/mips/bcm63xx/cpu.c @@ -297,7 +297,7 @@ void __init bcm63xx_cpu_init(void) /* soc registers location depends on cpu type */ expected_cpu_id = 0; - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_BMIPS3300: if ((read_c0_prid() & 0xff00) == PRID_IMP_BMIPS3300_ALT) { expected_cpu_id = BCM6348_CPU_ID; diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile index 5042d51..c653ebd 100644 --- a/arch/mips/boot/compressed/Makefile +++ b/arch/mips/boot/compressed/Makefile @@ -28,9 +28,10 @@ KBUILD_AFLAGS := $(LINUXINCLUDE) $(KBUILD_AFLAGS) -D__ASSEMBLY__ \ targets := head.o decompress.o dbg.o uart-16550.o uart-alchemy.o # decompressor objects (linked with vmlinuz) -vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/dbg.o +vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o ifdef CONFIG_DEBUG_ZBOOT +vmlinuzobjs-y += $(obj)/dbg.o vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART16550) += $(obj)/uart-16550.o vmlinuzobjs-$(CONFIG_MIPS_ALCHEMY) += $(obj)/uart-alchemy.o endif @@ -67,9 +68,18 @@ quiet_cmd_zld = LD $@ cmd_zld = $(LD) $(LDFLAGS) -Ttext $(VMLINUZ_LOAD_ADDRESS) -T $< $(vmlinuzobjs-y) -o $@ quiet_cmd_strip = STRIP $@ cmd_strip = $(STRIP) -s $@ +ifdef CONFIG_EMBEDDED +quiet_cmd_sstrip = SSTRIP $@ + cmd_sstrip = $(srctree)/scripts/sstrip.sh $@ +endif vmlinuz: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr $(call cmd,zld) $(call cmd,strip) + $(call cmd,sstrip) + +vmlinuz.unsstrip: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr + $(call cmd,zld) + $(call cmd,strip) # # Some DECstations need all possible sections of an ECOFF executable @@ -82,14 +92,14 @@ endif hostprogs-y += ../elf2ecoff ifdef CONFIG_32BIT - VMLINUZ = vmlinuz + VMLINUZ = vmlinuz.unsstrip else VMLINUZ = vmlinuz.32 endif quiet_cmd_32 = OBJCOPY $@ cmd_32 = $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@ -vmlinuz.32: vmlinuz +vmlinuz.32: vmlinuz.unsstrip $(call cmd,32) quiet_cmd_ecoff = ECOFF $@ @@ -98,11 +108,11 @@ vmlinuz.ecoff: $(obj)/../elf2ecoff $(VMLINUZ) $(call cmd,ecoff) OBJCOPYFLAGS_vmlinuz.bin := $(OBJCOPYFLAGS) -O binary -vmlinuz.bin: vmlinuz +vmlinuz.bin: vmlinuz.unsstrip $(call cmd,objcopy) OBJCOPYFLAGS_vmlinuz.srec := $(OBJCOPYFLAGS) -S -O srec -vmlinuz.srec: vmlinuz +vmlinuz.srec: vmlinuz.unsstrip $(call cmd,objcopy) -clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec} +clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec,unsstrip} diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c index 5cad0fa..2f761a5 100644 --- a/arch/mips/boot/compressed/decompress.c +++ b/arch/mips/boot/compressed/decompress.c @@ -27,8 +27,13 @@ unsigned long free_mem_end_ptr; extern unsigned char __image_begin, __image_end; /* debug interfaces */ +#ifdef CONFIG_DEBUG_ZBOOT extern void puts(const char *s); extern void puthex(unsigned long long val); +#else +#define puts(s) +#define puthex(val) +#endif void error(char *x) { diff --git a/arch/mips/boot/compressed/ld.script b/arch/mips/boot/compressed/ld.script index 8e6b07c..99ca111 100644 --- a/arch/mips/boot/compressed/ld.script +++ b/arch/mips/boot/compressed/ld.script @@ -46,5 +46,6 @@ SECTIONS *(.reginfo) *(.comment) *(.note) + *(.gnu.attributes) } } diff --git a/arch/mips/configs/fuloong2e_defconfig b/arch/mips/configs/fuloong2e_defconfig index e5b73de..1e02d0b 100644 --- a/arch/mips/configs/fuloong2e_defconfig +++ b/arch/mips/configs/fuloong2e_defconfig @@ -12,7 +12,6 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_PID_NS=y @@ -222,7 +221,8 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_REISERFS_FS=m CONFIG_AUTOFS_FS=y CONFIG_AUTOFS4_FS=y -CONFIG_FUSE_FS=y +CONFIG_FUSE_FS=m +CONFIG_CUSE=m CONFIG_ISO9660_FS=m CONFIG_JOLIET=y CONFIG_ZISOFS=y @@ -260,9 +260,9 @@ CONFIG_NLS_CODEPAGE_936=y CONFIG_NLS_ISO8859_1=y CONFIG_NLS_UTF8=y # CONFIG_ENABLE_MUST_CHECK is not set -CONFIG_DEBUG_FS=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_SYSCTL_SYSCALL_CHECK=y +# CONFIG_EARLY_PRINTK is not set CONFIG_CRYPTO_FIPS=y CONFIG_CRYPTO_AUTHENC=m CONFIG_CRYPTO_CCM=m diff --git a/arch/mips/configs/gdium_minimal_defconfig b/arch/mips/configs/gdium_minimal_defconfig new file mode 100644 index 0000000..595b414 --- /dev/null +++ b/arch/mips/configs/gdium_minimal_defconfig @@ -0,0 +1,125 @@ +CONFIG_MACH_LOONGSON=y +CONFIG_DEXXON_GDIUM=y +CONFIG_64BIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZMA=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_EMBEDDED=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_PCI=y +CONFIG_MIPS32_COMPAT=y +CONFIG_MIPS32_O32=y +CONFIG_MIPS32_N32=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6 is not set +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_NET_PCI=y +CONFIG_8139TOO=y +CONFIG_R8169=y +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_MOUSE_PS2_ALPS is not set +# CONFIG_MOUSE_PS2_LOGIPS2PP is not set +# CONFIG_MOUSE_PS2_TRACKPOINT is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_SENSORS_LM75=y +CONFIG_MFD_SM501=y +CONFIG_MFD_SM501_GPIO=y +CONFIG_FB=y +CONFIG_FB_SIS=y +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +CONFIG_FB_SM501=y +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_HIDRAW=y +CONFIG_USB_HIDDEV=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_ZEROPLUS=y +CONFIG_ZEROPLUS_FF=y +CONFIG_USB=y +# CONFIG_USB_DEVICE_CLASS is not set +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_LIBUSUAL=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_M41T80=y +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_FB_SM7XX=y +# CONFIG_MIPS_PLATFORM_DEVICES is not set +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_TMPFS=y +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_ASCII=y +CONFIG_NLS_UTF8=y +CONFIG_FRAME_WARN=1024 +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set diff --git a/arch/mips/configs/gdium_small_defconfig b/arch/mips/configs/gdium_small_defconfig new file mode 100644 index 0000000..641daf5 --- /dev/null +++ b/arch/mips/configs/gdium_small_defconfig @@ -0,0 +1,150 @@ +CONFIG_MACH_LOONGSON=y +CONFIG_DEXXON_GDIUM=y +CONFIG_64BIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZMA=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_EMBEDDED=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_PCI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_MIPS32_COMPAT=y +CONFIG_MIPS32_O32=y +CONFIG_MIPS32_N32=y +CONFIG_PM=y +CONFIG_HIBERNATION=y +CONFIG_PM_STD_PARTITION="/dev/sda4" +CONFIG_R4K_TIMER_FOR_CPUFREQ=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_LOONGSON2_CPUFREQ=m +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6 is not set +CONFIG_CFG80211=y +# CONFIG_CFG80211_DEFAULT_PS is not set +# CONFIG_CFG80211_WEXT is not set +CONFIG_LIB80211=y +CONFIG_MAC80211=y +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_MISC_DEVICES=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +# CONFIG_SATA_PMP is not set +CONFIG_PATA_AMD=y +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_NET_PCI=y +CONFIG_8139TOO=y +CONFIG_R8169=y +# CONFIG_NETDEV_10000 is not set +CONFIG_RT2X00=m +CONFIG_RT61PCI=m +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_EVDEV=y +CONFIG_MOUSE_PS2=m +# CONFIG_MOUSE_PS2_ALPS is not set +# CONFIG_MOUSE_PS2_LOGIPS2PP is not set +# CONFIG_MOUSE_PS2_TRACKPOINT is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_HWMON=m +CONFIG_SENSORS_LM75=m +CONFIG_MFD_SM501=y +CONFIG_MFD_SM501_GPIO=y +CONFIG_FB=y +CONFIG_FB_SIS=y +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +CONFIG_FB_SM501=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_PWM=m +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_CS5535AUDIO=m +CONFIG_HIDRAW=y +CONFIG_USB_HIDDEV=y +CONFIG_HID_GDIUM=y +CONFIG_USB=y +# CONFIG_USB_DEVICE_CLASS is not set +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_LIBUSUAL=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_M41T80=y +CONFIG_RTC_DRV_M41T80_WDT=y +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_FB_SM7XX=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FANOTIFY=y +CONFIG_TMPFS=y +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_ASCII=y +CONFIG_NLS_UTF8=y +CONFIG_FRAME_WARN=1024 +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_EARLY_PRINTK is not set diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig index b6acd2f..abc42e0 100644 --- a/arch/mips/configs/lemote2f_defconfig +++ b/arch/mips/configs/lemote2f_defconfig @@ -1,27 +1,27 @@ CONFIG_MACH_LOONGSON=y CONFIG_LEMOTE_MACH2F=y -CONFIG_CS5536_MFGPT=y CONFIG_64BIT=y +CONFIG_KSM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y -CONFIG_KEXEC=y -# CONFIG_SECCOMP is not set CONFIG_EXPERIMENTAL=y # CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZMA=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_AUDIT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=15 -CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y -CONFIG_RD_BZIP2=y -CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EXPERT=y CONFIG_PROFILING=y CONFIG_OPROFILE=m CONFIG_MODULES=y @@ -34,20 +34,18 @@ CONFIG_BINFMT_MISC=m CONFIG_MIPS32_COMPAT=y CONFIG_MIPS32_O32=y CONFIG_MIPS32_N32=y -CONFIG_PM=y CONFIG_HIBERNATION=y -CONFIG_PM_STD_PARTITION="/dev/hda3" +CONFIG_PM_STD_PARTITION="/dev/sda3" CONFIG_PM_RUNTIME=y +CONFIG_R4K_TIMER_FOR_CPUFREQ=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_DEBUG=y -CONFIG_CPU_FREQ_STAT=m CONFIG_CPU_FREQ_STAT_DETAILS=y -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m -CONFIG_CPU_FREQ_GOV_USERSPACE=m -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_LOONGSON2_CPUFREQ=m -CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_XFRM_USER=m @@ -59,7 +57,6 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_MULTIPATH=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_NET_IPIP=m -CONFIG_NET_IPGRE=m CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y @@ -79,12 +76,249 @@ CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_NETWORK_SECMARK=y CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NETFILTER_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m +CONFIG_IP_VS_FTP=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_ULOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_DCCP=m +CONFIG_IP_SCTP=m +CONFIG_RDS=m +CONFIG_RDS_TCP=m +CONFIG_TIPC=m +CONFIG_TIPC_ADVANCED=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +CONFIG_ATM_CLIP_NO_ICMP=y +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +CONFIG_ATM_BR2684_IPFILTER=y +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_IPX=m +CONFIG_ATALK=m CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_PKTGEN=m +CONFIG_CAN=m +CONFIG_CAN_RAW=m +CONFIG_CAN_BCM=m +CONFIG_CAN_VCAN=m +CONFIG_CAN_DEV=m +CONFIG_CAN_CALC_BITTIMING=y +CONFIG_CAN_SJA1000=m +CONFIG_CAN_SJA1000_ISA=m +CONFIG_CAN_SJA1000_PLATFORM=m +CONFIG_CAN_EMS_PCI=m +CONFIG_CAN_KVASER_PCI=m +CONFIG_CAN_PLX_PCI=m +CONFIG_CAN_EMS_USB=m +CONFIG_CAN_ESD_USB2=m +CONFIG_IRDA=m +CONFIG_IRLAN=m +CONFIG_IRCOMM=m +CONFIG_IRTTY_SIR=m +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_TOIM3232_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +CONFIG_ACT200L_DONGLE=m +CONFIG_KINGSUN_DONGLE=m +CONFIG_KSDAZZLE_DONGLE=m +CONFIG_KS959_DONGLE=m +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_VLSI_FIR=m +CONFIG_MCS_FIR=m CONFIG_BT=m CONFIG_BT_L2CAP=y CONFIG_BT_SCO=y @@ -95,30 +329,114 @@ CONFIG_BT_BNEP_MC_FILTER=y CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=m CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m CONFIG_CFG80211=m CONFIG_LIB80211=m CONFIG_LIB80211_DEBUG=y CONFIG_MAC80211=m -CONFIG_MAC80211_LEDS=y +CONFIG_WIMAX=m CONFIG_RFKILL=m -CONFIG_RFKILL_INPUT=y +CONFIG_CAIF=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -# CONFIG_MISC_DEVICES is not set -CONFIG_IDE=y -CONFIG_IDE_TASK_IOCTL=y -# CONFIG_IDEPCI_PCIBUS_ORDER is not set -CONFIG_BLK_DEV_AMD74XX=y -CONFIG_SCSI=m -CONFIG_BLK_DEV_SD=m +CONFIG_MISC_DEVICES=y +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_LEGACY=m +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y CONFIG_CHR_DEV_SG=m +CONFIG_CHR_DEV_SCH=m CONFIG_SCSI_MULTI_LUN=y -# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_FC_TGT_ATTRS=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_SRP_ATTRS=m +CONFIG_SCSI_SRP_TGT_ATTRS=y +CONFIG_ISCSI_TCP=m +CONFIG_SCSI_CXGB3_ISCSI=m +CONFIG_SCSI_BNX2_ISCSI=m +CONFIG_BE2ISCSI=m +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_HPSA=m +CONFIG_SCSI_3W_9XXX=m +CONFIG_SCSI_3W_SAS=m +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +CONFIG_SCSI_AIC7XXX_OLD=m +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +CONFIG_SCSI_AIC94XX=m +# CONFIG_AIC94XX_DEBUG is not set +CONFIG_SCSI_MVSAS=m +# CONFIG_SCSI_MVSAS_DEBUG is not set +CONFIG_MEGARAID_SAS=m +CONFIG_SCSI_MPT2SAS=m +CONFIG_SCSI_HPTIOP=m +CONFIG_FCOE=m +CONFIG_SCSI_STEX=m +CONFIG_SCSI_QLA_FC=m +CONFIG_SCSI_QLA_ISCSI=m +CONFIG_SCSI_LPFC=m +CONFIG_SCSI_PMCRAID=m +CONFIG_SCSI_PM8001=m +CONFIG_SCSI_SRP=m +CONFIG_SCSI_BFA_FC=m +CONFIG_SCSI_DH=m +CONFIG_SCSI_DH_RDAC=m +CONFIG_SCSI_DH_HP_SW=m +CONFIG_SCSI_DH_EMC=m +CONFIG_SCSI_DH_ALUA=m +CONFIG_SCSI_OSD_INITIATOR=m +CONFIG_SCSI_OSD_ULD=m +CONFIG_ATA=y +CONFIG_SATA_AHCI=m +CONFIG_SATA_INIC162X=m +CONFIG_SATA_SIL24=m +CONFIG_PDC_ADMA=m +CONFIG_SATA_QSTOR=m +CONFIG_SATA_SX4=m +CONFIG_ATA_PIIX=m +CONFIG_SATA_MV=m +CONFIG_SATA_NV=m +CONFIG_SATA_PROMISE=m +CONFIG_SATA_SIL=y +CONFIG_SATA_SIS=m +CONFIG_SATA_SVW=m +CONFIG_SATA_ULI=m +CONFIG_SATA_VIA=m +CONFIG_SATA_VITESSE=m +CONFIG_PATA_ARTOP=m +CONFIG_PATA_ATP867X=m +CONFIG_PATA_CMD64X=m +CONFIG_PATA_CS5536=y +CONFIG_PATA_IT821X=m +CONFIG_PATA_JMICRON=m +CONFIG_PATA_MARVELL=m +CONFIG_PATA_RDC=m +CONFIG_PATA_SCH=m +CONFIG_PATA_TOSHIBA=m +CONFIG_ATA_GENERIC=m CONFIG_MD=y CONFIG_BLK_DEV_MD=m CONFIG_MD_LINEAR=m @@ -129,7 +447,6 @@ CONFIG_MD_RAID456=m CONFIG_MD_MULTIPATH=m CONFIG_MD_FAULTY=m CONFIG_BLK_DEV_DM=m -CONFIG_DM_DEBUG=y CONFIG_DM_CRYPT=m CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m @@ -140,55 +457,192 @@ CONFIG_DM_MULTIPATH_QL=m CONFIG_DM_MULTIPATH_ST=m CONFIG_DM_DELAY=m CONFIG_DM_UEVENT=y -CONFIG_NETDEVICES=y +CONFIG_FUSION=y +CONFIG_FUSION_SPI=m +CONFIG_FUSION_FC=m +CONFIG_FUSION_SAS=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_MACVLAN=m CONFIG_TUN=m CONFIG_VETH=m +CONFIG_MII=y CONFIG_NET_ETHERNET=y CONFIG_NET_PCI=y -CONFIG_8139TOO=y -# CONFIG_8139TOO_PIO is not set -CONFIG_R8169=y -CONFIG_R8169_VLAN=y -# CONFIG_NETDEV_10000 is not set -CONFIG_USB_USBNET=m +CONFIG_8139TOO=m +CONFIG_R8169=m +CONFIG_AT76C50X_USB=m +CONFIG_USB_ZD1201=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_RTL8187B=m +CONFIG_ATH_COMMON=m +CONFIG_AR9170_USB=m +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_ZD1211RW=m +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_WAN=y +CONFIG_LANMEDIA=m +CONFIG_HDLC=m +CONFIG_HDLC_RAW=m +CONFIG_HDLC_RAW_ETH=m +CONFIG_HDLC_CISCO=m +CONFIG_HDLC_FR=m +CONFIG_HDLC_PPP=m +CONFIG_PCI200SYN=m +CONFIG_WANXL=m +CONFIG_PC300TOO=m +CONFIG_N2=m +CONFIG_C101=m +CONFIG_FARSYNC=m +CONFIG_DSCC4=m +CONFIG_DLCI=m +CONFIG_SDLA=m +CONFIG_ATM_DUMMY=m +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +CONFIG_ATM_ENI_DEBUG=y +CONFIG_CAIF_TTY=m +CONFIG_CAIF_SPI_SLAVE=m +CONFIG_PPP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_MPPE=m +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +CONFIG_PPPOL2TP=m +CONFIG_SLIP=m +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_NET_FC=y CONFIG_NETCONSOLE=m -CONFIG_NETCONSOLE_DYNAMIC=y -CONFIG_INPUT_POLLDEV=m +CONFIG_NETPOLL_TRAP=y +CONFIG_VMXNET3=m +CONFIG_PHONE=m +CONFIG_PHONE_IXJ=m CONFIG_INPUT_EVDEV=y -# CONFIG_MOUSE_PS2_ALPS is not set -# CONFIG_MOUSE_PS2_LOGIPS2PP is not set -# CONFIG_MOUSE_PS2_TRACKPOINT is not set +CONFIG_KEYBOARD_LKKBD=m +CONFIG_KEYBOARD_NEWTON=m +CONFIG_KEYBOARD_OPENCORES=m +CONFIG_KEYBOARD_STOWAWAY=m +CONFIG_KEYBOARD_SUNKBD=m +CONFIG_KEYBOARD_XTKBD=m CONFIG_MOUSE_APPLETOUCH=m -# CONFIG_SERIO_SERPORT is not set -CONFIG_SERIAL_NONSTANDARD=y +CONFIG_MOUSE_BCM5974=m +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_GTCO=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_USB_WACOM=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_AD7879=m +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_HAMPSHIRE=m +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_HTCPEN=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_WM97XX=m +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_TOUCHIT213=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_AD714X=m +CONFIG_INPUT_ATI_REMOTE=m +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_ADXL34X=m +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_NOZOMI=m +CONFIG_N_GSM=m CONFIG_SERIAL_8250=m -# CONFIG_SERIAL_8250_PCI is not set CONFIG_SERIAL_8250_NR_UARTS=16 CONFIG_SERIAL_8250_EXTENDED=y CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_FOURPORT=y -CONFIG_LEGACY_PTY_COUNT=16 CONFIG_HW_RANDOM=y CONFIG_RTC=y +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_MUX=m +CONFIG_I2C_MUX_PCA954x=m +CONFIG_I2C_OCORES=m +CONFIG_I2C_PARPORT_LIGHT=m +CONFIG_I2C_TINY_USB=m +CONFIG_SCx200_ACB=m +CONFIG_SPI=y +CONFIG_W1=m +CONFIG_W1_MASTER_DS2490=m +CONFIG_W1_SLAVE_THERM=m +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2431=m +CONFIG_W1_SLAVE_DS2433=m +CONFIG_W1_SLAVE_DS2433_CRC=y +CONFIG_W1_SLAVE_DS2760=m +CONFIG_W1_SLAVE_BQ27000=m CONFIG_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_SOFT_WATCHDOG=m +CONFIG_USBPCWATCHDOG=m CONFIG_MEDIA_SUPPORT=m CONFIG_VIDEO_DEV=m -CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_DVB_CORE=m CONFIG_VIDEO_VIVI=m CONFIG_USB_VIDEO_CLASS=m CONFIG_USB_M5602=m CONFIG_USB_STV06XX=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_BENQ=m CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m CONFIG_USB_GSPCA_ETOMS=m CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_JEILINJ=m CONFIG_USB_GSPCA_MARS=m CONFIG_USB_GSPCA_MR97310A=m CONFIG_USB_GSPCA_OV519=m CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7302=m CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SN9C2028=m CONFIG_USB_GSPCA_SN9C20X=m CONFIG_USB_GSPCA_SONIXB=m CONFIG_USB_GSPCA_SONIXJ=m @@ -198,30 +652,72 @@ CONFIG_USB_GSPCA_SPCA505=m CONFIG_USB_GSPCA_SPCA506=m CONFIG_USB_GSPCA_SPCA508=m CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_SPCA1528=m CONFIG_USB_GSPCA_SQ905=m CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_SQ930X=m CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STV0680=m CONFIG_USB_GSPCA_SUNPLUS=m CONFIG_USB_GSPCA_T613=m CONFIG_USB_GSPCA_TV8532=m CONFIG_USB_GSPCA_VC032X=m CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_DEBUGIFC=y +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_USBVISION=m CONFIG_USB_ET61X251=m CONFIG_USB_SN9C102=m +CONFIG_USB_PWC=m CONFIG_USB_ZR364XX=m CONFIG_USB_STKWEBCAM=m CONFIG_USB_S2255=m -# CONFIG_RADIO_ADAPTERS is not set +CONFIG_USB_DSBR=m +CONFIG_USB_MR800=m +CONFIG_DVB_USB=m +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_FRIIO=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_TTUSB_BUDGET=m +CONFIG_DVB_TTUSB_DEC=m CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y CONFIG_FIRMWARE_EDID=y -CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y CONFIG_FB_SIS=y CONFIG_FB_SIS_300=y CONFIG_FB_SIS_315=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_FB_UDL=m CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_GENERIC=m # CONFIG_VGA_CONSOLE is not set @@ -238,8 +734,6 @@ CONFIG_FONT_SUN8x16=y CONFIG_FONT_SUN12x22=y CONFIG_FONT_10x18=y CONFIG_LOGO=y -# CONFIG_LOGO_LINUX_MONO is not set -# CONFIG_LOGO_LINUX_VGA16 is not set CONFIG_SOUND=m CONFIG_SND=m CONFIG_SND_SEQUENCER=m @@ -255,30 +749,18 @@ CONFIG_SND_MPU401=m CONFIG_SND_AC97_POWER_SAVE=y CONFIG_SND_AC97_POWER_SAVE_DEFAULT=10 CONFIG_SND_CS5535AUDIO=m -# CONFIG_SND_MIPS is not set CONFIG_SND_USB_AUDIO=m CONFIG_SND_USB_CAIAQ=m CONFIG_SND_USB_CAIAQ_INPUT=y CONFIG_HIDRAW=y +CONFIG_USB_HID=m CONFIG_USB_HIDDEV=y -CONFIG_HID_A4TECH=m -CONFIG_HID_APPLE=m -CONFIG_HID_BELKIN=m -CONFIG_HID_CHERRY=m -CONFIG_HID_CHICONY=m -CONFIG_HID_CYPRESS=m CONFIG_HID_DRAGONRISE=m CONFIG_DRAGONRISE_FF=y -CONFIG_HID_EZKEY=m -CONFIG_HID_KYE=m CONFIG_HID_GYRATION=m CONFIG_HID_TWINHAN=m -CONFIG_HID_KENSINGTON=m -CONFIG_HID_LOGITECH=m CONFIG_LOGITECH_FF=y CONFIG_LOGIRUMBLEPAD2_FF=y -CONFIG_HID_MICROSOFT=m -CONFIG_HID_MONTEREY=m CONFIG_HID_NTRIG=m CONFIG_HID_PANTHERLORD=m CONFIG_PANTHERLORD_FF=y @@ -293,19 +775,14 @@ CONFIG_SMARTJOYPLUS_FF=y CONFIG_HID_TOPSEED=m CONFIG_HID_THRUSTMASTER=m CONFIG_THRUSTMASTER_FF=y -CONFIG_HID_WACOM=m CONFIG_HID_ZEROPLUS=m CONFIG_ZEROPLUS_FF=y CONFIG_USB=y -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_DEVICE_CLASS is not set CONFIG_USB_DYNAMIC_MINORS=y CONFIG_USB_SUSPEND=y -CONFIG_USB_OTG_WHITELIST=y -CONFIG_USB_MON=y +CONFIG_USB_MON=m CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_ROOT_HUB_TT=y -# CONFIG_USB_EHCI_TT_NEWSCHED is not set CONFIG_USB_OHCI_HCD=y CONFIG_USB_UHCI_HCD=m CONFIG_USB_WHCI_HCD=m @@ -313,7 +790,7 @@ CONFIG_USB_HWA_HCD=m CONFIG_USB_ACM=m CONFIG_USB_PRINTER=m CONFIG_USB_WDM=m -CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE=y CONFIG_USB_STORAGE_DATAFAB=m CONFIG_USB_STORAGE_FREECOM=m CONFIG_USB_STORAGE_ISD200=m @@ -322,22 +799,123 @@ CONFIG_USB_STORAGE_SDDR09=m CONFIG_USB_STORAGE_SDDR55=m CONFIG_USB_STORAGE_JUMPSHOT=m CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_CYPRESS_ATACB=m CONFIG_USB_LIBUSUAL=y +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m CONFIG_USB_SERIAL=m CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MOTOROLA=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIEMENS_MPI=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m +CONFIG_USB_SERIAL_ZIO=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m CONFIG_USB_LED=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +CONFIG_USB_APPLEDISPLAY=m +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_IOWARRIOR=m +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_CXACRU=m +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_XUSBATM=m CONFIG_USB_GADGET=m CONFIG_USB_GADGET_M66592=y +CONFIG_USB_ZERO=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_WEBCAM=m +CONFIG_NOP_USB_XCEIV=m CONFIG_MMC=m CONFIG_LEDS_CLASS=y CONFIG_STAGING=y # CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_PRISM2_USB=m +CONFIG_R8187SE=m +CONFIG_RTL8192E=m +CONFIG_USB_SERIAL_QUATECH2=m +CONFIG_USB_SERIAL_QUATECH_USB2=m CONFIG_FB_SM7XX=y -CONFIG_EXT2_FS=m -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_EXT3_FS_SECURITY=y +CONFIG_EASYCAP=m CONFIG_EXT4_FS=y CONFIG_REISERFS_FS=m CONFIG_REISERFS_PROC_INFO=y @@ -347,16 +925,17 @@ CONFIG_JFS_POSIX_ACL=y CONFIG_XFS_FS=m CONFIG_XFS_QUOTA=y CONFIG_XFS_POSIX_ACL=y -CONFIG_BTRFS_FS=m -CONFIG_QUOTA=y -CONFIG_QFMT_V2=m -CONFIG_AUTOFS_FS=m +CONFIG_GFS2_FS=m +CONFIG_BTRFS_FS=y CONFIG_AUTOFS4_FS=m +CONFIG_FUSE_FS=m +CONFIG_CUSE=m CONFIG_FSCACHE=m CONFIG_CACHEFILES=m CONFIG_ISO9660_FS=m CONFIG_JOLIET=y CONFIG_ZISOFS=y +CONFIG_UDF_FS=m CONFIG_MSDOS_FS=m CONFIG_VFAT_FS=m CONFIG_NTFS_FS=m @@ -367,6 +946,7 @@ CONFIG_CRAMFS=m CONFIG_SQUASHFS=m CONFIG_SQUASHFS_EMBEDDED=y CONFIG_ROMFS_FS=m +CONFIG_UFS_FS=m CONFIG_NFS_FS=m CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y @@ -412,15 +992,15 @@ CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_NLS_UTF8=y -CONFIG_PRINTK_TIME=y CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y CONFIG_STRIP_ASM_SYMS=y CONFIG_DEBUG_FS=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_DEBUG_KERNEL=y +CONFIG_TIMER_STATS=y CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_KEYS=y CONFIG_KEYS_DEBUG_PROC_KEYS=y -CONFIG_CRYPTO_FIPS=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_AUTHENC=m @@ -430,15 +1010,12 @@ CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m -CONFIG_CRYPTO_HMAC=m CONFIG_CRYPTO_XCBC=m -CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD128=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_RMD256=m CONFIG_CRYPTO_RMD320=m -CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m @@ -458,4 +1035,3 @@ CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_DEFLATE=m CONFIG_CRYPTO_ZLIB=m CONFIG_CRYPTO_LZO=m -CONFIG_CRC_T10DIF=y diff --git a/arch/mips/configs/lemote2f_minimal_defconfig b/arch/mips/configs/lemote2f_minimal_defconfig new file mode 100644 index 0000000..82ad0bd4 --- /dev/null +++ b/arch/mips/configs/lemote2f_minimal_defconfig @@ -0,0 +1,122 @@ +CONFIG_MACH_LOONGSON=y +CONFIG_LEMOTE_MACH2F=y +CONFIG_64BIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZMA=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_PCI=y +CONFIG_MIPS32_COMPAT=y +CONFIG_MIPS32_O32=y +CONFIG_MIPS32_N32=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6 is not set +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +# CONFIG_MISC_DEVICES is not set +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +# CONFIG_SATA_PMP is not set +CONFIG_PATA_AMD=y +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_NET_PCI=y +CONFIG_8139TOO=y +CONFIG_R8169=y +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_MFD_SUPPORT is not set +CONFIG_FB=y +CONFIG_FB_SIS=y +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_HIDRAW=y +CONFIG_USB_HIDDEV=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_ZEROPLUS=y +CONFIG_ZEROPLUS_FF=y +CONFIG_USB=y +# CONFIG_USB_DEVICE_CLASS is not set +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_LIBUSUAL=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_CMOS=y +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_FB_SM7XX=y +# CONFIG_MIPS_PLATFORM_DEVICES is not set +CONFIG_EXT4_FS=y +CONFIG_TMPFS=y +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_ASCII=y +CONFIG_NLS_UTF8=y +CONFIG_FRAME_WARN=1024 +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set diff --git a/arch/mips/configs/lemote2f_small_defconfig b/arch/mips/configs/lemote2f_small_defconfig new file mode 100644 index 0000000..a546502 --- /dev/null +++ b/arch/mips/configs/lemote2f_small_defconfig @@ -0,0 +1,152 @@ +CONFIG_MACH_LOONGSON=y +CONFIG_LEMOTE_MACH2F=y +CONFIG_64BIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZMA=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_PCI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_MIPS32_COMPAT=y +CONFIG_MIPS32_O32=y +CONFIG_MIPS32_N32=y +CONFIG_PM=y +CONFIG_HIBERNATION=y +CONFIG_PM_STD_PARTITION="/dev/sda3" +CONFIG_PM_RUNTIME=y +CONFIG_R4K_TIMER_FOR_CPUFREQ=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_LOONGSON2_CPUFREQ=m +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6 is not set +CONFIG_CFG80211=y +# CONFIG_CFG80211_DEFAULT_PS is not set +# CONFIG_CFG80211_WEXT is not set +CONFIG_LIB80211=y +CONFIG_MAC80211=y +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +# CONFIG_SATA_PMP is not set +CONFIG_PATA_AMD=y +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_NET_PCI=y +CONFIG_8139TOO=y +CONFIG_R8169=y +# CONFIG_NETDEV_10000 is not set +CONFIG_RTL8187B=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_HW_RANDOM is not set +CONFIG_HWMON=m +# CONFIG_MFD_SUPPORT is not set +CONFIG_FB=y +CONFIG_FB_SIS=y +CONFIG_FB_SIS_300=y +CONFIG_FB_SIS_315=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_CS5535AUDIO=m +CONFIG_HIDRAW=y +CONFIG_USB_HIDDEV=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_ZEROPLUS=y +CONFIG_ZEROPLUS_FF=y +CONFIG_USB=y +# CONFIG_USB_DEVICE_CLASS is not set +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_LIBUSUAL=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_CMOS=y +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_FB_SM7XX=y +CONFIG_EXT4_FS=y +CONFIG_TMPFS=y +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_ASCII=y +CONFIG_NLS_UTF8=y +CONFIG_FRAME_WARN=1024 +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set diff --git a/arch/mips/configs/yeeloong_tiny_defconfig b/arch/mips/configs/yeeloong_tiny_defconfig new file mode 100644 index 0000000..93e3e7b --- /dev/null +++ b/arch/mips/configs/yeeloong_tiny_defconfig @@ -0,0 +1,74 @@ +CONFIG_MACH_LOONGSON=y +CONFIG_LEMOTE_MACH2F=y +CONFIG_HZ_1000=y +CONFIG_PREEMPT=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_SWAP is not set +CONFIG_TINY_RCU=y +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +# CONFIG_PRINTK is not set +# CONFIG_BUG is not set +# CONFIG_ELF_CORE is not set +# CONFIG_PCSPKR_PLATFORM is not set +# CONFIG_BASE_FULL is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_SHMEM is not set +# CONFIG_AIO is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_PCI_QUIRKS is not set +CONFIG_SLOB=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_PCI=y +# CONFIG_FW_LOADER is not set +# CONFIG_BLK_DEV is not set +CONFIG_BLK_DEV_SD=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +# CONFIG_ATA_VERBOSE_ERROR is not set +# CONFIG_SATA_PMP is not set +CONFIG_PATA_AMD=y +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_CONSOLE_TRANSLATIONS is not set +# CONFIG_DEVKMEM is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_MFD_SUPPORT is not set +# CONFIG_VGA_ARB is not set +CONFIG_FB=y +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_CMOS=y +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_FB_SM7XX=y +# CONFIG_MIPS_PLATFORM_DEVICES is not set +CONFIG_EXT2_FS=y +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_PROC_FS is not set +# CONFIG_SYSFS is not set +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_BKL is not set +CONFIG_CRC16=y diff --git a/arch/mips/include/asm/clock.h b/arch/mips/include/asm/clock.h deleted file mode 100644 index 83894aa..0000000 --- a/arch/mips/include/asm/clock.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __ASM_MIPS_CLOCK_H -#define __ASM_MIPS_CLOCK_H - -#include -#include -#include -#include - -extern void (*cpu_wait) (void); - -struct clk; - -struct clk_ops { - void (*init) (struct clk *clk); - void (*enable) (struct clk *clk); - void (*disable) (struct clk *clk); - void (*recalc) (struct clk *clk); - int (*set_rate) (struct clk *clk, unsigned long rate, int algo_id); - long (*round_rate) (struct clk *clk, unsigned long rate); -}; - -struct clk { - struct list_head node; - const char *name; - int id; - struct module *owner; - - struct clk *parent; - struct clk_ops *ops; - - struct kref kref; - - unsigned long rate; - unsigned long flags; -}; - -#define CLK_ALWAYS_ENABLED (1 << 0) -#define CLK_RATE_PROPAGATES (1 << 1) - -/* Should be defined by processor-specific code */ -void arch_init_clk_ops(struct clk_ops **, int type); - -int clk_init(void); - -int __clk_enable(struct clk *); -void __clk_disable(struct clk *); - -void clk_recalc_rate(struct clk *); - -int clk_register(struct clk *); -void clk_unregister(struct clk *); - -/* the exported API, in addition to clk_set_rate */ -/** - * clk_set_rate_ex - set the clock rate for a clock source, with additional parameter - * @clk: clock source - * @rate: desired clock rate in Hz - * @algo_id: algorithm id to be passed down to ops->set_rate - * - * Returns success (0) or negative errno. - */ -int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id); - -#endif /* __ASM_MIPS_CLOCK_H */ diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index ca400f7..c4e1834 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -13,8 +13,12 @@ #include #include +#ifndef current_cpu_prid +#define current_cpu_prid() current_cpu_data.processor_id +#endif + #ifndef current_cpu_type -#define current_cpu_type() current_cpu_data.cputype +#define current_cpu_type() current_cpu_data.cputype #endif /* diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h index 5f95a4b..9b29ae7 100644 --- a/arch/mips/include/asm/cpu.h +++ b/arch/mips/include/asm/cpu.h @@ -37,6 +37,8 @@ #define PRID_COMP_CAVIUM 0x0d0000 #define PRID_COMP_INGENIC 0xd00000 +#define PRID_COMP_MASK 0xff0000 + /* * Assigned values for the product ID register. In order to detect a * certain CPU type exactly eventually additional registers may need to @@ -74,6 +76,7 @@ #define PRID_IMP_LOONGSON2 0x6300 #define PRID_IMP_UNKNOWN 0xff00 +#define PRID_IMP_MASK 0xff00 /* * These are the PRID's for when 23:16 == PRID_COMP_MIPS @@ -192,6 +195,12 @@ #define PRID_REV_LOONGSON2E 0x0002 #define PRID_REV_LOONGSON2F 0x0003 +#define cpu_prid_comp() (current_cpu_prid() & PRID_COMP_MASK) +#define cpu_prid_imp() (current_cpu_prid() & PRID_IMP_MASK) +#define cpu_prid_rev() (current_cpu_prid() & PRID_REV_MASK) + +#define cpu_prid_encode(comp, imp, rev) ((comp) | (imp) | (rev)) + /* * Older processors used to encode processor version and revision in two * 4-bit bitfields, the 4K seems to simply count up and even newer MTI cores @@ -321,5 +330,4 @@ enum cpu_type_enum { #define MIPS_ASE_DSP 0x00000010 /* Signal Processing ASE */ #define MIPS_ASE_MIPSMT 0x00000020 /* CPU supports MIPS MT */ - #endif /* _ASM_CPU_H */ diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h index 7aa37dd..655f849 100644 --- a/arch/mips/include/asm/dma-mapping.h +++ b/arch/mips/include/asm/dma-mapping.h @@ -5,9 +5,7 @@ #include #include -#ifndef CONFIG_SGI_IP27 /* Kludge to fix 2.6.39 build for IP27 */ #include -#endif extern struct dma_map_ops *mips_dma_map_ops; diff --git a/arch/mips/include/asm/ftrace.h b/arch/mips/include/asm/ftrace.h index ce35c9a..4c02d4c 100644 --- a/arch/mips/include/asm/ftrace.h +++ b/arch/mips/include/asm/ftrace.h @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive for * more details. * - * Copyright (C) 2009 DSLab, Lanzhou University, China + * Copyright (C) 2009, 2010 DSLab, Lanzhou University, China * Author: Wu Zhangjin */ @@ -83,8 +83,8 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) struct dyn_arch_ftrace { }; - #endif /* CONFIG_DYNAMIC_FTRACE */ + #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */ #endif /* _ASM_MIPS_FTRACE_H */ diff --git a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h index 675bd86..5938f70 100644 --- a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h @@ -16,6 +16,20 @@ #ifndef __ASM_MACH_LOONGSON_CPU_FEATURE_OVERRIDES_H #define __ASM_MACH_LOONGSON_CPU_FEATURE_OVERRIDES_H +#ifdef CONFIG_CPU_LOONGSON2 +#define cpu_prid_loongson2() \ + cpu_prid_encode(PRID_COMP_LEGACY, PRID_IMP_LOONGSON2, 0) + +#ifdef CONFIG_CPU_LOONGSON2F +#define current_cpu_prid() (cpu_prid_loongson2() | PRID_REV_LOONGSON2F) +#else /* CONFIG_CPU_LOONGSON2E */ +#define current_cpu_prid() (cpu_prid_loongson2() | PRID_REV_LOONGSON2E) +#endif + +#endif /* CONFIG_CPU_LOONGSON2 */ + +#define current_cpu_type() CPU_LOONGSON2 + #define cpu_dcache_line_size() 32 #define cpu_icache_line_size() 32 #define cpu_scache_line_size() 32 diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h index 2a8e2bb..38bceee 100644 --- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h +++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h @@ -255,21 +255,12 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo); * IDE STANDARD */ #define IDE_CAP 0x00 -#define IDE_CONFIG 0x01 -#define IDE_SMI 0x02 -#define IDE_ERROR 0x03 -#define IDE_PM 0x04 -#define IDE_DIAG 0x05 - -/* - * IDE SPEC. - */ #define IDE_IO_BAR 0x08 #define IDE_CFG 0x10 #define IDE_DTC 0x12 #define IDE_CAST 0x13 #define IDE_ETC 0x14 -#define IDE_INTERNAL_PM 0x15 +#define IDE_PM 0x15 /* * ACC STANDARD @@ -301,5 +292,40 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo); /* GPIO : I/O SPACE; REG : 32BITS */ #define GPIOL_OUT_VAL 0x00 #define GPIOL_OUT_EN 0x04 +#define GPIOL_OUT_AUX1_SEL 0x10 +/* SMB : I/O SPACE, REG : 8BITS WIDTH */ +#define SMB_SDA 0x00 +#define SMB_STS 0x01 +#define SMB_STS_SLVSTP (1 << 7) +#define SMB_STS_SDAST (1 << 6) +#define SMB_STS_BER (1 << 5) +#define SMB_STS_NEGACK (1 << 4) +#define SMB_STS_STASTR (1 << 3) +#define SMB_STS_NMATCH (1 << 2) +#define SMB_STS_MASTER (1 << 1) +#define SMB_STS_XMIT (1 << 0) +#define SMB_CTRL_STS 0x02 +#define SMB_CSTS_TGSTL (1 << 5) +#define SMB_CSTS_TSDA (1 << 4) +#define SMB_CSTS_GCMTCH (1 << 3) +#define SMB_CSTS_MATCH (1 << 2) +#define SMB_CSTS_BB (1 << 1) +#define SMB_CSTS_BUSY (1 << 0) +#define SMB_CTRL1 0x03 +#define SMB_CTRL1_STASTRE (1 << 7) +#define SMB_CTRL1_NMINTE (1 << 6) +#define SMB_CTRL1_GCMEN (1 << 5) +#define SMB_CTRL1_ACK (1 << 4) +#define SMB_CTRL1_RSVD (1 << 3) +#define SMB_CTRL1_INTEN (1 << 2) +#define SMB_CTRL1_STOP (1 << 1) +#define SMB_CTRL1_START (1 << 0) +#define SMB_ADDR 0x04 +#define SMB_ADDR_SAEN (1 << 7) +#define SMB_CONTROLLER_ADDR (0xef << 0) +#define SMB_CTRL2 0x05 +#define SMB_FREQ (0x20 << 1) +#define SMB_ENABLE (0x01 << 0) +#define SMB_CTRL3 0x06 #endif /* _CS5536_H */ diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h index 4b493d6..d058e46 100644 --- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h +++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h @@ -10,26 +10,45 @@ #ifdef CONFIG_CS5536_MFGPT extern void setup_mfgpt0_timer(void); -extern void disable_mfgpt0_counter(void); -extern void enable_mfgpt0_counter(void); +extern void disable_mfgpt_counter(void); +extern void enable_mfgpt_counter(void); #else static inline void __maybe_unused setup_mfgpt0_timer(void) { } -static inline void __maybe_unused disable_mfgpt0_counter(void) +static inline void __maybe_unused disable_mfgpt_counter(void) { } -static inline void __maybe_unused enable_mfgpt0_counter(void) +static inline void __maybe_unused enable_mfgpt_counter(void) { } #endif -#define MFGPT_TICK_RATE 14318000 -#define COMPARE ((MFGPT_TICK_RATE + HZ/2) / HZ) +#define MFGPT_CLK_RATE(c) ((14318000UL-32768)*c + 32768) +#define MFGPT_TICK_RATE(c, scale) (MFGPT_CLK_RATE(c) / (1 << scale)) +#define MFGPT_COMPARE(c, scale) ((MFGPT_TICK_RATE(c, scale)+HZ/2)/HZ) -#define MFGPT_BASE mfgpt_base -#define MFGPT0_CMP2 (MFGPT_BASE + 2) -#define MFGPT0_CNT (MFGPT_BASE + 4) -#define MFGPT0_SETUP (MFGPT_BASE + 6) +#define MFGPT_SETUP_ENABLE (1 << 15) +#define MFGPT_SETUP_ACK (3 << 13) +#define MFGPT_SETUP_SETUP (1 << 12) +#define MFGPT_SETUP_CMP2EVT (3 << 8) +#define MFGPT_SETUP_CMP1EVT (3 << 6) +#define MFGPT_SETUP_CLOCK(c) (c << 4) +#define MFGPT_SETUP_SCALE(scale) scale + +#define MFGPT0_CMP1 mfgpt_base +#define MFGPT0_CMP2 (mfgpt_base + 0x02) +#define MFGPT0_CNT (mfgpt_base + 0x04) +#define MFGPT0_SETUP (mfgpt_base + 0x06) + +#define MFGPT1_CMP1 (mfgpt_base + 0x08) +#define MFGPT1_CMP2 (mfgpt_base + 0x0A) +#define MFGPT1_CNT (mfgpt_base + 0x0C) +#define MFGPT1_SETUP (mfgpt_base + 0x0E) + +#define MFGPT2_CMP1 (mfgpt_base + 0x10) +#define MFGPT2_CMP2 (mfgpt_base + 0x12) +#define MFGPT2_CNT (mfgpt_base + 0x14) +#define MFGPT2_SETUP (mfgpt_base + 0x16) #endif /*!_CS5536_MFGPT_H */ diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h index 0dca9c8..cfefc37 100644 --- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h +++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h @@ -13,6 +13,7 @@ #include #include +#include extern void cs5536_pci_conf_write4(int function, int reg, u32 value); extern u32 cs5536_pci_conf_read4(int function, int reg); @@ -44,16 +45,6 @@ extern u32 cs5536_pci_conf_read4(int function, int reg); #define CFG_PCI_VENDOR_ID(mod_dev_id, sys_vendor_id) \ (((mod_dev_id) << 16) | (sys_vendor_id)) -/* VENDOR ID */ -#define CS5536_VENDOR_ID 0x1022 - -/* DEVICE ID */ -#define CS5536_ISA_DEVICE_ID 0x2090 -#define CS5536_IDE_DEVICE_ID 0x209a -#define CS5536_ACC_DEVICE_ID 0x2093 -#define CS5536_OHCI_DEVICE_ID 0x2094 -#define CS5536_EHCI_DEVICE_ID 0x2095 - /* CLASS CODE : CLASS SUB-CLASS INTERFACE */ #define CS5536_ISA_CLASS_CODE 0x060100 #define CS5536_IDE_CLASS_CODE 0x010180 @@ -86,16 +77,6 @@ extern u32 cs5536_pci_conf_read4(int function, int reg); /* CARDBUS CIS POINTER */ #define PCI_CARDBUS_CIS_POINTER 0x00000000 -/* SUBSYSTEM VENDOR ID */ -#define CS5536_SUB_VENDOR_ID CS5536_VENDOR_ID - -/* SUBSYSTEM ID */ -#define CS5536_ISA_SUB_ID CS5536_ISA_DEVICE_ID -#define CS5536_IDE_SUB_ID CS5536_IDE_DEVICE_ID -#define CS5536_ACC_SUB_ID CS5536_ACC_DEVICE_ID -#define CS5536_OHCI_SUB_ID CS5536_OHCI_DEVICE_ID -#define CS5536_EHCI_SUB_ID CS5536_EHCI_DEVICE_ID - /* EXPANSION ROM BAR */ #define PCI_EXPANSION_ROM_BAR 0x00000000 diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h index 21c4ece..40f6a81 100644 --- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h +++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h @@ -17,15 +17,43 @@ typedef u32 (*cs5536_pci_vsm_read)(int reg); extern void pci_##name##_write_reg(int reg, u32 value); \ extern u32 pci_##name##_read_reg(int reg); +#define DEFINE_CS5536_MODULE(name) \ +static void pci_##name##_write_reg(int reg, u32 value) {} \ +static u32 pci_##name##_read_reg(int reg) { return 0; } + +/* isa module */ +#ifdef CONFIG_CS5536_ISA +DECLARE_CS5536_MODULE(isa) +#else +DEFINE_CS5536_MODULE(isa) +#endif + /* ide module */ +#ifdef CONFIG_CS5536_IDE DECLARE_CS5536_MODULE(ide) +#else +DEFINE_CS5536_MODULE(ide) +#endif + /* acc module */ +#ifdef CONFIG_CS5536_AUDIO DECLARE_CS5536_MODULE(acc) +#else +DEFINE_CS5536_MODULE(acc) +#endif + /* ohci module */ +#ifdef CONFIG_CS5536_OHCI DECLARE_CS5536_MODULE(ohci) -/* isa module */ -DECLARE_CS5536_MODULE(isa) +#else +DEFINE_CS5536_MODULE(ohci) +#endif + /* ehci module */ +#ifdef CONFIG_CS5536_EHCI DECLARE_CS5536_MODULE(ehci) +#else +DEFINE_CS5536_MODULE(ehci) +#endif #endif /* _CS5536_VSM_H */ diff --git a/arch/mips/include/asm/mach-loongson/ec_kb3310b.h b/arch/mips/include/asm/mach-loongson/ec_kb3310b.h new file mode 100644 index 0000000..2e86905 --- /dev/null +++ b/arch/mips/include/asm/mach-loongson/ec_kb3310b.h @@ -0,0 +1,170 @@ +/* + * KB3310B Embedded Controller + * + * Copyright (C) 2008 Lemote Inc. + * Author: liujl , 2008-03-14 + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin + * + * 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 _EC_KB3310B_H +#define _EC_KB3310B_H + +extern unsigned char ec_read(unsigned short addr); +extern void ec_write(unsigned short addr, unsigned char val); +extern int ec_query_seq(unsigned char cmd); +extern int ec_query_event_num(void); +extern int ec_get_event_num(void); + +typedef int (*sci_handler) (int status); +extern sci_handler yeeloong_report_lid_status; + +#define ON 1 +#define OFF 0 + +#define SCI_IRQ_NUM 0x0A + +/* + * The following registers are determined by the EC index configuration. + * 1, fill the PORT_HIGH as EC register high part. + * 2, fill the PORT_LOW as EC register low part. + * 3, fill the PORT_DATA as EC register write data or get the data from it. + */ +#define EC_IO_PORT_HIGH 0x0381 +#define EC_IO_PORT_LOW 0x0382 +#define EC_IO_PORT_DATA 0x0383 + +/* + * EC delay time is 500us for register and status access + */ +#define EC_REG_DELAY 500 /* unit : us */ +#define EC_CMD_TIMEOUT 0x1000 + +/* + * EC access port for SCI communication + */ +#define EC_CMD_PORT 0x66 +#define EC_STS_PORT 0x66 +#define EC_DAT_PORT 0x62 +#define CMD_INIT_IDLE_MODE 0xdd +#define CMD_EXIT_IDLE_MODE 0xdf +#define CMD_INIT_RESET_MODE 0xd8 +#define CMD_REBOOT_SYSTEM 0x8c +#define CMD_GET_EVENT_NUM 0x84 +#define CMD_PROGRAM_PIECE 0xda + +/* Temperature & Fan registers */ +#define REG_TEMPERATURE_VALUE 0xF458 +#define REG_FAN_AUTO_MAN_SWITCH 0xF459 +#define BIT_FAN_AUTO 0 +#define BIT_FAN_MANUAL 1 +#define REG_FAN_CONTROL 0xF4D2 +#define REG_FAN_STATUS 0xF4DA +#define REG_FAN_SPEED_HIGH 0xFE22 +#define REG_FAN_SPEED_LOW 0xFE23 +#define REG_FAN_SPEED_LEVEL 0xF4CC +/* Fan speed divider */ +#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/ + +/* Battery registers */ +#define REG_BAT_DESIGN_CAP_HIGH 0xF77D +#define REG_BAT_DESIGN_CAP_LOW 0xF77E +#define REG_BAT_FULLCHG_CAP_HIGH 0xF780 +#define REG_BAT_FULLCHG_CAP_LOW 0xF781 +#define REG_BAT_DESIGN_VOL_HIGH 0xF782 +#define REG_BAT_DESIGN_VOL_LOW 0xF783 +#define REG_BAT_CURRENT_HIGH 0xF784 +#define REG_BAT_CURRENT_LOW 0xF785 +#define REG_BAT_VOLTAGE_HIGH 0xF786 +#define REG_BAT_VOLTAGE_LOW 0xF787 +#define REG_BAT_TEMPERATURE_HIGH 0xF788 +#define REG_BAT_TEMPERATURE_LOW 0xF789 +#define REG_BAT_RELATIVE_CAP_HIGH 0xF492 +#define REG_BAT_RELATIVE_CAP_LOW 0xF493 +#define REG_BAT_VENDOR 0xF4C4 +#define FLAG_BAT_VENDOR_SANYO 0x01 +#define FLAG_BAT_VENDOR_SIMPLO 0x02 +#define REG_BAT_CELL_COUNT 0xF4C6 +#define FLAG_BAT_CELL_3S1P 0x03 +#define FLAG_BAT_CELL_3S2P 0x06 +#define REG_BAT_CHARGE 0xF4A2 +#define FLAG_BAT_CHARGE_DISCHARGE 0x01 +#define FLAG_BAT_CHARGE_CHARGE 0x02 +#define FLAG_BAT_CHARGE_ACPOWER 0x00 +#define REG_BAT_STATUS 0xF4B0 +#define BIT_BAT_STATUS_LOW (1 << 5) +#define BIT_BAT_STATUS_DESTROY (1 << 2) +#define BIT_BAT_STATUS_FULL (1 << 1) +#define BIT_BAT_STATUS_IN (1 << 0) +#define REG_BAT_CHARGE_STATUS 0xF4B1 +#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2) +#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1) +#define REG_BAT_STATE 0xF482 +#define REG_BAT_POWER 0xF440 +#define BIT_BAT_POWER_S3 (1 << 2) +#define BIT_BAT_POWER_ON (1 << 1) +#define BIT_BAT_POWER_ACIN (1 << 0) + +/* Audio: rd/wr */ +#define REG_AUDIO_VOLUME 0xF46C +#define REG_AUDIO_MUTE 0xF4E7 +#define REG_AUDIO_BEEP 0xF4D0 +/* USB port power or not: rd/wr */ +#define REG_USB0_FLAG 0xF461 +#define REG_USB1_FLAG 0xF462 +#define REG_USB2_FLAG 0xF463 +/* LID */ +#define REG_LID_DETECT 0xF4BD +/* CRT */ +#define REG_CRT_DETECT 0xF4AD +/* LCD backlight brightness adjust: 9 levels */ +#define REG_DISPLAY_BRIGHTNESS 0xF4F5 +/* LCD backlight control: off/restore */ +#define REG_BACKLIGHT_CTRL 0xF7BD +/* Reset the machine auto-clear: rd/wr */ +#define REG_RESET 0xF4EC +/* Light the led: rd/wr */ +#define REG_LED 0xF4C8 +#define BIT_LED_RED_POWER (1 << 0) +#define BIT_LED_ORANGE_POWER (1 << 1) +#define BIT_LED_GREEN_CHARGE (1 << 2) +#define BIT_LED_RED_CHARGE (1 << 3) +#define BIT_LED_NUMLOCK (1 << 4) +/* Test led mode, all led on/off */ +#define REG_LED_TEST 0xF4C2 +#define BIT_LED_TEST_IN 1 +#define BIT_LED_TEST_OUT 0 +/* Camera on/off */ +#define REG_CAMERA_STATUS 0xF46A +#define REG_CAMERA_CONTROL 0xF7B7 +/* Wlan Status */ +#define REG_WLAN 0xF4FA +#define REG_DISPLAY_LCD 0xF79F + +/* SCI Event Number from EC */ +enum { + EVENT_LID = 0x23, /* Turn on/off LID */ + EVENT_SWITCHVIDEOMODE, /* Fn+F3 for display switch */ + EVENT_SLEEP, /* Fn+F1 for entering sleep mode */ + EVENT_OVERTEMP, /* Over-temperature happened */ + EVENT_CRT_DETECT, /* CRT is connected */ + EVENT_CAMERA, /* Camera on/off */ + EVENT_USB_OC2, /* USB2 Over Current occurred */ + EVENT_USB_OC0, /* USB0 Over Current occurred */ + EVENT_DISPLAYTOGGLE, /* Fn+F2, Turn on/off backlight */ + EVENT_AUDIO_MUTE, /* Fn+F4, Mute on/off */ + EVENT_DISPLAY_BRIGHTNESS,/* Fn+^/V, LCD backlight brightness adjust */ + EVENT_AC_BAT, /* AC & Battery relative issue */ + EVENT_AUDIO_VOLUME, /* Fn+<|>, Volume adjust */ + EVENT_WLAN, /* Wlan on/off */ +}; + +#define EVENT_START EVENT_LID +#define EVENT_END EVENT_WLAN + +#endif /* !_EC_KB3310B_H */ diff --git a/arch/mips/include/asm/mach-loongson/gpio.h b/arch/mips/include/asm/mach-loongson/gpio.h index e30e73d..0fd06bf 100644 --- a/arch/mips/include/asm/mach-loongson/gpio.h +++ b/arch/mips/include/asm/mach-loongson/gpio.h @@ -13,12 +13,16 @@ #ifndef __STLS2F_GPIO_H #define __STLS2F_GPIO_H +#ifdef CONFIG_GENERIC_GPIO +#define ARCH_NR_GPIOS 4 #include extern void gpio_set_value(unsigned gpio, int value); extern int gpio_get_value(unsigned gpio); extern int gpio_cansleep(unsigned gpio); +#endif + /* The chip can do interrupt * but it has not been tested and doc not clear */ diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h index 1e29b9d..ed089c7 100644 --- a/arch/mips/include/asm/mach-loongson/loongson.h +++ b/arch/mips/include/asm/mach-loongson/loongson.h @@ -31,17 +31,13 @@ extern void __init prom_init_memory(void); extern void __init prom_init_cmdline(void); extern void __init prom_init_machtype(void); extern void __init prom_init_env(void); -#ifdef CONFIG_LOONGSON_UART_BASE -extern unsigned long _loongson_uart_base, loongson_uart_base; -extern void prom_init_loongson_uart_base(void); -#endif +extern void __init prom_init_uart_base(void); -static inline void prom_init_uart_base(void) -{ -#ifdef CONFIG_LOONGSON_UART_BASE - prom_init_loongson_uart_base(); -#endif -} +/* + * Copy kernel command line from arcs_cmdline + */ +#include +extern char loongson_cmdline[COMMAND_LINE_SIZE]; /* irq operation functions */ extern void bonito_irqdispatch(void); @@ -243,12 +239,14 @@ static inline void do_perfcnt_IRQ(void) ((((ADDR)>>26) & LOONGSON_PCIMAP_PCIMAP_LO0) << ((WIN)*6)) #ifdef CONFIG_CPU_SUPPORTS_CPUFREQ -#include -extern void loongson2_cpu_wait(void); -extern struct cpufreq_frequency_table loongson2_clockmod_table[]; - /* Chip Config */ #define LOONGSON_CHIPCFG0 LOONGSON_REG(LOONGSON_REGBASE + 0x80) +#define LOONGSON_GET_CPUFREQ() (LOONGSON_CHIPCFG0 & 7) + +#define LOONGSON_SET_CPUFREQ(level) do { \ + LOONGSON_CHIPCFG0 = (LOONGSON_CHIPCFG0 & (~7)) | (level); \ +} while (0) + #endif /* diff --git a/arch/mips/include/asm/mach-loongson/machine.h b/arch/mips/include/asm/mach-loongson/machine.h index 4321338..8575995 100644 --- a/arch/mips/include/asm/mach-loongson/machine.h +++ b/arch/mips/include/asm/mach-loongson/machine.h @@ -12,16 +12,16 @@ #define __ASM_MACH_LOONGSON_MACHINE_H #ifdef CONFIG_LEMOTE_FULOONG2E - -#define LOONGSON_MACHTYPE MACH_LEMOTE_FL2E - + #define LOONGSON_MACHTYPE MACH_LEMOTE_FL2E #endif /* use fuloong2f as the default machine of LEMOTE_MACH2F */ #ifdef CONFIG_LEMOTE_MACH2F + #define LOONGSON_MACHTYPE MACH_LEMOTE_FL2F +#endif -#define LOONGSON_MACHTYPE MACH_LEMOTE_FL2F - +#ifdef CONFIG_DEXXON_GDIUM + #define LOONGSON_MACHTYPE MACH_DEXXON_GDIUM2F10 #endif #endif /* __ASM_MACH_LOONGSON_MACHINE_H */ diff --git a/arch/mips/include/asm/timex.h b/arch/mips/include/asm/timex.h index 6529704..bf6a701 100644 --- a/arch/mips/include/asm/timex.h +++ b/arch/mips/include/asm/timex.h @@ -10,6 +10,10 @@ #ifdef __KERNEL__ +#ifdef CONFIG_CSRC_R4K +#define ARCH_HAS_PREPARED_LPJ +#endif + #include /* diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c index 98c5a97..0f33eba 100644 --- a/arch/mips/kernel/cevt-r4k.c +++ b/arch/mips/kernel/cevt-r4k.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,51 @@ #ifndef CONFIG_MIPS_MT_SMTC +#ifdef CONFIG_R4K_TIMER_FOR_CPUFREQ + +extern cycle_t read_virtual_count(void); +extern unsigned int scale_shift; +#define hpt_scale_down(cycle) ((cycle) >> scale_shift) +#define hpt_scale_down_shift(cycle, shift) ((cycle) >> (shift)) + +static u64 last_hpt_target; + +static inline void update_virtual_target(u32 delta) +{ + last_hpt_target = read_virtual_count() + delta; +} + +/* + * This should be called with irq disabled and spin lock + * + * We must update the virtual clocksource and the clockevent when prepare or + * post change the cpu frequency. + */ + +void notrace update_virtual_count(unsigned int target_scale_shift) +{ + u64 now64; + /* + * If we want to change the cpufreq before the target timer event is + * met, we must update the delta to the new one. + */ + now64 = read_virtual_count(); + if (now64 < last_hpt_target) { + unsigned int cnt = read_c0_count(); + cnt += hpt_scale_down_shift((last_hpt_target - now64), + target_scale_shift); + write_c0_compare(cnt); + } +} +EXPORT_SYMBOL(update_virtual_count); + +#else + +#define hpt_scale_down(cycle) (cycle) +#define update_virtual_target(delta) + +#endif /* CONFIG_R4K_TIMER_FOR_CPUFREQ */ + static int mips_next_event(unsigned long delta, struct clock_event_device *evt) { @@ -30,9 +76,13 @@ static int mips_next_event(unsigned long delta, int res; cnt = read_c0_count(); - cnt += delta; + cnt += hpt_scale_down(delta); write_c0_compare(cnt); res = ((int)(read_c0_count() - cnt) >= 0) ? -ETIME : 0; + + /* Update the virtual counter */ + update_virtual_target(delta); + return res; } diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index ebc0cd2..8a0a407 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -152,14 +152,12 @@ __setup("nodsp", dsp_disable); void __init check_wait(void) { - struct cpuinfo_mips *c = ¤t_cpu_data; - if (nowait) { printk("Wait instruction disabled.\n"); return; } - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_R3081: case CPU_R3081E: cpu_wait = r3081_wait; @@ -207,7 +205,7 @@ void __init check_wait(void) case CPU_74K: cpu_wait = r4k_wait; - if ((c->processor_id & 0xff) >= PRID_REV_ENCODE_332(2, 1, 0)) + if (cpu_prid_rev() >= PRID_REV_ENCODE_332(2, 1, 0)) cpu_wait = r4k_wait_irqoff; break; @@ -223,7 +221,7 @@ void __init check_wait(void) * WAIT on Rev2.0 and Rev3.0 has E16. * Rev3.1 WAIT is nop, why bother */ - if ((c->processor_id & 0xff) <= 0x64) + if (cpu_prid_rev() <= 0x64) break; /* @@ -236,7 +234,7 @@ void __init check_wait(void) */ break; case CPU_RM9000: - if ((c->processor_id & 0x00ff) >= 0x40) + if (cpu_prid_rev() >= 0x40) cpu_wait = r4k_wait; break; default: @@ -246,16 +244,14 @@ void __init check_wait(void) static inline void check_errata(void) { - struct cpuinfo_mips *c = ¤t_cpu_data; - - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_34K: /* * Erratum "RPS May Cause Incorrect Instruction Execution" * This code only handles VPE0, any SMP/SMTC/RTOS code * making use of VPE1 will be responsable for that VPE. */ - if ((c->processor_id & PRID_REV_MASK) <= PRID_REV_34K_V1_0_2) + if (cpu_prid_rev() <= PRID_REV_34K_V1_0_2) write_c0_config7(read_c0_config7() | MIPS_CONF7_RPS); break; default: @@ -332,7 +328,7 @@ static inline void cpu_probe_vmbits(struct cpuinfo_mips *c) static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) { - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_R2000: c->cputype = CPU_R2000; __cpu_name[cpu] = "R2000"; @@ -344,7 +340,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) c->tlbsize = 64; break; case PRID_IMP_R3000: - if ((c->processor_id & 0xff) == PRID_REV_R3000A) { + if (cpu_prid_rev() == PRID_REV_R3000A) { if (cpu_has_confreg()) { c->cputype = CPU_R3081E; __cpu_name[cpu] = "R3081"; @@ -366,7 +362,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) break; case PRID_IMP_R4000: if (read_c0_config() & CONF_SC) { - if ((c->processor_id & 0xff) >= PRID_REV_R4400) { + if (cpu_prid_rev() >= PRID_REV_R4400) { c->cputype = CPU_R4400PC; __cpu_name[cpu] = "R4400PC"; } else { @@ -374,7 +370,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) __cpu_name[cpu] = "R4000PC"; } } else { - if ((c->processor_id & 0xff) >= PRID_REV_R4400) { + if (cpu_prid_rev() >= PRID_REV_R4400) { c->cputype = CPU_R4400SC; __cpu_name[cpu] = "R4400SC"; } else { @@ -390,7 +386,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) c->tlbsize = 48; break; case PRID_IMP_VR41XX: - switch (c->processor_id & 0xf0) { + switch (current_cpu_prid() & 0xf0) { case PRID_REV_VR4111: c->cputype = CPU_VR4111; __cpu_name[cpu] = "NEC VR4111"; @@ -400,7 +396,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) __cpu_name[cpu] = "NEC VR4121"; break; case PRID_REV_VR4122: - if ((c->processor_id & 0xf) < 0x3) { + if ((current_cpu_prid() & 0xf) < 0x3) { c->cputype = CPU_VR4122; __cpu_name[cpu] = "NEC VR4122"; } else { @@ -409,7 +405,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) } break; case PRID_REV_VR4130: - if ((c->processor_id & 0xf) < 0x4) { + if ((current_cpu_prid() & 0xf) < 0x4) { c->cputype = CPU_VR4131; __cpu_name[cpu] = "NEC VR4131"; } else { @@ -462,12 +458,12 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) c->isa_level = MIPS_CPU_ISA_I; c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE; - if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) { + if ((current_cpu_prid() & 0xf0) == (PRID_REV_TX3927 & 0xf0)) { c->cputype = CPU_TX3927; __cpu_name[cpu] = "TX3927"; c->tlbsize = 64; } else { - switch (c->processor_id & 0xff) { + switch (cpu_prid_rev()) { case PRID_REV_TX3912: c->cputype = CPU_TX3912; __cpu_name[cpu] = "TX3912"; @@ -494,7 +490,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) __cpu_name[cpu] = "R49XX"; c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_LLSC; - if (!(c->processor_id & 0x08)) + if (!(current_cpu_prid() & 0x08)) c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; c->tlbsize = 48; break; @@ -789,7 +785,7 @@ static void __cpuinit decode_configs(struct cpuinfo_mips *c) static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_4KC: c->cputype = CPU_4KC; __cpu_name[cpu] = "MIPS 4Kc"; @@ -841,11 +837,11 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu) static inline void cpu_probe_alchemy(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_AU1_REV1: case PRID_IMP_AU1_REV2: c->cputype = CPU_ALCHEMY; - switch ((c->processor_id >> 24) & 0xff) { + switch ((current_cpu_prid() >> 24) & 0xff) { case 0: __cpu_name[cpu] = "Au1000"; break; @@ -860,7 +856,7 @@ static inline void cpu_probe_alchemy(struct cpuinfo_mips *c, unsigned int cpu) break; case 4: __cpu_name[cpu] = "Au1200"; - if ((c->processor_id & 0xff) == 2) + if (cpu_prid_rev() == 2) __cpu_name[cpu] = "Au1250"; break; case 5: @@ -878,12 +874,12 @@ static inline void cpu_probe_sibyte(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_SB1: c->cputype = CPU_SB1; __cpu_name[cpu] = "SiByte SB1"; /* FPU in pass1 is known to have issues. */ - if ((c->processor_id & 0xff) < 0x02) + if (cpu_prid_rev() < 0x02) c->options &= ~(MIPS_CPU_FPU | MIPS_CPU_32FPR); break; case PRID_IMP_SB1A: @@ -896,7 +892,7 @@ static inline void cpu_probe_sibyte(struct cpuinfo_mips *c, unsigned int cpu) static inline void cpu_probe_sandcraft(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_SR71000: c->cputype = CPU_SR71000; __cpu_name[cpu] = "Sandcraft SR71000"; @@ -909,7 +905,7 @@ static inline void cpu_probe_sandcraft(struct cpuinfo_mips *c, unsigned int cpu) static inline void cpu_probe_nxp(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_PR4450: c->cputype = CPU_PR4450; __cpu_name[cpu] = "Philips PR4450"; @@ -921,7 +917,7 @@ static inline void cpu_probe_nxp(struct cpuinfo_mips *c, unsigned int cpu) static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_BMIPS32_REV4: case PRID_IMP_BMIPS32_REV8: c->cputype = CPU_BMIPS32; @@ -936,7 +932,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu) set_elf_platform(cpu, "bmips3300"); break; case PRID_IMP_BMIPS43XX: { - int rev = c->processor_id & 0xff; + int rev = cpu_prid_rev(); if (rev >= PRID_REV_BMIPS4380_LO && rev <= PRID_REV_BMIPS4380_HI) { @@ -962,7 +958,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu) static inline void cpu_probe_cavium(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_CAVIUM_CN38XX: case PRID_IMP_CAVIUM_CN31XX: case PRID_IMP_CAVIUM_CN30XX: @@ -995,7 +991,7 @@ static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int cpu) decode_configs(c); /* JZRISC does not implement the CP0 counter. */ c->options &= ~MIPS_CPU_COUNTER; - switch (c->processor_id & 0xff00) { + switch (cpu_prid_imp()) { case PRID_IMP_JZRISC: c->cputype = CPU_JZRISC; __cpu_name[cpu] = "Ingenic JZRISC"; @@ -1078,7 +1074,7 @@ __cpuinit void cpu_probe(void) c->cputype = CPU_UNKNOWN; c->processor_id = read_c0_prid(); - switch (c->processor_id & 0xff0000) { + switch (cpu_prid_comp()) { case PRID_COMP_LEGACY: cpu_probe_legacy(c, cpu); break; @@ -1157,7 +1153,7 @@ __cpuinit void cpu_report(void) struct cpuinfo_mips *c = ¤t_cpu_data; printk(KERN_INFO "CPU revision is: %08x (%s)\n", - c->processor_id, cpu_name_string()); + current_cpu_prid(), cpu_name_string()); if (c->options & MIPS_CPU_FPU) printk(KERN_INFO "FPU revision is: %08x\n", c->fpu_id); } diff --git a/arch/mips/kernel/cpufreq/Kconfig b/arch/mips/kernel/cpufreq/Kconfig index 58c601e..f631087 100644 --- a/arch/mips/kernel/cpufreq/Kconfig +++ b/arch/mips/kernel/cpufreq/Kconfig @@ -4,11 +4,36 @@ config MIPS_EXTERNAL_TIMER bool + select TIMER_SUPPORTS_CPUFREQ + +config R4K_TIMER_FOR_CPUFREQ + bool "Enable R4K Timer for CPUFreq Driver" + depends on !PREEMPT_RT + depends on CSRC_R4K && CEVT_R4K + depends on CPU_LOONGSON2F + select TIMER_SUPPORTS_CPUFREQ + default n + help + This option ensures the R4K Timer works normally with the CPUFreq + driver. Currently, It is only designed for Loongson2F with specific + optimization. + + If no external timer provided by your Loongson2F boards, this is + preferrable. + + If unsure, say NO, but for Gdium netbook, Say Yes Please to ensure + the netbook is not too hot! + +# For PREEMPT_RT need precise time, we must disable TIMER_SUPPORTS_CPUFREQ +# Exactly, we may need to disable the whole cpu freq support +config TIMER_SUPPORTS_CPUFREQ + bool + depends on !PREEMPT_RT config MIPS_CPUFREQ bool default y - depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER + depends on CPU_SUPPORTS_CPUFREQ && TIMER_SUPPORTS_CPUFREQ if MIPS_CPUFREQ diff --git a/arch/mips/kernel/cpufreq/Makefile b/arch/mips/kernel/cpufreq/Makefile index c3479a43..05a5715 100644 --- a/arch/mips/kernel/cpufreq/Makefile +++ b/arch/mips/kernel/cpufreq/Makefile @@ -2,4 +2,4 @@ # Makefile for the Linux/MIPS cpufreq. # -obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o loongson2_clock.o +obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o diff --git a/arch/mips/kernel/cpufreq/loongson2_clock.c b/arch/mips/kernel/cpufreq/loongson2_clock.c deleted file mode 100644 index cefc6e2..0000000 --- a/arch/mips/kernel/cpufreq/loongson2_clock.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology - * Author: Yanhua, yanh@lemote.com - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include -#include - -#include - -#include - -static LIST_HEAD(clock_list); -static DEFINE_SPINLOCK(clock_lock); -static DEFINE_MUTEX(clock_list_sem); - -/* Minimum CLK support */ -enum { - DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT, - DC_87PT, DC_DISABLE, DC_RESV -}; - -struct cpufreq_frequency_table loongson2_clockmod_table[] = { - {DC_RESV, CPUFREQ_ENTRY_INVALID}, - {DC_ZERO, CPUFREQ_ENTRY_INVALID}, - {DC_25PT, 0}, - {DC_37PT, 0}, - {DC_50PT, 0}, - {DC_62PT, 0}, - {DC_75PT, 0}, - {DC_87PT, 0}, - {DC_DISABLE, 0}, - {DC_RESV, CPUFREQ_TABLE_END}, -}; -EXPORT_SYMBOL_GPL(loongson2_clockmod_table); - -static struct clk cpu_clk = { - .name = "cpu_clk", - .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, - .rate = 800000000, -}; - -struct clk *clk_get(struct device *dev, const char *id) -{ - return &cpu_clk; -} -EXPORT_SYMBOL(clk_get); - -static void propagate_rate(struct clk *clk) -{ - struct clk *clkp; - - list_for_each_entry(clkp, &clock_list, node) { - if (likely(clkp->parent != clk)) - continue; - if (likely(clkp->ops && clkp->ops->recalc)) - clkp->ops->recalc(clkp); - if (unlikely(clkp->flags & CLK_RATE_PROPAGATES)) - propagate_rate(clkp); - } -} - -int clk_enable(struct clk *clk) -{ - return 0; -} -EXPORT_SYMBOL(clk_enable); - -void clk_disable(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_disable); - -unsigned long clk_get_rate(struct clk *clk) -{ - return (unsigned long)clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); - -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_put); - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - return clk_set_rate_ex(clk, rate, 0); -} -EXPORT_SYMBOL_GPL(clk_set_rate); - -int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) -{ - int ret = 0; - int regval; - int i; - - if (likely(clk->ops && clk->ops->set_rate)) { - unsigned long flags; - - spin_lock_irqsave(&clock_lock, flags); - ret = clk->ops->set_rate(clk, rate, algo_id); - spin_unlock_irqrestore(&clock_lock, flags); - } - - if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) - propagate_rate(clk); - - for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END; - i++) { - if (loongson2_clockmod_table[i].frequency == - CPUFREQ_ENTRY_INVALID) - continue; - if (rate == loongson2_clockmod_table[i].frequency) - break; - } - if (rate != loongson2_clockmod_table[i].frequency) - return -ENOTSUPP; - - clk->rate = rate; - - regval = LOONGSON_CHIPCFG0; - regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1); - LOONGSON_CHIPCFG0 = regval; - - return ret; -} -EXPORT_SYMBOL_GPL(clk_set_rate_ex); - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (likely(clk->ops && clk->ops->round_rate)) { - unsigned long flags, rounded; - - spin_lock_irqsave(&clock_lock, flags); - rounded = clk->ops->round_rate(clk, rate); - spin_unlock_irqrestore(&clock_lock, flags); - - return rounded; - } - - return rate; -} -EXPORT_SYMBOL_GPL(clk_round_rate); - -/* - * This is the simple version of Loongson-2 wait, Maybe we need do this in - * interrupt disabled content - */ - -DEFINE_SPINLOCK(loongson2_wait_lock); -void loongson2_cpu_wait(void) -{ - u32 cpu_freq; - unsigned long flags; - - spin_lock_irqsave(&loongson2_wait_lock, flags); - cpu_freq = LOONGSON_CHIPCFG0; - LOONGSON_CHIPCFG0 &= ~0x7; /* Put CPU into wait mode */ - LOONGSON_CHIPCFG0 = cpu_freq; /* Restore CPU state */ - spin_unlock_irqrestore(&loongson2_wait_lock, flags); -} -EXPORT_SYMBOL_GPL(loongson2_cpu_wait); - -MODULE_AUTHOR("Yanhua "); -MODULE_DESCRIPTION("cpufreq driver for Loongson 2F"); -MODULE_LICENSE("GPL"); diff --git a/arch/mips/kernel/cpufreq/loongson2_cpufreq.c b/arch/mips/kernel/cpufreq/loongson2_cpufreq.c index ae5db20..fb480d4 100644 --- a/arch/mips/kernel/cpufreq/loongson2_cpufreq.c +++ b/arch/mips/kernel/cpufreq/loongson2_cpufreq.c @@ -1,187 +1,305 @@ /* - * Cpufreq driver for the loongson-2 processors + * Cpufreq driver for the loongson-2 (>= 2F) processors * - * The 2E revision of loongson processor not support this feature. - * - * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology - * Author: Yanhua, yanh@lemote.com + * Copyright (C) 2010, Wu Zhangjin * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include -#include -#include -#include /* set_cpus_allowed() */ #include +#include #include -#include - #include -static uint nowait; +#define DC_RESV 0 -static struct clk *cpuclk; +/* + * For Loongson's frequency is not high, we set the minimum level as 50% to + * avoid spending too much time on freq switching + */ +static struct cpufreq_frequency_table clockmod_table[] = { + {DC_RESV, CPUFREQ_ENTRY_INVALID}, + {1, 0}, + {3, 0}, + {7, 0}, + {DC_RESV, CPUFREQ_TABLE_END}, +}; -static void (*saved_cpu_wait) (void); +static unsigned int max_cpufreq_khz; -static int loongson2_cpu_freq_notifier(struct notifier_block *nb, - unsigned long val, void *data); +static inline unsigned int idx_to_freq(unsigned int idx) +{ + /* + * freq = max_cpufreq_khz * ((index + 1) / total levels) + * = (max_cpufreq_khz * (index + 1)) / 8 + * = (max_cpufreq_khz * (index + 1)) >> 3 + */ + return (max_cpufreq_khz * (idx + 1)) >> 3; +} -static struct notifier_block loongson2_cpufreq_notifier_block = { - .notifier_call = loongson2_cpu_freq_notifier -}; +static inline unsigned int l2_cpufreq_get(unsigned int cpu) +{ + return idx_to_freq(LOONGSON_GET_CPUFREQ()); +} -static int loongson2_cpu_freq_notifier(struct notifier_block *nb, - unsigned long val, void *data) +static inline unsigned int idx_to_scale_shift(unsigned int newstate) { - if (val == CPUFREQ_POSTCHANGE) - current_cpu_data.udelay_val = loops_per_jiffy; - return 0; + /* + * newstate the the index of the array clockmod_table, the valid value + * is 1, 2, 3. + * + * The return value is the scale_shift for respective frequency. + * + * newstate | Freq_scale of CR80 | multiple | scale_shift + * 1 | 1 | 8 / (1+1) = 4 | 2 + * 2 | 3 | 8 / (3+1) = 2 | 1 + * 3 | 7 | 8 / (7+1) = 1 | 0 + * + * scale_shift = 3 - newstate + */ + + return 3 - newstate; +} + +#ifdef CONFIG_R4K_TIMER_FOR_CPUFREQ +extern unsigned int scale_shift; +extern void update_virtual_count(unsigned int target_scale_shift); + +static inline void sync_virtual_count(unsigned int target_scale_shift) +{ + update_virtual_count(target_scale_shift); + scale_shift = target_scale_shift; } -static unsigned int loongson2_cpufreq_get(unsigned int cpu) +static void notrace l2_cpufreq_set(unsigned int newstate) { - return clk_get_rate(cpuclk); + unsigned long flag; + unsigned int target_scale_shift; + + target_scale_shift = idx_to_scale_shift(newstate); + + pr_debug("%s: scale_shift = %d, target_scale_shift = %d, target_set: %d\n", + __func__, scale_shift, target_scale_shift, + clockmod_table[newstate].index); + + /* For we are UP, Give up the spin lock... */ + raw_local_irq_save(flag); + /* When freq becomes higher ... */ + if (scale_shift > target_scale_shift) + sync_virtual_count(target_scale_shift); + /* Set the CR80 register */ + LOONGSON_SET_CPUFREQ(clockmod_table[newstate].index); + /* When freq becomes lower ... */ + if (scale_shift < target_scale_shift) + sync_virtual_count(target_scale_shift); + raw_local_irq_restore(flag); + + pr_debug("%s: scale_shift = %d, target_scale_shift = %d, target_set: %d\n", + __func__, scale_shift, target_scale_shift, + clockmod_table[newstate].index); } /* - * Here we notify other drivers of the proposed change and the final change. + * The CPUFreq driver will put the cpu into the lowest level(1), no need to do + * it here. If we do it here, some CPUFreq governors will not function well, + * so, disable the cpu_wait() completely when the R4K is used. */ -static int loongson2_cpufreq_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) + +#if 0 +/* + * Put CPU into the 1st level, We have no good method to recover the timesplice + * in wait mode, so, we only allow the CPU gointo the 1st level, not the ZERO + * level. + * + * To avoid recording the garbage result in the kernel tracing, we don't call + * notifiers when FUNCTION_TRACER is enabled. + */ + +void notrace loongson2_cpu_wait(void) { - unsigned int cpu = policy->cpu; - unsigned int newstate = 0; - cpumask_t cpus_allowed; +#ifdef CONFIG_FUNCTION_TRACER + /* If we are already in the 1st level, stop resetting it. */ + if (LOONGSON_GET_CPUFREQ() != 1) + l2_cpufreq_set(1); +#else + { struct cpufreq_freqs freqs; - unsigned int freq; - if (!cpu_online(cpu)) - return -ENODEV; + freqs.old = l2_cpufreq_get(0); + freqs.new = idx_to_freq(1); - cpus_allowed = current->cpus_allowed; - set_cpus_allowed_ptr(current, cpumask_of(cpu)); + if (freqs.new == freqs.old) + return; - if (cpufreq_frequency_table_target - (policy, &loongson2_clockmod_table[0], target_freq, relation, - &newstate)) - return -EINVAL; + /* notifiers */ + freqs.cpu = 0; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - freq = - ((cpu_clock_freq / 1000) * - loongson2_clockmod_table[newstate].index) / 8; - if (freq < policy->min || freq > policy->max) - return -EINVAL; + /* setting the cpu frequency as the 1st level */ + l2_cpufreq_set(1); - pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000); + /* notifiers */ + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } +#endif +} +#else +#define loongson2_cpu_wait NULL +#endif - freqs.cpu = cpu; - freqs.old = loongson2_cpufreq_get(cpu); - freqs.new = freq; - freqs.flags = 0; +#else /* MIPS_EXTERNAL_TIMER */ + +static void l2_cpufreq_set(unsigned int newstate) +{ + unsigned long flags; + + local_irq_save(flags); + LOONGSON_SET_CPUFREQ(clockmod_table[newstate].index); + local_irq_restore(flags); +} + +static void notrace loongson2_cpu_wait(void) +{ + u32 cpufreq; + ktime_t kt1, kt2; + s64 idle_time_ns; + unsigned long flags; + + local_irq_save(flags); + kt1 = ktime_get_real(); + sched_clock_idle_sleep_event(); + + /* Record the cpu frequency */ + cpufreq = LOONGSON_CHIPCFG0; + + /* + * Currently, there is no wait instruction in Loongson platform, + * herein, we emulate the wait mode via setting the cpu frequency to + * the lowest level to put it into the standby mode, which can be waked + * up by external interrupts + */ + LOONGSON_SET_CPUFREQ(0); + + /* Resotore it */ + LOONGSON_CHIPCFG0 = cpufreq; + + /* + * report back to the scheduler how long we deep-idled + */ + kt2 = ktime_get_real(); + idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1)); + sched_clock_idle_wakeup_event(idle_time_ns); + local_irq_restore(flags); +} + +#endif /* CONFIG_R4K_TIMER_FOR_CPUFREQ */ + +static inline void register_cpu_wait(void) +{ + cpu_wait = loongson2_cpu_wait; +} +static inline void unregister_cpu_wait(void) +{ + cpu_wait = NULL; +} + +static int l2_cpufreq_target(struct cpufreq_policy *policy, unsigned int + target_freq, unsigned int relation) +{ + unsigned int newstate; + struct cpufreq_freqs freqs; + + if (cpufreq_frequency_table_target(policy, &clockmod_table[0], + target_freq, relation, &newstate)) + return -EINVAL; + + freqs.old = l2_cpufreq_get(policy->cpu); + freqs.new = idx_to_freq(clockmod_table[newstate].index); if (freqs.new == freqs.old) return 0; /* notifiers */ + freqs.cpu = policy->cpu; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - set_cpus_allowed_ptr(current, &cpus_allowed); - /* setting the cpu frequency */ - clk_set_rate(cpuclk, freq); + l2_cpufreq_set(newstate); /* notifiers */ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - pr_debug("cpufreq: set frequency %u kHz\n", freq); - return 0; } -static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) +static int l2_cpufreq_cpu_init(struct cpufreq_policy *policy) { int i; - if (!cpu_online(policy->cpu)) - return -ENODEV; + /* get max cpu frequency in khz */ + max_cpufreq_khz = cpu_clock_freq / 1000; - cpuclk = clk_get(NULL, "cpu_clk"); - if (IS_ERR(cpuclk)) { - printk(KERN_ERR "cpufreq: couldn't get CPU clk\n"); - return PTR_ERR(cpuclk); - } - - cpuclk->rate = cpu_clock_freq / 1000; - if (!cpuclk->rate) - return -EINVAL; + /* table init */ + for (i = 1; clockmod_table[i].index != DC_RESV; i++) + clockmod_table[i].frequency = idx_to_freq(clockmod_table[i].index); - /* clock table init */ - for (i = 2; - (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END); - i++) - loongson2_clockmod_table[i].frequency = (cpuclk->rate * i) / 8; + cpufreq_frequency_table_get_attr(clockmod_table, policy->cpu); - policy->cur = loongson2_cpufreq_get(policy->cpu); + /* cpuinfo and default policy values */ - cpufreq_frequency_table_get_attr(&loongson2_clockmod_table[0], - policy->cpu); + policy->cur = max_cpufreq_khz; - return cpufreq_frequency_table_cpuinfo(policy, - &loongson2_clockmod_table[0]); + return cpufreq_frequency_table_cpuinfo(policy, &clockmod_table[0]); } -static int loongson2_cpufreq_verify(struct cpufreq_policy *policy) +static int l2_cpufreq_verify(struct cpufreq_policy *policy) { - return cpufreq_frequency_table_verify(policy, - &loongson2_clockmod_table[0]); + return cpufreq_frequency_table_verify(policy, &clockmod_table[0]); } -static int loongson2_cpufreq_exit(struct cpufreq_policy *policy) +static int l2_cpufreq_cpu_exit(struct cpufreq_policy *policy) { - clk_put(cpuclk); + cpufreq_frequency_table_put_attr(policy->cpu); return 0; } -static struct freq_attr *loongson2_table_attr[] = { +static struct freq_attr *clockmod_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, }; -static struct cpufreq_driver loongson2_cpufreq_driver = { +static struct cpufreq_driver l2_cpufreq_driver = { .owner = THIS_MODULE, - .name = "loongson2", - .init = loongson2_cpufreq_cpu_init, - .verify = loongson2_cpufreq_verify, - .target = loongson2_cpufreq_target, - .get = loongson2_cpufreq_get, - .exit = loongson2_cpufreq_exit, - .attr = loongson2_table_attr, + .name = "l2_cpufreq", + .init = l2_cpufreq_cpu_init, + .exit = l2_cpufreq_cpu_exit, + .verify = l2_cpufreq_verify, + .target = l2_cpufreq_target, + .get = l2_cpufreq_get, + .attr = clockmod_attr, }; static struct platform_device_id platform_device_ids[] = { { - .name = "loongson2_cpufreq", + .name = "l2_cpufreq", }, {} }; - MODULE_DEVICE_TABLE(platform, platform_device_ids); static struct platform_driver platform_driver = { .driver = { - .name = "loongson2_cpufreq", + .name = "l2_cpufreq", .owner = THIS_MODULE, }, .id_table = platform_device_ids, }; -static int __init cpufreq_init(void) +static int __init l2_cpufreq_init(void) { int ret; @@ -190,38 +308,29 @@ static int __init cpufreq_init(void) if (ret) return ret; - pr_info("cpufreq: Loongson-2F CPU frequency driver.\n"); - - cpufreq_register_notifier(&loongson2_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - - ret = cpufreq_register_driver(&loongson2_cpufreq_driver); - - if (!ret && !nowait) { - saved_cpu_wait = cpu_wait; - cpu_wait = loongson2_cpu_wait; + ret = cpufreq_register_driver(&l2_cpufreq_driver); + if (ret) { + platform_driver_unregister(&platform_driver); + return ret; } - return ret; + register_cpu_wait(); + + return 0; } -static void __exit cpufreq_exit(void) +static void __exit l2_cpufreq_exit(void) { - if (!nowait && saved_cpu_wait) - cpu_wait = saved_cpu_wait; - cpufreq_unregister_driver(&loongson2_cpufreq_driver); - cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); + unregister_cpu_wait(); + + cpufreq_unregister_driver(&l2_cpufreq_driver); platform_driver_unregister(&platform_driver); } -module_init(cpufreq_init); -module_exit(cpufreq_exit); - -module_param(nowait, uint, 0644); -MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait"); +module_init(l2_cpufreq_init); +module_exit(l2_cpufreq_exit); -MODULE_AUTHOR("Yanhua "); -MODULE_DESCRIPTION("cpufreq driver for Loongson2F"); +MODULE_AUTHOR("Wu Zhangjin "); +MODULE_DESCRIPTION("cpufreq driver for Loongson-2"); MODULE_LICENSE("GPL"); diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c index decd1fa..b45a139 100644 --- a/arch/mips/kernel/csrc-r4k.c +++ b/arch/mips/kernel/csrc-r4k.c @@ -6,27 +6,236 @@ * Copyright (C) 2007 by Ralf Baechle */ #include +#include #include +#include +#include +#include +#include #include -static cycle_t c0_hpt_read(struct clocksource *cs) +#include + +/* + * Some MIPS cpu can change their frequency, meaning that read_c0_count doesn't + * run at the same speed :( + * Have to handle this case. + */ +#ifdef CONFIG_R4K_TIMER_FOR_CPUFREQ + +unsigned int scale_shift; +EXPORT_SYMBOL(scale_shift); +#define hpt_scale_up(cycle) ((cycle) << scale_shift) + +/* + * read_virtual_count -- read the virtual 64bit count + * + * This should be called with irq disabled and spin lock + * + * @now: This should be read from the real count register + * Return the virtual but precise 64bit count + */ + +static u32 hpt_last_read; + +cycle_t notrace read_virtual_count(void) +{ + static u64 hpt_last_cnt; + u64 diff; + unsigned int now; + unsigned long flags; + + local_irq_save(flags); + + now = read_c0_count(); + + if (unlikely(!hpt_last_read)) { + /* + * The '1st' time c0_hpt_read() is called so, the cycle read + * from the counter is the real one + * + * When resuming from PM, we also reset it. + */ + hpt_last_cnt = hpt_last_read = now; + } else { + /* Get diff and Check for counter overflow */ + diff = (now - hpt_last_read) & CLOCKSOURCE_MASK(32); + /* Calculate the real cycles */ + hpt_last_cnt += hpt_scale_up(diff); + /* Save for the next access */ + hpt_last_read = now; + } + + local_irq_restore(flags); + + return hpt_last_cnt; +} + +static inline void reset_virtual_count(void) +{ + hpt_last_read = 0; +} + +static void r4k_suspend(struct clocksource *cs) +{ +} + +static void r4k_resume(struct clocksource *cs) +{ + reset_virtual_count(); +} + +#define hpt_read() read_virtual_count() +#else +#define setup_r4k_for_cpufreq(clock) +#define hpt_read() read_c0_count() +#define r4k_suspend NULL +#define r4k_resume NULL +#endif /* CONFIG_R4K_TIMER_FOR_CPUFREQ */ + +cycle_t notrace c0_hpt_read(struct clocksource *cs) +{ + return hpt_read(); +} + +/* + * MIPS sched_clock implementation. + * + * Because the hardware timer period is quite short and because cnt32_to_63() + * needs to be called at least once per half period to work properly, a kernel + * timer is set up to ensure this requirement is always met. + * + * Please refer to include/linux/cnt32_to_63.h and arch/arm/plat-orion/time.c + */ +#define CLOCK2NS_SCALE_FACTOR 8 + +static unsigned long clock2ns_scale __read_mostly; + +#ifdef CONFIG_R4K_TIMER_FOR_CPUFREQ +#define hpt_read64() read_virtual_count() +#else +#define hpt_read64() cnt32_to_63(read_c0_count()) +#endif +unsigned long long notrace sched_clock(void) +{ + return (hpt_read64() * clock2ns_scale) >> CLOCK2NS_SCALE_FACTOR; +} + +#ifndef CONFIG_R4K_TIMER_FOR_CPUFREQ +static struct timer_list cnt32_to_63_keepwarm_timer; + +static void cnt32_to_63_keepwarm(unsigned long data) { - return read_c0_count(); + mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data)); + sched_clock(); +} +#endif + +static inline void setup_hres_sched_clock(unsigned long clock) +{ + unsigned long long v; +#ifndef CONFIG_R4K_TIMER_FOR_CPUFREQ + unsigned long data; +#endif + + v = NSEC_PER_SEC; + v <<= CLOCK2NS_SCALE_FACTOR; + v += clock/2; + do_div(v, clock); + /* + * We want an even value to automatically clear the top bit + * returned by cnt32_to_63() without an additional run time + * instruction. So if the LSB is 1 then round it up. + */ + if (v & 1) + v++; + clock2ns_scale = v; +#ifndef CONFIG_R4K_TIMER_FOR_CPUFREQ + data = 0x80000000UL / clock * HZ; + setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, data); + mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data)); +#endif } static struct clocksource clocksource_mips = { .name = "MIPS", .read = c0_hpt_read, +#ifdef CONFIG_R4K_TIMER_FOR_CPUFREQ + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY, +#else .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, +#endif +#ifdef CONFIG_PM + .suspend = r4k_suspend, + .resume = r4k_resume, +#endif }; +static u64 r4k_udelay_factor __read_mostly; +static u64 r4k_ndelay_factor __read_mostly; + +static inline void r4k_setup_delays(void) +{ + r4k_udelay_factor = mips_hpt_frequency; + do_div(r4k_udelay_factor, 1000000); + /* + * For __ndelay we divide by 2^16, so the factor is multiplied + * by the same amount. + */ + r4k_ndelay_factor = (r4k_udelay_factor * 0x10000ull); + do_div(r4k_ndelay_factor, 1000ull); + + lpj_fine = mips_hpt_frequency / HZ; +} + +static inline void rep_nop(void) +{ + __asm__ __volatile__("nop;" : : : "memory"); +} + +void __delay(unsigned int loops) +{ + cycle_t now, bclock; + + preempt_disable(); + bclock = hpt_read64(); + for (;;) { + now = hpt_read64(); + if ((now - bclock) >= loops) + break; + /* Allow RT tasks to run */ + preempt_enable(); + rep_nop(); + preempt_disable(); + } + preempt_enable(); +} +EXPORT_SYMBOL(__delay); + +void __udelay(unsigned int us) +{ + __delay(us * r4k_udelay_factor); +} +EXPORT_SYMBOL(__udelay); + +void __ndelay(unsigned int ns) +{ + __delay((ns * r4k_ndelay_factor) >> 16); +} +EXPORT_SYMBOL(__ndelay); + int __init init_r4k_clocksource(void) { if (!cpu_has_counter || !mips_hpt_frequency) return -ENXIO; + r4k_setup_delays(); + + setup_hres_sched_clock(mips_hpt_frequency); + /* Calculate a somewhat reasonable rating value */ clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000; diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index b30cb25..ce40b54 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -64,12 +64,12 @@ void __noreturn cpu_idle(void) smtc_idle_loop_hook(); #endif + /* Don't trace irqs off for idle */ + stop_critical_timings(); if (cpu_wait) { - /* Don't trace irqs off for idle */ - stop_critical_timings(); (*cpu_wait)(); - start_critical_timings(); } + start_critical_timings(); } #ifdef CONFIG_HOTPLUG_CPU if (!cpu_online(cpu) && !cpu_isset(cpu, cpu_callin_map) && diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 1d81316..293f1ff 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -26,6 +26,18 @@ .align 5 NESTED(handle_sys, PT_SIZE, sp) +#ifdef CONFIG_MIPS_USER_RDTSC + MFC0 k0, CP0_EPC + lw k1, 0(k0) + sltiu k1, k1, 0x1c + bne k1, zero, 1f # Normal syscall code: 0x0c < 0x1c + nop + mfc0 v0, CP0_COUNT # Get TSC + PTR_ADDIU k0, 4 # ret from syscall + MTC0 k0, CP0_EPC + eret +1: +#endif /* CONFIG_MIPS_USER_RDTSC */ .set noat SAVE_SOME TRACE_IRQS_ON_RELOAD diff --git a/arch/mips/kernel/spram.c b/arch/mips/kernel/spram.c index 1821d12..76649b5 100644 --- a/arch/mips/kernel/spram.c +++ b/arch/mips/kernel/spram.c @@ -202,7 +202,7 @@ void __cpuinit spram_config(void) struct cpuinfo_mips *c = ¤t_cpu_data; unsigned int config0; - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_24K: case CPU_34K: case CPU_74K: diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index 1083ad4..16a6daa 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -105,7 +105,7 @@ static __init int cpu_has_mfc0_count_bug(void) * The published errata for the R4400 up to 3.0 say the CPU * has the mfc0 from count bug. */ - if ((current_cpu_data.processor_id & 0xff) <= 0x30) + if (cpu_prid_rev() <= 0x30) return 1; /* @@ -119,6 +119,11 @@ static __init int cpu_has_mfc0_count_bug(void) void __init time_init(void) { +#ifdef CONFIG_HR_SCHED_CLOCK + if (!mips_clockevent_init() || !cpu_has_mfc0_count_bug()) + write_c0_count(0); +#endif + plat_time_init(); if (!mips_clockevent_init() || !cpu_has_mfc0_count_bug()) diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index cbea618..a8dd1b1 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -616,14 +616,14 @@ static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode) regs->regs[rt] = smp_processor_id(); return 0; case 1: /* SYNCI length */ - regs->regs[rt] = min(current_cpu_data.dcache.linesz, - current_cpu_data.icache.linesz); + regs->regs[rt] = min(cpu_dcache_line_size(), + cpu_icache_line_size()); return 0; case 2: /* Read count register */ regs->regs[rt] = read_c0_count(); return 0; case 3: /* Count register resolution */ - switch (current_cpu_data.cputype) { + switch (current_cpu_type()) { case CPU_20KC: case CPU_25KF: regs->regs[rt] = 1; diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index b2cad4f..e6ebe8b 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -2,9 +2,13 @@ # Makefile for MIPS-specific library files.. # -lib-y += csum_partial.o delay.o memcpy.o memcpy-inatomic.o memset.o \ +lib-y += csum_partial.o memcpy.o memcpy-inatomic.o memset.o \ strlen_user.o strncpy_user.o strnlen_user.o uncached.o +ifndef CONFIG_CSRC_R4K +lib-y += delay.o +endif + obj-y += iomap.o obj-$(CONFIG_PCI) += iomap-pci.o diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig index aca93ee..421ae85 100644 --- a/arch/mips/loongson/Kconfig +++ b/arch/mips/loongson/Kconfig @@ -32,61 +32,121 @@ config LEMOTE_FULOONG2E config LEMOTE_MACH2F bool "Lemote Loongson 2F family machines" - select ARCH_SPARSEMEM_ENABLE + select ARCH_SPARSEMEM_ENABLE if HIBERNATION select BOARD_SCACHE select BOOT_ELF32 select CEVT_R4K if ! MIPS_EXTERNAL_TIMER select CPU_HAS_WB - select CS5536 + select CS5536 if PCI select CSRC_R4K if ! MIPS_EXTERNAL_TIMER select DMA_NONCOHERENT select GENERIC_ISA_DMA_SUPPORT_BROKEN select HW_HAS_PCI select I8259 select IRQ_CPU - select ISA select SYS_HAS_CPU_LOONGSON2F select SYS_HAS_EARLY_PRINTK select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_64BIT_KERNEL - select SYS_SUPPORTS_HIGHMEM + select SYS_SUPPORTS_HIGHMEM if ! EMBEDDED select SYS_SUPPORTS_LITTLE_ENDIAN - select LOONGSON_MC146818 + select LOONGSON_MC146818 if RTC_DRV_CMOS help Lemote Loongson 2F family machines utilize the 2F revision of Loongson processor and the AMD CS5536 south bridge. These family machines include fuloong2f mini PC, yeeloong2f notebook, LingLoong allinone PC and so forth. + +config DEXXON_GDIUM + bool "Dexxon Gdium Netbook" + select ARCH_SPARSEMEM_ENABLE + select BOARD_SCACHE + select BOOT_ELF32 + select CEVT_R4K if ! MIPS_EXTERNAL_TIMER + select CPU_HAS_WB + select CSRC_R4K if ! MIPS_EXTERNAL_TIMER + select DMA_NONCOHERENT + select GENERIC_ISA_DMA_SUPPORT_BROKEN + select HW_HAS_PCI + select I8259 + select IRQ_CPU + select ISA + select SYS_HAS_CPU_LOONGSON2F + select SYS_HAS_EARLY_PRINTK + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_64BIT_KERNEL + select SYS_SUPPORTS_HIGHMEM + select SYS_SUPPORTS_LITTLE_ENDIAN + select ARCH_REQUIRE_GPIOLIB + select HAVE_PWM if MFD_SM501 + help + Dexxon gdium netbook based on Loongson 2F and SM502. endchoice config CS5536 + select CS5536_IDE if (PATA_AMD || BLK_DEV_AMD74XX || PATA_CS5536) + select CS5536_OHCI if USB_OHCI_HCD + select CS5536_EHCI if USB_EHCI_HCD + select CS5536_AUDIO if SND_CS5535AUDIO + select CS5536_ISA + bool + +config CS5536_ISA + select ISA + bool + +config CS5536_IDE + bool + +config CS5536_OHCI + bool + +config CS5536_EHCI + bool + +config CS5536_AUDIO bool config CS5536_MFGPT bool "CS5536 MFGPT Timer" + depends on BROKEN depends on CS5536 select MIPS_EXTERNAL_TIMER help This option enables the mfgpt0 timer of AMD CS5536. - If you want to enable the Loongson2 CPUFreq Driver, Please enable - this option at first, otherwise, You will get wrong system time. + To enable the Loongson2 CPUFreq Driver, this option is a + precondition, but from 2.6.37, we have a better choice, that is + CONFIG_R4K_TIMER_FOR_CPUFREQ=y. To get a good CPUFreq driver, that + option should be enabled with the ondemand governor. - If unsure, say Yes. + If unsure, say NO. config LOONGSON_SUSPEND bool default y depends on CPU_SUPPORTS_CPUFREQ && SUSPEND -config LOONGSON_UART_BASE - bool - default y - depends on EARLY_PRINTK || SERIAL_8250 - config LOONGSON_MC146818 bool default n +config GDIUM_PWM_CLOCK + tristate "Gdium PWM Timer" + default n + depends on HAVE_PWM && EXPERIMENTAL && BROKEN + select MIPS_EXTERNAL_TIMER + help + This options enables the experimental sm501-pwm based clock. With it, + you may be possible to use the loongson2f cpufreq driver. + +config GDIUM_VERSION + int "Configure Gdium Version" + depends on DEXXON_GDIUM + default "3" + help + I have no information about how to determine which version your board + is, If the default config doesn't work for it, please change it to + smaller ones. endif # MACH_LOONGSON diff --git a/arch/mips/loongson/Makefile b/arch/mips/loongson/Makefile index 2b76cb0..1c3ae7c 100644 --- a/arch/mips/loongson/Makefile +++ b/arch/mips/loongson/Makefile @@ -15,3 +15,9 @@ obj-$(CONFIG_LEMOTE_FULOONG2E) += fuloong-2e/ # obj-$(CONFIG_LEMOTE_MACH2F) += lemote-2f/ + +# +# Dexxon gdium netbook, based on loongson 2F and SM502 +# + +obj-$(CONFIG_DEXXON_GDIUM) += gdium/ diff --git a/arch/mips/loongson/Platform b/arch/mips/loongson/Platform index 29692e5..6be5dff 100644 --- a/arch/mips/loongson/Platform +++ b/arch/mips/loongson/Platform @@ -30,3 +30,4 @@ platform-$(CONFIG_MACH_LOONGSON) += loongson/ cflags-$(CONFIG_MACH_LOONGSON) += -I$(srctree)/arch/mips/include/asm/mach-loongson -mno-branch-likely load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000 load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000 +load-$(CONFIG_DEXXON_GDIUM) += 0xffffffff80200000 diff --git a/arch/mips/loongson/common/Makefile b/arch/mips/loongson/common/Makefile index e526488..ed90596 100644 --- a/arch/mips/loongson/common/Makefile +++ b/arch/mips/loongson/common/Makefile @@ -3,7 +3,10 @@ # obj-y += setup.o init.o cmdline.o env.o time.o reset.o irq.o \ - pci.o bonito-irq.o mem.o machtype.o platform.o + bonito-irq.o mem.o machtype.o platform.o + +obj-$(CONFIG_PCI) += pci.o + obj-$(CONFIG_GENERIC_GPIO) += gpio.o # @@ -11,7 +14,6 @@ obj-$(CONFIG_GENERIC_GPIO) += gpio.o # obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_SERIAL_8250) += serial.o -obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o obj-$(CONFIG_LOONGSON_MC146818) += rtc.o # diff --git a/arch/mips/loongson/common/cmdline.c b/arch/mips/loongson/common/cmdline.c index 353e1d2..8bce6a8 100644 --- a/arch/mips/loongson/common/cmdline.c +++ b/arch/mips/loongson/common/cmdline.c @@ -17,10 +17,15 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ +#include #include #include +/* the kernel command line copied from arcs_cmdline */ +char loongson_cmdline[COMMAND_LINE_SIZE]; +EXPORT_SYMBOL(loongson_cmdline); + void __init prom_init_cmdline(void) { int prom_argc; @@ -45,4 +50,31 @@ void __init prom_init_cmdline(void) } prom_init_machtype(); + + /* append machine specific command line */ + switch (mips_machtype) { + case MACH_LEMOTE_LL2F: + if ((strstr(arcs_cmdline, "video=")) == NULL) + strcat(arcs_cmdline, " video=sisfb:1360x768-16@60"); + break; + case MACH_LEMOTE_FL2F: + if ((strstr(arcs_cmdline, "ide_core.ignore_cable=")) == NULL) + strcat(arcs_cmdline, " ide_core.ignore_cable=0"); + break; + case MACH_LEMOTE_ML2F7: + /* Mengloong-2F has a 800x480 screen */ + if ((strstr(arcs_cmdline, "vga=")) == NULL) + strcat(arcs_cmdline, " vga=0x313"); + break; + case MACH_DEXXON_GDIUM2F10: + /* gdium has a 1024x600 screen */ + if ((strstr(arcs_cmdline, "video=")) == NULL) + strcat(arcs_cmdline, " video=sm501fb:1024x600@60"); + break; + default: + break; + } + + /* copy arcs_cmdline into loongson_cmdline */ + strncpy(loongson_cmdline, arcs_cmdline, COMMAND_LINE_SIZE); } diff --git a/arch/mips/loongson/common/cs5536/Makefile b/arch/mips/loongson/common/cs5536/Makefile index f12e640..70f6057 100644 --- a/arch/mips/loongson/common/cs5536/Makefile +++ b/arch/mips/loongson/common/cs5536/Makefile @@ -2,8 +2,13 @@ # Makefile for CS5536 support. # -obj-$(CONFIG_CS5536) += cs5536_pci.o cs5536_ide.o cs5536_acc.o cs5536_ohci.o \ - cs5536_isa.o cs5536_ehci.o +obj-$(CONFIG_CS5536) += cs5536_pci.o + +obj-$(CONFIG_ISA) += cs5536_isa.o +obj-$(CONFIG_CS5536_IDE) += cs5536_ide.o +obj-$(CONFIG_CS5536_AUDIO) += cs5536_acc.o +obj-$(CONFIG_CS5536_OHCI) += cs5536_ohci.o +obj-$(CONFIG_CS5536_EHCI) += cs5536_ehci.o # # Enable cs5536 mfgpt Timer diff --git a/arch/mips/loongson/common/cs5536/cs5536_acc.c b/arch/mips/loongson/common/cs5536/cs5536_acc.c index b3fd5ea..602b539 100644 --- a/arch/mips/loongson/common/cs5536/cs5536_acc.c +++ b/arch/mips/loongson/common/cs5536/cs5536_acc.c @@ -18,7 +18,7 @@ void pci_acc_write_reg(int reg, u32 value) { - u32 hi = 0, lo = value; + u32 hi, lo; switch (reg) { case PCI_COMMAND: @@ -66,75 +66,73 @@ void pci_acc_write_reg(int reg, u32 value) u32 pci_acc_read_reg(int reg) { u32 hi, lo; - u32 conf_data = 0; + u32 cfg = 0; switch (reg) { case PCI_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_ACC_DEVICE_ID, CS5536_VENDOR_ID); + cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_AUDIO, + PCI_VENDOR_ID_AMD); break; case PCI_COMMAND: _rdmsr(GLIU_MSR_REG(GLIU_IOD_BM1), &hi, &lo); if (((lo & 0xfff00000) || (hi & 0x000000ff)) && ((hi & 0xf0000000) == 0xa0000000)) - conf_data |= PCI_COMMAND_IO; + cfg |= PCI_COMMAND_IO; _rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo); if ((lo & 0x300) == 0x300) - conf_data |= PCI_COMMAND_MASTER; + cfg |= PCI_COMMAND_MASTER; break; case PCI_STATUS: - conf_data |= PCI_STATUS_66MHZ; - conf_data |= PCI_STATUS_FAST_BACK; + cfg |= PCI_STATUS_66MHZ; + cfg |= PCI_STATUS_FAST_BACK; _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo); if (lo & SB_PARE_ERR_FLAG) - conf_data |= PCI_STATUS_PARITY; - conf_data |= PCI_STATUS_DEVSEL_MEDIUM; + cfg |= PCI_STATUS_PARITY; + cfg |= PCI_STATUS_DEVSEL_MEDIUM; break; case PCI_CLASS_REVISION: _rdmsr(ACC_MSR_REG(ACC_CAP), &hi, &lo); - conf_data = lo & 0x000000ff; - conf_data |= (CS5536_ACC_CLASS_CODE << 8); + cfg = lo & 0x000000ff; + cfg |= (CS5536_ACC_CLASS_CODE << 8); break; case PCI_CACHE_LINE_SIZE: - conf_data = - CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, - PCI_NORMAL_LATENCY_TIMER); + cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, + PCI_NORMAL_LATENCY_TIMER); break; case PCI_BAR0_REG: _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo); if (lo & SOFT_BAR_ACC_FLAG) { - conf_data = CS5536_ACC_RANGE | + cfg = CS5536_ACC_RANGE | PCI_BASE_ADDRESS_SPACE_IO; lo &= ~SOFT_BAR_ACC_FLAG; _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo); } else { _rdmsr(GLIU_MSR_REG(GLIU_IOD_BM1), &hi, &lo); - conf_data = (hi & 0x000000ff) << 12; - conf_data |= (lo & 0xfff00000) >> 20; - conf_data |= 0x01; - conf_data &= ~0x02; + cfg = (hi & 0x000000ff) << 12; + cfg |= (lo & 0xfff00000) >> 20; + cfg |= 0x01; + cfg &= ~0x02; } break; case PCI_CARDBUS_CIS: - conf_data = PCI_CARDBUS_CIS_POINTER; + cfg = PCI_CARDBUS_CIS_POINTER; break; case PCI_SUBSYSTEM_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_ACC_SUB_ID, CS5536_SUB_VENDOR_ID); + cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_AUDIO, + PCI_VENDOR_ID_AMD); break; case PCI_ROM_ADDRESS: - conf_data = PCI_EXPANSION_ROM_BAR; + cfg = PCI_EXPANSION_ROM_BAR; break; case PCI_CAPABILITY_LIST: - conf_data = PCI_CAPLIST_USB_POINTER; + cfg = PCI_CAPLIST_USB_POINTER; break; case PCI_INTERRUPT_LINE: - conf_data = - CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_ACC_INTR); + cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_ACC_INTR); break; default: break; } - return conf_data; + return cfg; } diff --git a/arch/mips/loongson/common/cs5536/cs5536_ehci.c b/arch/mips/loongson/common/cs5536/cs5536_ehci.c index 5b5cbba..0fd49b8 100644 --- a/arch/mips/loongson/common/cs5536/cs5536_ehci.c +++ b/arch/mips/loongson/common/cs5536/cs5536_ehci.c @@ -18,7 +18,7 @@ void pci_ehci_write_reg(int reg, u32 value) { - u32 hi = 0, lo = value; + u32 hi, lo; switch (reg) { case PCI_COMMAND: @@ -78,83 +78,78 @@ void pci_ehci_write_reg(int reg, u32 value) u32 pci_ehci_read_reg(int reg) { - u32 conf_data = 0; + u32 cfg = 0; u32 hi, lo; switch (reg) { case PCI_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_EHCI_DEVICE_ID, CS5536_VENDOR_ID); + case PCI_SUBSYSTEM_VENDOR_ID: + cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_EHC, + PCI_VENDOR_ID_AMD); break; case PCI_COMMAND: _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo); if (hi & PCI_COMMAND_MASTER) - conf_data |= PCI_COMMAND_MASTER; + cfg |= PCI_COMMAND_MASTER; if (hi & PCI_COMMAND_MEMORY) - conf_data |= PCI_COMMAND_MEMORY; + cfg |= PCI_COMMAND_MEMORY; break; case PCI_STATUS: - conf_data |= PCI_STATUS_66MHZ; - conf_data |= PCI_STATUS_FAST_BACK; + cfg |= PCI_STATUS_66MHZ; + cfg |= PCI_STATUS_FAST_BACK; _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo); if (lo & SB_PARE_ERR_FLAG) - conf_data |= PCI_STATUS_PARITY; - conf_data |= PCI_STATUS_DEVSEL_MEDIUM; + cfg |= PCI_STATUS_PARITY; + cfg |= PCI_STATUS_DEVSEL_MEDIUM; break; case PCI_CLASS_REVISION: _rdmsr(USB_MSR_REG(USB_CAP), &hi, &lo); - conf_data = lo & 0x000000ff; - conf_data |= (CS5536_EHCI_CLASS_CODE << 8); + cfg = lo & 0x000000ff; + cfg |= (CS5536_EHCI_CLASS_CODE << 8); break; case PCI_CACHE_LINE_SIZE: - conf_data = - CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, - PCI_NORMAL_LATENCY_TIMER); + cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, + PCI_NORMAL_LATENCY_TIMER); break; case PCI_BAR0_REG: _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo); if (lo & SOFT_BAR_EHCI_FLAG) { - conf_data = CS5536_EHCI_RANGE | + cfg = CS5536_EHCI_RANGE | PCI_BASE_ADDRESS_SPACE_MEMORY; lo &= ~SOFT_BAR_EHCI_FLAG; _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo); } else { _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo); - conf_data = lo & 0xfffff000; + cfg = lo & 0xfffff000; } break; case PCI_CARDBUS_CIS: - conf_data = PCI_CARDBUS_CIS_POINTER; - break; - case PCI_SUBSYSTEM_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_EHCI_SUB_ID, CS5536_SUB_VENDOR_ID); + cfg = PCI_CARDBUS_CIS_POINTER; break; case PCI_ROM_ADDRESS: - conf_data = PCI_EXPANSION_ROM_BAR; + cfg = PCI_EXPANSION_ROM_BAR; break; case PCI_CAPABILITY_LIST: - conf_data = PCI_CAPLIST_USB_POINTER; + cfg = PCI_CAPLIST_USB_POINTER; break; case PCI_INTERRUPT_LINE: - conf_data = - CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR); + cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR); break; case PCI_EHCI_LEGSMIEN_REG: _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo); - conf_data = (hi & 0x003f0000) >> 16; + cfg = (hi & 0x003f0000) >> 16; break; case PCI_EHCI_LEGSMISTS_REG: _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo); - conf_data = (hi & 0x3f000000) >> 24; + cfg = (hi & 0x3f000000) >> 24; break; case PCI_EHCI_FLADJ_REG: _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo); - conf_data = hi & 0x00003f00; + cfg = hi & 0x00003f00; break; default: break; } - return conf_data; + return cfg; } diff --git a/arch/mips/loongson/common/cs5536/cs5536_ide.c b/arch/mips/loongson/common/cs5536/cs5536_ide.c index 681d129..32ad4ee 100644 --- a/arch/mips/loongson/common/cs5536/cs5536_ide.c +++ b/arch/mips/loongson/common/cs5536/cs5536_ide.c @@ -18,7 +18,7 @@ void pci_ide_write_reg(int reg, u32 value) { - u32 hi = 0, lo = value; + u32 hi, lo; switch (reg) { case PCI_COMMAND: @@ -72,26 +72,16 @@ void pci_ide_write_reg(int reg, u32 value) _wrmsr(IDE_MSR_REG(IDE_CFG), hi, lo); } break; - case PCI_IDE_DTC_REG: - _rdmsr(IDE_MSR_REG(IDE_DTC), &hi, &lo); - lo = value; - _wrmsr(IDE_MSR_REG(IDE_DTC), hi, lo); - break; - case PCI_IDE_CAST_REG: - _rdmsr(IDE_MSR_REG(IDE_CAST), &hi, &lo); - lo = value; - _wrmsr(IDE_MSR_REG(IDE_CAST), hi, lo); - break; - case PCI_IDE_ETC_REG: - _rdmsr(IDE_MSR_REG(IDE_ETC), &hi, &lo); - lo = value; - _wrmsr(IDE_MSR_REG(IDE_ETC), hi, lo); - break; - case PCI_IDE_PM_REG: - _rdmsr(IDE_MSR_REG(IDE_INTERNAL_PM), &hi, &lo); - lo = value; - _wrmsr(IDE_MSR_REG(IDE_INTERNAL_PM), hi, lo); +#define SET_PCI_IDE_REG(r) \ + case PCI_IDE_##r##_REG: \ + _rdmsr(IDE_MSR_REG(IDE_##r), &hi, &lo); \ + lo = value; \ + _wrmsr(IDE_MSR_REG(IDE_##r), hi, lo); \ break; + SET_PCI_IDE_REG(DTC) + SET_PCI_IDE_REG(CAST) + SET_PCI_IDE_REG(ETC) + SET_PCI_IDE_REG(PM) default: break; } @@ -99,94 +89,79 @@ void pci_ide_write_reg(int reg, u32 value) u32 pci_ide_read_reg(int reg) { - u32 conf_data = 0; + u32 cfg = 0; u32 hi, lo; switch (reg) { case PCI_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_IDE_DEVICE_ID, CS5536_VENDOR_ID); + case PCI_SUBSYSTEM_VENDOR_ID: + cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_IDE, + PCI_VENDOR_ID_AMD); break; case PCI_COMMAND: _rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo); if (lo & 0xfffffff0) - conf_data |= PCI_COMMAND_IO; + cfg |= PCI_COMMAND_IO; _rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo); if ((lo & 0x30) == 0x30) - conf_data |= PCI_COMMAND_MASTER; + cfg |= PCI_COMMAND_MASTER; break; case PCI_STATUS: - conf_data |= PCI_STATUS_66MHZ; - conf_data |= PCI_STATUS_FAST_BACK; + cfg |= PCI_STATUS_66MHZ; + cfg |= PCI_STATUS_FAST_BACK; _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo); if (lo & SB_PARE_ERR_FLAG) - conf_data |= PCI_STATUS_PARITY; - conf_data |= PCI_STATUS_DEVSEL_MEDIUM; + cfg |= PCI_STATUS_PARITY; + cfg |= PCI_STATUS_DEVSEL_MEDIUM; break; case PCI_CLASS_REVISION: _rdmsr(IDE_MSR_REG(IDE_CAP), &hi, &lo); - conf_data = lo & 0x000000ff; - conf_data |= (CS5536_IDE_CLASS_CODE << 8); + cfg = lo & 0x000000ff; + cfg |= (CS5536_IDE_CLASS_CODE << 8); break; case PCI_CACHE_LINE_SIZE: _rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo); hi &= 0x000000f8; - conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, hi); + cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, hi); break; case PCI_BAR4_REG: _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo); if (lo & SOFT_BAR_IDE_FLAG) { - conf_data = CS5536_IDE_RANGE | + cfg = CS5536_IDE_RANGE | PCI_BASE_ADDRESS_SPACE_IO; lo &= ~SOFT_BAR_IDE_FLAG; _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo); } else { _rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo); - conf_data = lo & 0xfffffff0; - conf_data |= 0x01; - conf_data &= ~0x02; + cfg = lo & 0xfffffff0; + cfg |= 0x01; + cfg &= ~0x02; } break; case PCI_CARDBUS_CIS: - conf_data = PCI_CARDBUS_CIS_POINTER; - break; - case PCI_SUBSYSTEM_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_IDE_SUB_ID, CS5536_SUB_VENDOR_ID); + cfg = PCI_CARDBUS_CIS_POINTER; break; case PCI_ROM_ADDRESS: - conf_data = PCI_EXPANSION_ROM_BAR; + cfg = PCI_EXPANSION_ROM_BAR; break; case PCI_CAPABILITY_LIST: - conf_data = PCI_CAPLIST_POINTER; + cfg = PCI_CAPLIST_POINTER; break; case PCI_INTERRUPT_LINE: - conf_data = - CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_IDE_INTR); - break; - case PCI_IDE_CFG_REG: - _rdmsr(IDE_MSR_REG(IDE_CFG), &hi, &lo); - conf_data = lo; - break; - case PCI_IDE_DTC_REG: - _rdmsr(IDE_MSR_REG(IDE_DTC), &hi, &lo); - conf_data = lo; - break; - case PCI_IDE_CAST_REG: - _rdmsr(IDE_MSR_REG(IDE_CAST), &hi, &lo); - conf_data = lo; - break; - case PCI_IDE_ETC_REG: - _rdmsr(IDE_MSR_REG(IDE_ETC), &hi, &lo); - conf_data = lo; + cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_IDE_INTR); break; - case PCI_IDE_PM_REG: - _rdmsr(IDE_MSR_REG(IDE_INTERNAL_PM), &hi, &lo); - conf_data = lo; +#define GET_PCI_IDE_REG(r) \ + case PCI_IDE_##r##_REG: \ + _rdmsr(IDE_MSR_REG(IDE_##r), &hi, &cfg); \ break; + GET_PCI_IDE_REG(CFG) + GET_PCI_IDE_REG(DTC) + GET_PCI_IDE_REG(CAST) + GET_PCI_IDE_REG(ETC) + GET_PCI_IDE_REG(PM) default: break; } - return conf_data; + return cfg; } diff --git a/arch/mips/loongson/common/cs5536/cs5536_isa.c b/arch/mips/loongson/common/cs5536/cs5536_isa.c index 4d9f65a..3469f64 100644 --- a/arch/mips/loongson/common/cs5536/cs5536_isa.c +++ b/arch/mips/loongson/common/cs5536/cs5536_isa.c @@ -86,7 +86,7 @@ static void divil_lbar_disable(void) void pci_isa_write_bar(int n, u32 value) { - u32 hi = 0, lo = value; + u32 hi, lo; if (value == PCI_BAR_RANGE_MASK) { _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo); @@ -95,7 +95,7 @@ void pci_isa_write_bar(int n, u32 value) } else if (value & 0x01) { /* NATIVE reg */ hi = 0x0000f001; - lo &= bar_space_range[n]; + lo = value & bar_space_range[n]; _wrmsr(divil_msr_reg[n], hi, lo); /* RCONFx is 4bytes in units for I/O space */ @@ -112,21 +112,21 @@ void pci_isa_write_bar(int n, u32 value) u32 pci_isa_read_bar(int n) { - u32 conf_data = 0; + u32 cfg = 0; u32 hi, lo; _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo); if (lo & soft_bar_flag[n]) { - conf_data = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO; + cfg = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO; lo &= ~soft_bar_flag[n]; _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo); } else { _rdmsr(divil_msr_reg[n], &hi, &lo); - conf_data = lo & bar_space_range[n]; - conf_data |= 0x01; - conf_data &= ~0x02; + cfg = lo & bar_space_range[n]; + cfg |= 0x01; + cfg &= ~0x02; } - return conf_data; + return cfg; } /* @@ -136,7 +136,7 @@ u32 pci_isa_read_bar(int n) */ void pci_isa_write_reg(int reg, u32 value) { - u32 hi = 0, lo = value; + u32 hi, lo; u32 temp; switch (reg) { @@ -230,45 +230,46 @@ void pci_isa_write_reg(int reg, u32 value) */ u32 pci_isa_read_reg(int reg) { - u32 conf_data = 0; + u32 cfg = 0; u32 hi, lo; switch (reg) { case PCI_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_ISA_DEVICE_ID, CS5536_VENDOR_ID); + case PCI_SUBSYSTEM_VENDOR_ID: + cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_ISA, + PCI_VENDOR_ID_AMD); break; case PCI_COMMAND: /* we just check the first LBAR for the IO enable bit, */ /* maybe we should changed later. */ _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_SMB), &hi, &lo); if (hi & 0x01) - conf_data |= PCI_COMMAND_IO; + cfg |= PCI_COMMAND_IO; break; case PCI_STATUS: - conf_data |= PCI_STATUS_66MHZ; - conf_data |= PCI_STATUS_DEVSEL_MEDIUM; - conf_data |= PCI_STATUS_FAST_BACK; + cfg |= PCI_STATUS_66MHZ; + cfg |= PCI_STATUS_DEVSEL_MEDIUM; + cfg |= PCI_STATUS_FAST_BACK; _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo); if (lo & SB_TAS_ERR_FLAG) - conf_data |= PCI_STATUS_SIG_TARGET_ABORT; + cfg |= PCI_STATUS_SIG_TARGET_ABORT; if (lo & SB_TAR_ERR_FLAG) - conf_data |= PCI_STATUS_REC_TARGET_ABORT; + cfg |= PCI_STATUS_REC_TARGET_ABORT; if (lo & SB_MAR_ERR_FLAG) - conf_data |= PCI_STATUS_REC_MASTER_ABORT; + cfg |= PCI_STATUS_REC_MASTER_ABORT; if (lo & SB_PARE_ERR_FLAG) - conf_data |= PCI_STATUS_DETECTED_PARITY; + cfg |= PCI_STATUS_DETECTED_PARITY; break; case PCI_CLASS_REVISION: _rdmsr(GLCP_MSR_REG(GLCP_CHIP_REV_ID), &hi, &lo); - conf_data = lo & 0x000000ff; - conf_data |= (CS5536_ISA_CLASS_CODE << 8); + cfg = lo & 0x000000ff; + cfg |= (CS5536_ISA_CLASS_CODE << 8); break; case PCI_CACHE_LINE_SIZE: _rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo); hi &= 0x000000f8; - conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi); + cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi); break; /* * we only use the LBAR of DIVIL, no RCONF used. @@ -292,25 +293,21 @@ u32 pci_isa_read_reg(int reg) return pci_isa_read_bar(5); break; case PCI_CARDBUS_CIS: - conf_data = PCI_CARDBUS_CIS_POINTER; - break; - case PCI_SUBSYSTEM_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_ISA_SUB_ID, CS5536_SUB_VENDOR_ID); + cfg = PCI_CARDBUS_CIS_POINTER; break; case PCI_ROM_ADDRESS: - conf_data = PCI_EXPANSION_ROM_BAR; + cfg = PCI_EXPANSION_ROM_BAR; break; case PCI_CAPABILITY_LIST: - conf_data = PCI_CAPLIST_POINTER; + cfg = PCI_CAPLIST_POINTER; break; case PCI_INTERRUPT_LINE: /* no interrupt used here */ - conf_data = CFG_PCI_INTERRUPT_LINE(0x00, 0x00); + cfg = CFG_PCI_INTERRUPT_LINE(0x00, 0x00); break; default: break; } - return conf_data; + return cfg; } diff --git a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c index 0cb1b97..c949dd9 100644 --- a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c +++ b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c @@ -7,6 +7,9 @@ * Copyright (C) 2009 Lemote Inc. * Author: Wu zhangjin, wuzhangjin@gmail.com * + * Copyright (C) 2010 Lemote Inc. + * Author: Gang Liang, randomizedthinking@gmail.com + * * Reference: AMD Geode(TM) CS5536 Companion Device Data Book * * This program is free software; you can redistribute it and/or modify it @@ -15,11 +18,24 @@ * option) any later version. */ +/* + * The MFGPT base address is variable, i.e., it could change over time. In + * reality, it only changes once when setting up the PCI memory mapping (occurs + * about 0.2 second from boot). But because of this, we have to read in the + * mfgpt base address repeatly in the beginning of various routines, most + * noticeably, mfgpt1_read_cycle (for sched_clock), and mfgpt1_interrupt. + * + * The source of problem is that PMON and the current cs5536 set up pci + * register window differently (to be further confirmed). Can we set + * them the same so as to save the trouble here? + * + * Now an ugly hack is used to save a few CPU cycles... likely an + * over-optimization. Feel free to remove it. + */ + #include #include #include -#include -#include #include #include @@ -27,108 +43,143 @@ #include -DEFINE_SPINLOCK(mfgpt_lock); -EXPORT_SYMBOL(mfgpt_lock); +static void mfgpt0_set_mode(enum clock_event_mode, struct clock_event_device*); +static int mfgpt0_next_event(unsigned long, struct clock_event_device*); +static irqreturn_t mfgpt0_interrupt(int irq, void *dev_id); +static void mfgpt0_start_timer(u16 delta); + +static cycle_t mfgpt1_read_cycle(struct clocksource *cs); +static enum clock_event_mode mfgpt0_mode = CLOCK_EVT_MODE_SHUTDOWN; static u32 mfgpt_base; -/* - * Initialize the MFGPT timer. - * - * This is also called after resume to bring the MFGPT into operation again. - */ +static struct clock_event_device mfgpt0_clockevent = { + .name = "mfgpt0", + .features = CLOCK_EVT_MODE_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .set_mode = mfgpt0_set_mode, + .set_next_event = mfgpt0_next_event, + .rating = 220, + .irq = CS5536_MFGPT_INTR, +}; + +static struct irqaction irq5 = { + .handler = mfgpt0_interrupt, + .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER, + .name = "mfgpt0-timer" +}; -/* disable counter */ -void disable_mfgpt0_counter(void) +static struct clocksource mfgpt1_clocksource = { + .name = "mfgpt1", + .rating = 210, + .read = mfgpt1_read_cycle, + .mask = CLOCKSOURCE_MASK(16), + .flags = CLOCK_SOURCE_IS_CONTINUOUS +}; + +static inline void enable_mfgpt0_counter(void) { - outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP); + u32 basehi; + _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base); + + /* clockevent: 14M, divisor = 8 (scale=3), CMP2 event mode */ + outw(MFGPT_SETUP_ACK | MFGPT_SETUP_CMP2EVT | + MFGPT_SETUP_CLOCK(1) | MFGPT_SETUP_SCALE(3), MFGPT0_SETUP); + outw(0, MFGPT0_CNT); + outw(MFGPT_COMPARE(1, 3), MFGPT0_CMP2); + outw(0xFFFF, MFGPT0_SETUP); } -EXPORT_SYMBOL(disable_mfgpt0_counter); -/* enable counter, comparator2 to event mode, 14.318MHz clock */ -void enable_mfgpt0_counter(void) +static inline void enable_mfgpt1_counter(void) { - outw(0xe310, MFGPT0_SETUP); + u32 basehi; + _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base); + + /* clocksource: 32K w/ divisor = 2 (scale=1) */ + outw(MFGPT_SETUP_ACK | MFGPT_SETUP_CLOCK(0) | + MFGPT_SETUP_SCALE(1), MFGPT1_SETUP); + + outw(0, MFGPT1_CNT); + outw(0xFFFF, MFGPT1_CMP2); /* CNT won't tick with no CMP set */ + outw(0xFFFF, MFGPT1_SETUP); } -EXPORT_SYMBOL(enable_mfgpt0_counter); -static void init_mfgpt_timer(enum clock_event_mode mode, - struct clock_event_device *evt) +void enable_mfgpt_counter(void) { - spin_lock(&mfgpt_lock); - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - outw(COMPARE, MFGPT0_CMP2); /* set comparator2 */ - outw(0, MFGPT0_CNT); /* set counter to 0 */ - enable_mfgpt0_counter(); - break; - - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_UNUSED: - if (evt->mode == CLOCK_EVT_MODE_PERIODIC || - evt->mode == CLOCK_EVT_MODE_ONESHOT) - disable_mfgpt0_counter(); - break; - - case CLOCK_EVT_MODE_ONESHOT: - /* The oneshot mode have very high deviation, Not use it! */ - break; - - case CLOCK_EVT_MODE_RESUME: - /* Nothing to do here */ - break; - } - spin_unlock(&mfgpt_lock); + /* TODO: add a mfgpt system hard reset here + * timers might not reset correctly when OS crashes + */ + + enable_mfgpt0_counter(); + enable_mfgpt1_counter(); } +EXPORT_SYMBOL(enable_mfgpt_counter); -static struct clock_event_device mfgpt_clockevent = { - .name = "mfgpt", - .features = CLOCK_EVT_FEAT_PERIODIC, - .set_mode = init_mfgpt_timer, - .irq = CS5536_MFGPT_INTR, -}; +void disable_mfgpt_counter(void) +{ + outw(0x7FFF, MFGPT0_SETUP); + outw(0x7FFF, MFGPT1_SETUP); +} +EXPORT_SYMBOL(disable_mfgpt_counter); -static irqreturn_t timer_interrupt(int irq, void *dev_id) +static void mfgpt0_start_timer(u16 delta) { - u32 basehi; + outw(0x7FFF, MFGPT0_SETUP); + outw(0, MFGPT0_CNT); + outw(delta, MFGPT0_CMP2); + outw(0xFFFF, MFGPT0_SETUP); +} - /* - * get MFGPT base address - * - * NOTE: do not remove me, it's need for the value of mfgpt_base is - * variable - */ +static void mfgpt0_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + outw(0x7FFF, MFGPT0_SETUP); + if (mode == CLOCK_EVT_MODE_PERIODIC) + mfgpt0_start_timer(MFGPT_COMPARE(1, 3)); + + mfgpt0_mode = mode; +} + +static int mfgpt0_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + mfgpt0_start_timer(delta); + return 0; +} + +static irqreturn_t mfgpt0_interrupt(int irq, void *dev_id) +{ + u32 basehi; _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base); - /* ack */ - outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP); + /* stop the timer and ack the interrupt */ + outw(0x7FFF, MFGPT0_SETUP); - mfgpt_clockevent.event_handler(&mfgpt_clockevent); + if (mfgpt0_mode == CLOCK_EVT_MODE_SHUTDOWN) + return IRQ_HANDLED; + /* restart timer for periodic mode */ + if (mfgpt0_mode == CLOCK_EVT_MODE_PERIODIC) + outw(0xFFFF, MFGPT0_SETUP); + + mfgpt0_clockevent.event_handler(&mfgpt0_clockevent); return IRQ_HANDLED; } -static struct irqaction irq5 = { - .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER, - .name = "timer" -}; - /* * Initialize the conversion factor and the min/max deltas of the clock event * structure and register the clock event source with the framework. */ void __init setup_mfgpt0_timer(void) { - u32 basehi; - struct clock_event_device *cd = &mfgpt_clockevent; + struct clock_event_device *cd = &mfgpt0_clockevent; unsigned int cpu = smp_processor_id(); - cd->cpumask = cpumask_of(cpu); - clockevent_set_clock(cd, MFGPT_TICK_RATE); - cd->max_delta_ns = clockevent_delta2ns(0xffff, cd); - cd->min_delta_ns = clockevent_delta2ns(0xf, cd); + + cd->shift = 22; + cd->mult = div_sc(MFGPT_TICK_RATE(1, 3), NSEC_PER_SEC, cd->shift); + + cd->min_delta_ns = clockevent_delta2ns(0xF, cd); + cd->max_delta_ns = clockevent_delta2ns(0xFFFF, cd); /* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */ _wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100); @@ -136,79 +187,24 @@ void __init setup_mfgpt0_timer(void) /* Enable Interrupt Gate 5 */ _wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000); - /* get MFGPT base address */ - _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base); - + enable_mfgpt0_counter(); clockevents_register_device(cd); - setup_irq(CS5536_MFGPT_INTR, &irq5); } -/* - * Since the MFGPT overflows every tick, its not very useful - * to just read by itself. So use jiffies to emulate a free - * running counter: - */ -static cycle_t mfgpt_read(struct clocksource *cs) +static cycle_t mfgpt1_read_cycle(struct clocksource *cs) { - unsigned long flags; - int count; - u32 jifs; - static int old_count; - static u32 old_jifs; - - spin_lock_irqsave(&mfgpt_lock, flags); - /* - * Although our caller may have the read side of xtime_lock, - * this is now a seqlock, and we are cheating in this routine - * by having side effects on state that we cannot undo if - * there is a collision on the seqlock and our caller has to - * retry. (Namely, old_jifs and old_count.) So we must treat - * jiffies as volatile despite the lock. We read jiffies - * before latching the timer count to guarantee that although - * the jiffies value might be older than the count (that is, - * the counter may underflow between the last point where - * jiffies was incremented and the point where we latch the - * count), it cannot be newer. - */ - jifs = jiffies; - /* read the count */ - count = inw(MFGPT0_CNT); - - /* - * It's possible for count to appear to go the wrong way for this - * reason: - * - * The timer counter underflows, but we haven't handled the resulting - * interrupt and incremented jiffies yet. - * - * Previous attempts to handle these cases intelligently were buggy, so - * we just do the simple thing now. - */ - if (count < old_count && jifs == old_jifs) - count = old_count; - - old_count = count; - old_jifs = jifs; - - spin_unlock_irqrestore(&mfgpt_lock, flags); - - return (cycle_t) (jifs * COMPARE) + count; + return inw(MFGPT1_CNT); } -static struct clocksource clocksource_mfgpt = { - .name = "mfgpt", - .rating = 120, /* Functional for real use, but not desired */ - .read = mfgpt_read, - .mask = CLOCKSOURCE_MASK(32), -}; - -int __init init_mfgpt_clocksource(void) +int __init init_mfgpt1_clocksource(void) { if (num_possible_cpus() > 1) /* MFGPT does not scale! */ return 0; - return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE); + enable_mfgpt1_counter(); + + return clocksource_register_hz(&mfgpt1_clocksource, MFGPT_TICK_RATE(0, 1)); } -arch_initcall(init_mfgpt_clocksource); +arch_initcall(init_mfgpt1_clocksource); diff --git a/arch/mips/loongson/common/cs5536/cs5536_ohci.c b/arch/mips/loongson/common/cs5536/cs5536_ohci.c index bdedf51..bdb43bb 100644 --- a/arch/mips/loongson/common/cs5536/cs5536_ohci.c +++ b/arch/mips/loongson/common/cs5536/cs5536_ohci.c @@ -18,7 +18,7 @@ void pci_ohci_write_reg(int reg, u32 value) { - u32 hi = 0, lo = value; + u32 hi, lo; switch (reg) { case PCI_COMMAND: @@ -73,77 +73,72 @@ void pci_ohci_write_reg(int reg, u32 value) u32 pci_ohci_read_reg(int reg) { - u32 conf_data = 0; + u32 cfg = 0; u32 hi, lo; switch (reg) { case PCI_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_OHCI_DEVICE_ID, CS5536_VENDOR_ID); + case PCI_SUBSYSTEM_VENDOR_ID: + cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_OHC, + PCI_VENDOR_ID_AMD); break; case PCI_COMMAND: _rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo); if (hi & PCI_COMMAND_MASTER) - conf_data |= PCI_COMMAND_MASTER; + cfg |= PCI_COMMAND_MASTER; if (hi & PCI_COMMAND_MEMORY) - conf_data |= PCI_COMMAND_MEMORY; + cfg |= PCI_COMMAND_MEMORY; break; case PCI_STATUS: - conf_data |= PCI_STATUS_66MHZ; - conf_data |= PCI_STATUS_FAST_BACK; + cfg |= PCI_STATUS_66MHZ; + cfg |= PCI_STATUS_FAST_BACK; _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo); if (lo & SB_PARE_ERR_FLAG) - conf_data |= PCI_STATUS_PARITY; - conf_data |= PCI_STATUS_DEVSEL_MEDIUM; + cfg |= PCI_STATUS_PARITY; + cfg |= PCI_STATUS_DEVSEL_MEDIUM; break; case PCI_CLASS_REVISION: _rdmsr(USB_MSR_REG(USB_CAP), &hi, &lo); - conf_data = lo & 0x000000ff; - conf_data |= (CS5536_OHCI_CLASS_CODE << 8); + cfg = lo & 0x000000ff; + cfg |= (CS5536_OHCI_CLASS_CODE << 8); break; case PCI_CACHE_LINE_SIZE: - conf_data = - CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, - PCI_NORMAL_LATENCY_TIMER); + cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, + PCI_NORMAL_LATENCY_TIMER); break; case PCI_BAR0_REG: _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo); if (lo & SOFT_BAR_OHCI_FLAG) { - conf_data = CS5536_OHCI_RANGE | + cfg = CS5536_OHCI_RANGE | PCI_BASE_ADDRESS_SPACE_MEMORY; lo &= ~SOFT_BAR_OHCI_FLAG; _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo); } else { _rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo); - conf_data = lo & 0xffffff00; - conf_data &= ~0x0000000f; /* 32bit mem */ + cfg = lo & 0xffffff00; + cfg &= ~0x0000000f; /* 32bit mem */ } break; case PCI_CARDBUS_CIS: - conf_data = PCI_CARDBUS_CIS_POINTER; - break; - case PCI_SUBSYSTEM_VENDOR_ID: - conf_data = - CFG_PCI_VENDOR_ID(CS5536_OHCI_SUB_ID, CS5536_SUB_VENDOR_ID); + cfg = PCI_CARDBUS_CIS_POINTER; break; case PCI_ROM_ADDRESS: - conf_data = PCI_EXPANSION_ROM_BAR; + cfg = PCI_EXPANSION_ROM_BAR; break; case PCI_CAPABILITY_LIST: - conf_data = PCI_CAPLIST_USB_POINTER; + cfg = PCI_CAPLIST_USB_POINTER; break; case PCI_INTERRUPT_LINE: - conf_data = - CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR); + cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR); break; case PCI_OHCI_INT_REG: _rdmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), &hi, &lo); if ((lo & 0x00000f00) == CS5536_USB_INTR) - conf_data = 1; + cfg = 1; break; default: break; } - return conf_data; + return cfg; } diff --git a/arch/mips/loongson/common/early_printk.c b/arch/mips/loongson/common/early_printk.c index a71736f..649b270 100644 --- a/arch/mips/loongson/common/early_printk.c +++ b/arch/mips/loongson/common/early_printk.c @@ -10,9 +10,13 @@ * option) any later version. */ #include +#include +#include #include +unsigned long _loongson_uart_base; + #define PORT(base, offset) (u8 *)(base + offset) static inline unsigned int serial_in(unsigned char *base, int offset) @@ -39,3 +43,29 @@ void prom_putchar(char c) serial_out(uart_base, UART_TX, c); } + +void __init prom_init_uart_base(void) +{ + unsigned long loongson_uart_base; + + switch (mips_machtype) { + case MACH_LEMOTE_FL2E: + loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8; + break; + case MACH_LEMOTE_FL2F: + case MACH_LEMOTE_LL2F: + loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8; + break; + case MACH_LEMOTE_ML2F7: + case MACH_LEMOTE_YL2F89: + case MACH_DEXXON_GDIUM2F10: + case MACH_LEMOTE_NAS: + default: + /* The CPU provided serial port */ + loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8; + break; + } + + _loongson_uart_base = + (unsigned long)ioremap_nocache(loongson_uart_base, 8); +} diff --git a/arch/mips/loongson/common/env.c b/arch/mips/loongson/common/env.c index d93830a..d532c83 100644 --- a/arch/mips/loongson/common/env.c +++ b/arch/mips/loongson/common/env.c @@ -40,7 +40,6 @@ void __init prom_init_env(void) /* pmon passes arguments in 32bit pointers */ int *_prom_envp; unsigned long bus_clock; - unsigned int processor_id; long l; /* firmware arguments are initialized in head.S */ @@ -60,8 +59,7 @@ void __init prom_init_env(void) if (bus_clock == 0) bus_clock = 66000000; if (cpu_clock_freq == 0) { - processor_id = (¤t_cpu_data)->processor_id; - switch (processor_id & PRID_REV_MASK) { + switch (cpu_prid_rev()) { case PRID_REV_LOONGSON2E: cpu_clock_freq = 533080000; break; diff --git a/arch/mips/loongson/common/gpio.c b/arch/mips/loongson/common/gpio.c index e8a0ffa..a8645f8 100644 --- a/arch/mips/loongson/common/gpio.c +++ b/arch/mips/loongson/common/gpio.c @@ -19,7 +19,6 @@ #include #include -#define STLS2F_N_GPIO 4 #define STLS2F_GPIO_IN_OFFSET 16 static DEFINE_SPINLOCK(gpio_lock); @@ -29,7 +28,7 @@ int gpio_get_value(unsigned gpio) u32 val; u32 mask; - if (gpio >= STLS2F_N_GPIO) + if (gpio >= ARCH_NR_GPIOS) return __gpio_get_value(gpio); mask = 1 << (gpio + STLS2F_GPIO_IN_OFFSET); @@ -46,7 +45,7 @@ void gpio_set_value(unsigned gpio, int state) u32 val; u32 mask; - if (gpio >= STLS2F_N_GPIO) { + if (gpio >= ARCH_NR_GPIOS) { __gpio_set_value(gpio, state); return ; } @@ -66,7 +65,7 @@ EXPORT_SYMBOL(gpio_set_value); int gpio_cansleep(unsigned gpio) { - if (gpio < STLS2F_N_GPIO) + if (gpio < ARCH_NR_GPIOS) return 0; else return __gpio_cansleep(gpio); @@ -78,7 +77,7 @@ static int ls2f_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) u32 temp; u32 mask; - if (gpio >= STLS2F_N_GPIO) + if (gpio >= ARCH_NR_GPIOS) return -EINVAL; spin_lock(&gpio_lock); @@ -97,7 +96,7 @@ static int ls2f_gpio_direction_output(struct gpio_chip *chip, u32 temp; u32 mask; - if (gpio >= STLS2F_N_GPIO) + if (gpio >= ARCH_NR_GPIOS) return -EINVAL; gpio_set_value(gpio, level); @@ -129,7 +128,7 @@ static struct gpio_chip ls2f_chip = { .direction_output = ls2f_gpio_direction_output, .set = ls2f_gpio_set_value, .base = 0, - .ngpio = STLS2F_N_GPIO, + .ngpio = ARCH_NR_GPIOS, }; static int __init ls2f_gpio_setup(void) diff --git a/arch/mips/loongson/common/init.c b/arch/mips/loongson/common/init.c index 19d3415..73f4460 100644 --- a/arch/mips/loongson/common/init.c +++ b/arch/mips/loongson/common/init.c @@ -30,8 +30,10 @@ void __init prom_init(void) prom_init_env(); prom_init_memory(); +#ifdef CONFIG_EARLY_PRINTK /*init the uart base address */ prom_init_uart_base(); +#endif } void __init prom_free_prom_memory(void) diff --git a/arch/mips/loongson/common/irq.c b/arch/mips/loongson/common/irq.c index 5897471..30396c8 100644 --- a/arch/mips/loongson/common/irq.c +++ b/arch/mips/loongson/common/irq.c @@ -10,6 +10,10 @@ #include #include +#include +#include +#include + #include /* * the first level int-handler will jump here if it is a bonito irq @@ -48,20 +52,32 @@ asmlinkage void plat_irq_dispatch(void) void __init arch_init_irq(void) { /* - * Clear all of the interrupts while we change the able around a bit. - * int-handler is not on bootstrap + * The vector addresses of the generic exceptions are in the cached + * address space. */ - clear_c0_status(ST0_IM | ST0_BEV); + clear_c0_status(ST0_BEV); - /* no steer */ + /* No steer */ LOONGSON_INTSTEER = 0; /* - * Mask out all interrupt by writing "1" to all bit position in - * the interrupt reset reg. + * Clear all interrupts */ LOONGSON_INTENCLR = ~0; + /* + * Sets the first-level interrupt dispatcher: + * + * 0-15: i8259 interrupt (If CONFIG_I8259 selected) + * 16-23: mips cpu interrupt + * 32-63: bonito irq + */ + mips_cpu_irq_init(); + bonito_irq_init(); +#ifdef CONFIG_I8259 + init_i8259_irqs(); +#endif + /* machine specific irq init */ mach_init_irq(); } diff --git a/arch/mips/loongson/common/mem.c b/arch/mips/loongson/common/mem.c index 30eba60..e90f2de 100644 --- a/arch/mips/loongson/common/mem.c +++ b/arch/mips/loongson/common/mem.c @@ -14,39 +14,24 @@ #include #include +#define MB(x) ((x) << 20) + void __init prom_init_memory(void) { - add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM); - - add_memory_region(memsize << 20, LOONGSON_PCI_MEM_START - (memsize << - 20), BOOT_MEM_RESERVED); - + add_memory_region(0x0, MB(memsize), BOOT_MEM_RAM); + add_memory_region(MB(memsize), LOONGSON_PCI_MEM_START - MB(memsize), BOOT_MEM_RESERVED); #ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG - { - int bit; - - bit = fls(memsize + highmemsize); - if (bit != ffs(memsize + highmemsize)) - bit += 20; - else - bit = bit + 20 - 1; - - /* set cpu window3 to map CPU to DDR: 2G -> 2G */ - LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul, - 0x80000000ul, (1 << bit)); - mmiowb(); - } -#endif /* !CONFIG_CPU_SUPPORTS_ADDRWINCFG */ + /* set cpu window3 to map CPU to DDR: 2G -> 0G */ + LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul, 0, MB(memsize + highmemsize)); + mmiowb(); +#endif #ifdef CONFIG_64BIT if (highmemsize > 0) - add_memory_region(LOONGSON_HIGHMEM_START, - highmemsize << 20, BOOT_MEM_RAM); - + add_memory_region(LOONGSON_HIGHMEM_START, MB(highmemsize), BOOT_MEM_RAM); add_memory_region(LOONGSON_PCI_MEM_END + 1, LOONGSON_HIGHMEM_START - - LOONGSON_PCI_MEM_END - 1, BOOT_MEM_RESERVED); - -#endif /* !CONFIG_64BIT */ + LOONGSON_PCI_MEM_END - 1, BOOT_MEM_RESERVED); +#endif } /* override of arch/mips/mm/cache.c: __uncached_access */ diff --git a/arch/mips/loongson/common/mtd.c b/arch/mips/loongson/common/mtd.c new file mode 100644 index 0000000..49a57a7 --- /dev/null +++ b/arch/mips/loongson/common/mtd.c @@ -0,0 +1,91 @@ +/* + * Driver for flushing/dumping ROM of PMON on loongson family machines + * + * Copyright (C) 2008-2009 Lemote Inc. + * Author: Yan Hua + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define FLASH_PHYS_ADDR LOONGSON_BOOT_BASE +#define FLASH_SIZE 0x080000 + +#define FLASH_PARTITION0_ADDR 0x00000000 +#define FLASH_PARTITION0_SIZE 0x00080000 + +struct map_info flash_map = { + .name = "flash device", + .size = FLASH_SIZE, + .bankwidth = 1, +}; + +struct mtd_partition flash_parts[] = { + { + .name = "Bootloader", + .offset = FLASH_PARTITION0_ADDR, + .size = FLASH_PARTITION0_SIZE}, +}; + +#define PARTITION_COUNT ARRAY_SIZE(flash_parts) + +static struct mtd_info *mymtd; + +int __init init_flash(void) +{ + printk(KERN_NOTICE "flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + + flash_map.phys = FLASH_PHYS_ADDR; + flash_map.virt = ioremap(FLASH_PHYS_ADDR, FLASH_SIZE); + + if (!flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&flash_map); + + mymtd = do_map_probe("cfi_probe", &flash_map); + if (mymtd) { + add_mtd_partitions(mymtd, flash_parts, PARTITION_COUNT); + printk(KERN_NOTICE "pmon flash device initialized\n"); + return 0; + } + + iounmap((void *)flash_map.virt); + return -ENXIO; +} + +static void __exit cleanup_flash(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (flash_map.virt) { + iounmap((void *)flash_map.virt); + flash_map.virt = 0; + } +} + +module_init(init_flash); +module_exit(cleanup_flash); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yanhua "); +MODULE_DESCRIPTION("MTD driver for pmon flushing/dumping"); diff --git a/arch/mips/loongson/common/pci.c b/arch/mips/loongson/common/pci.c index 31d8c5e..5077456 100644 --- a/arch/mips/loongson/common/pci.c +++ b/arch/mips/loongson/common/pci.c @@ -50,11 +50,11 @@ static void __init setup_pcimap(void) LOONGSON_PCIMAP_WIN(0, 0); /* - * PCI-DMA to local mapping: [2G,2G+256M] -> [0M,256M] + * PCI-DMA to local mapping: [2G,4G] -> [0M,2G] */ LOONGSON_PCIBASE0 = 0x80000000ul; /* base: 2G -> mmap: 0M */ - /* size: 256M, burst transmission, pre-fetch enable, 64bit */ - LOONGSON_PCI_HIT0_SEL_L = 0xc000000cul; + /* size: 2G, burst transmission, pre-fetch enable, 64bit */ + LOONGSON_PCI_HIT0_SEL_L = 0x8000000cul; LOONGSON_PCI_HIT0_SEL_H = 0xfffffffful; LOONGSON_PCI_HIT1_SEL_L = 0x00000006ul; /* set this BAR as invalid */ LOONGSON_PCI_HIT1_SEL_H = 0x00000000ul; diff --git a/arch/mips/loongson/common/platform.c b/arch/mips/loongson/common/platform.c index ed007a2..4b97404 100644 --- a/arch/mips/loongson/common/platform.c +++ b/arch/mips/loongson/common/platform.c @@ -12,16 +12,14 @@ #include static struct platform_device loongson2_cpufreq_device = { - .name = "loongson2_cpufreq", + .name = "l2_cpufreq", .id = -1, }; static int __init loongson2_cpufreq_init(void) { - struct cpuinfo_mips *c = ¤t_cpu_data; - /* Only 2F revision and it's successors support CPUFreq */ - if ((c->processor_id & PRID_REV_MASK) >= PRID_REV_LOONGSON2F) + if (cpu_prid_rev() >= PRID_REV_LOONGSON2F) return platform_device_register(&loongson2_cpufreq_device); return -ENODEV; diff --git a/arch/mips/loongson/common/serial.c b/arch/mips/loongson/common/serial.c index 7580873..8f2ebf4 100644 --- a/arch/mips/loongson/common/serial.c +++ b/arch/mips/loongson/common/serial.c @@ -19,58 +19,44 @@ #include #include -#define PORT(int) \ +#define PORT(int, base_baud, io_type, port) \ { \ .irq = int, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ + .uartclk = base_baud, \ + .iotype = io_type, \ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \ .regshift = 0, \ + .iobase = port, \ } -#define PORT_M(int) \ -{ \ - .irq = MIPS_CPU_IRQ_BASE + (int), \ - .uartclk = 3686400, \ - .iotype = UPIO_MEM, \ - .membase = (void __iomem *)NULL, \ - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \ - .regshift = 0, \ -} - -static struct plat_serial8250_port uart8250_data[][2] = { - [MACH_LOONGSON_UNKNOWN] {}, - [MACH_LEMOTE_FL2E] {PORT(4), {} }, - [MACH_LEMOTE_FL2F] {PORT(3), {} }, - [MACH_LEMOTE_ML2F7] {PORT_M(3), {} }, - [MACH_LEMOTE_YL2F89] {PORT_M(3), {} }, - [MACH_DEXXON_GDIUM2F10] {PORT_M(3), {} }, - [MACH_LEMOTE_NAS] {PORT_M(3), {} }, - [MACH_LEMOTE_LL2F] {PORT(3), {} }, - [MACH_LOONGSON_END] {}, +static struct plat_serial8250_port uart8250_data[] = { + /* ttyS0: cpu_uart0 Yeeloong, Gdium, UNAS, ... */ + PORT((MIPS_CPU_IRQ_BASE + 3), 3686400, UPIO_MEM, 0x3f8), + /* ttyS1: sb_uart1 2E */ + PORT(4, 1843200, UPIO_PORT, 0x3f8), + /* ttyS2: sb_uart2 fuloong2f */ + PORT(3, 1843200, UPIO_PORT, 0x2f8), + {}, }; static struct platform_device uart8250_device = { .name = "serial8250", .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = uart8250_data, + }, }; static int __init serial_init(void) { - unsigned char iotype; - - iotype = uart8250_data[mips_machtype][0].iotype; - - if (UPIO_MEM == iotype) - uart8250_data[mips_machtype][0].membase = - (void __iomem *)_loongson_uart_base; - else if (UPIO_PORT == iotype) - uart8250_data[mips_machtype][0].iobase = - loongson_uart_base - LOONGSON_PCIIO_BASE; - - uart8250_device.dev.platform_data = uart8250_data[mips_machtype]; + uart8250_data[0].membase = (void __iomem *)ioremap_nocache( + LOONGSON_LIO1_BASE + uart8250_data[0].iobase, 8); - return platform_device_register(&uart8250_device); + platform_device_register(&uart8250_device); + return 0; } device_initcall(serial_init); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liu shiwei "); +MODULE_DESCRIPTION("loongson serial"); diff --git a/arch/mips/loongson/common/time.c b/arch/mips/loongson/common/time.c index 9fdd01f..b8cd030 100644 --- a/arch/mips/loongson/common/time.c +++ b/arch/mips/loongson/common/time.c @@ -10,6 +10,8 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ +#include + #include #include @@ -24,8 +26,81 @@ void __init plat_time_init(void) setup_mfgpt0_timer(); } +#ifdef CONFIG_LOONGSON_MC146818 void read_persistent_clock(struct timespec *ts) { ts->tv_sec = mc146818_get_cmos_time(); ts->tv_nsec = 0; } +#else + +/* If no CMOS RTC, use the one below */ + +/* + * Cloned from drivers/rtc/hctosys.c + * + * If CONFIG_RTC_HCTOSYS=y is enabled, the system time can be set from the + * hardware clock(when boot and resuming from suspend), this may be also done + * (duplicately) by the timekeeper, which may need to be avoided(TODO). + * + * read_persistent_clock() may be useful in some places, e.g. there is not + * peristent clock in the system, we can use this to recover the system time. + * + * Note: The device indicated by CONFIG_RTC_HCTOSYS_DEVICE must be the one + * created by the RTC driver. Use Gdium as an example, We must disable the + * rt_cmos driver If we want to use the rtc_m41t80 driver for + * CONFIG_RTC_HCTOSYS_DEVICE is configured as /dev/rtc0, if rtc_cmos is + * enabled, rtc_cmos driver will be used, but it is not supported by Gdium. + * So, for Gdium, please ensure "# CONFIG_RTC_DRV_CMOS is not set" + */ + +#ifdef CONFIG_RTC_HCTOSYS +void read_persistent_clock(struct timespec *ts) +{ + int err = -ENODEV; + struct rtc_time tm; + struct rtc_device *rtc; + + /* We can not access the RTC device before it is initialized ... */ + if (rtc_hctosys_ret != 0) { + ts->tv_sec = 0; + ts->tv_nsec = 0; + return; + } + + rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + + if (rtc == NULL) { + pr_err("%s: unable to open rtc device (%s)\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); + goto err_open; + } + + err = rtc_read_time(rtc, &tm); + if (err) { + dev_err(rtc->dev.parent, + "hctosys: unable to read the hardware clock\n"); + goto err_read; + + } + + err = rtc_valid_tm(&tm); + if (err) { + dev_err(rtc->dev.parent, + "hctosys: invalid date/time\n"); + goto err_invalid; + } + + ts->tv_nsec = NSEC_PER_SEC >> 1, + rtc_tm_to_time(&tm, &ts->tv_sec); + +err_invalid: +err_read: + rtc_class_close(rtc); + +err_open: + rtc_hctosys_ret = err; +} +#endif /* CONFIG_RTC_HCTOSYS */ + +#endif /* !CONFIG_LOONGSON_MC146818 */ diff --git a/arch/mips/loongson/common/uart_base.c b/arch/mips/loongson/common/uart_base.c deleted file mode 100644 index d69ea54..0000000 --- a/arch/mips/loongson/common/uart_base.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2009 Lemote Inc. - * Author: Wu Zhangjin, wuzhangjin@gmail.com - * - * 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 - -/* ioremapped */ -unsigned long _loongson_uart_base; -EXPORT_SYMBOL(_loongson_uart_base); -/* raw */ -unsigned long loongson_uart_base; -EXPORT_SYMBOL(loongson_uart_base); - -void prom_init_loongson_uart_base(void) -{ - switch (mips_machtype) { - case MACH_LEMOTE_FL2E: - loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8; - break; - case MACH_LEMOTE_FL2F: - case MACH_LEMOTE_LL2F: - loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8; - break; - case MACH_LEMOTE_ML2F7: - case MACH_LEMOTE_YL2F89: - case MACH_DEXXON_GDIUM2F10: - case MACH_LEMOTE_NAS: - default: - /* The CPU provided serial port */ - loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8; - break; - } - - _loongson_uart_base = - (unsigned long)ioremap_nocache(loongson_uart_base, 8); -} diff --git a/arch/mips/loongson/fuloong-2e/irq.c b/arch/mips/loongson/fuloong-2e/irq.c index 3cf1fef..560a065 100644 --- a/arch/mips/loongson/fuloong-2e/irq.c +++ b/arch/mips/loongson/fuloong-2e/irq.c @@ -9,7 +9,6 @@ */ #include -#include #include #include @@ -47,21 +46,10 @@ static struct irqaction cascade_irqaction = { void __init mach_init_irq(void) { - /* init all controller - * 0-15 ------> i8259 interrupt - * 16-23 ------> mips cpu interrupt - * 32-63 ------> bonito irq - */ - /* most bonito irq should be level triggered */ LOONGSON_INTEDGE = LOONGSON_ICU_SYSTEMERR | LOONGSON_ICU_MASTERERR | LOONGSON_ICU_RETRYERR | LOONGSON_ICU_MBOXES; - /* Sets the first-level interrupt dispatcher. */ - mips_cpu_irq_init(); - init_i8259_irqs(); - bonito_irq_init(); - /* bonito irq at IP2 */ setup_irq(MIPS_CPU_IRQ_BASE + 2, &cascade_irqaction); /* 8259 irq at IP5 */ diff --git a/arch/mips/loongson/gdium/Makefile b/arch/mips/loongson/gdium/Makefile new file mode 100644 index 0000000..f3f4f51 --- /dev/null +++ b/arch/mips/loongson/gdium/Makefile @@ -0,0 +1,6 @@ +# Makefile for gdium + +obj-y += irq.o reset.o platform.o + +obj-$(CONFIG_MFD_SM501) += sm501-pwm.o +obj-$(CONFIG_GDIUM_PWM_CLOCK) += gdium-clock.o diff --git a/arch/mips/loongson/gdium/gdium-clock.c b/arch/mips/loongson/gdium/gdium-clock.c new file mode 100644 index 0000000..fdbf42a --- /dev/null +++ b/arch/mips/loongson/gdium/gdium-clock.c @@ -0,0 +1,234 @@ +/* + * Doesn't work really well. When used, the clocksource is producing + * bad timings and the clockevent can't be used (don't have one shot feature + * thus can't switch on the fly and the pwm is initialised too late to be able + * to use it at boot time). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CLOCK_PWM 1 +#define CLOCK_PWM_FREQ 1500000 /* Freq in Hz */ +#define CLOCK_LATCH ((CLOCK_PWM_FREQ + HZ/2) / HZ) +#define CLOCK_PWM_PERIOD (1000000000/CLOCK_PWM_FREQ) /* period ns */ +#define CLOCK_PWM_DUTY 50 +#define CLOCK_PWM_IRQ (MIPS_CPU_IRQ_BASE + 4) + +static const char drv_name[] = "gdium-clock"; + +static struct pwm_device *clock_pwm; + +static DEFINE_SPINLOCK(clock_pwm_lock); +static uint64_t clock_tick; + +static irqreturn_t gdium_pwm_clock_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *cd = dev_id; + unsigned long flag; + + spin_lock_irqsave(&clock_pwm_lock, flag); + clock_tick++; + /* wait intn2 to finish */ + do { + LOONGSON_INTENCLR = (1 << 13); + } while (LOONGSON_INTISR & (1 << 13)); + spin_unlock_irqrestore(&clock_pwm_lock, flag); + + if (cd && cd->event_handler) + cd->event_handler(cd); + + return IRQ_HANDLED; +} + +static cycle_t gdium_pwm_clock_read(struct clocksource *cs) +{ + unsigned long flag; + uint32_t jifs; + uint64_t ticks; + + spin_lock_irqsave(&clock_pwm_lock, flag); + jifs = jiffies; + ticks = clock_tick; + spin_unlock_irqrestore(&clock_pwm_lock, flag); + /* return (cycle_t)ticks; */ + return (cycle_t)(CLOCK_LATCH * jifs); +} + +static struct clocksource gdium_pwm_clock_clocksource = { + .name = "gdium_csrc", + .read = gdium_pwm_clock_read, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY, + .shift = 20, +}; + +/* Debug fs */ +static int gdium_pwm_clock_show(struct seq_file *s, void *p) +{ + unsigned long flag; + uint64_t ticks; + + spin_lock_irqsave(&clock_pwm_lock, flag); + ticks = clock_tick; + spin_unlock_irqrestore(&clock_pwm_lock, flag); + seq_printf(s, "%lld\n", ticks); + return 0; +} + +static int gdium_pwm_clock_open(struct inode *inode, struct file *file) +{ + return single_open(file, gdium_pwm_clock_show, inode->i_private); +} + +static const struct file_operations gdium_pwm_clock_fops = { + .open = gdium_pwm_clock_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; +static struct dentry *debugfs_file; + +static void gdium_pwm_clock_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* Nothing to do ... */ +} + +static struct clock_event_device gdium_pwm_clock_cevt = { + .name = "gdium_cevt", + .features = CLOCK_EVT_FEAT_PERIODIC, + /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ + .rating = 299, + .irq = CLOCK_PWM_IRQ, + .set_mode = gdium_pwm_clock_set_mode, +}; + +static struct platform_device_id platform_device_ids[] = { + { + .name = "gdium-pwmclk", + }, + {} +}; +MODULE_DEVICE_TABLE(platform, platform_device_ids); + +static struct platform_driver gdium_pwm_clock_driver = { + .driver = { + .name = drv_name, + .owner = THIS_MODULE, + }, + .id_table = platform_device_ids, +}; + +static int gdium_pwm_clock_drvinit(void) +{ + int ret; + struct clocksource *cs = &gdium_pwm_clock_clocksource; + struct clock_event_device *cd = &gdium_pwm_clock_cevt; + unsigned int cpu = smp_processor_id(); + + clock_tick = 0; + + clock_pwm = pwm_request(CLOCK_PWM, drv_name); + if (clock_pwm == NULL) { + pr_err("unable to request PWM for Gdium clock\n"); + return -EBUSY; + } + ret = pwm_config(clock_pwm, CLOCK_PWM_DUTY, CLOCK_PWM_PERIOD); + if (ret) { + pr_err("unable to configure PWM for Gdium clock\n"); + goto err_pwm_request; + } + ret = pwm_enable(clock_pwm); + if (ret) { + pr_err("unable to enable PWM for Gdium clock\n"); + goto err_pwm_request; + } + + cd->cpumask = cpumask_of(cpu); + + cd->shift = 22; + cd->mult = div_sc(CLOCK_PWM_FREQ, NSEC_PER_SEC, cd->shift); + cd->max_delta_ns = clockevent_delta2ns(0x7FFF, cd); + cd->min_delta_ns = clockevent_delta2ns(0xF, cd); + clockevents_register_device(&gdium_pwm_clock_cevt); + + /* SM501 PWM1 connected to intn2 <->ip4 */ + LOONGSON_INTPOL = (1 << 13); + LOONGSON_INTEDGE &= ~(1 << 13); + ret = request_irq(CLOCK_PWM_IRQ, gdium_pwm_clock_interrupt, IRQF_DISABLED, drv_name, &gdium_pwm_clock_cevt); + if (ret) { + pr_err("Can't claim irq\n"); + goto err_pwm_disable; + } + + cs->rating = 200; + cs->mult = clocksource_hz2mult(CLOCK_PWM_FREQ, cs->shift); + ret = clocksource_register(&gdium_pwm_clock_clocksource); + if (ret) { + pr_err("Can't register clocksource\n"); + goto err_irq; + } + pr_info("Clocksource registered with shift %d and mult %d\n", + cs->shift, cs->mult); + + debugfs_file = debugfs_create_file(drv_name, S_IFREG | S_IRUGO, + NULL, NULL, &gdium_pwm_clock_fops); + + return 0; + +err_irq: + free_irq(CLOCK_PWM_IRQ, &gdium_pwm_clock_cevt); +err_pwm_disable: + pwm_disable(clock_pwm); +err_pwm_request: + pwm_free(clock_pwm); + return ret; +} + +static void gdium_pwm_clock_drvexit(void) +{ + free_irq(CLOCK_PWM_IRQ, &gdium_pwm_clock_cevt); + pwm_disable(clock_pwm); + pwm_free(clock_pwm); +} + + +static int __devinit gdium_pwm_clock_init(void) +{ + int ret = gdium_pwm_clock_drvinit(); + + if (ret) { + pr_err("Fail to register gdium clock driver\n"); + return ret; + } + + return platform_driver_register(&gdium_pwm_clock_driver); +} + +static void __exit gdium_pwm_clock_cleanup(void) +{ + gdium_pwm_clock_drvexit(); + platform_driver_unregister(&gdium_pwm_clock_driver); +} + +module_init(gdium_pwm_clock_init); +module_exit(gdium_pwm_clock_cleanup); + +MODULE_AUTHOR("Arnaud Patard "); +MODULE_DESCRIPTION("Gdium PWM clock driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gdium-pwmclk"); diff --git a/arch/mips/loongson/gdium/irq.c b/arch/mips/loongson/gdium/irq.c new file mode 100644 index 0000000..2415d20 --- /dev/null +++ b/arch/mips/loongson/gdium/irq.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007 Lemote Inc. + * Author: Fuxin Zhang, zhangfx@lemote.com + * + * Copyright (c) 2010 yajin + * + * 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 + +#define LOONGSON_TIMER_IRQ (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */ +#define LOONGSON_NORTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 6) /* bonito */ +#define LOONGSON_UART_IRQ (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */ + +void mach_irq_dispatch(unsigned int pending) +{ + if (pending & CAUSEF_IP7) + do_IRQ(LOONGSON_TIMER_IRQ); + else if (pending & CAUSEF_IP6) { /* North Bridge, Perf counter */ + do_perfcnt_IRQ(); + bonito_irqdispatch(); + } else if (pending & CAUSEF_IP3) /* CPU UART */ + do_IRQ(LOONGSON_UART_IRQ); +#if defined(CONFIG_GDIUM_PWM_CLOCK) || defined(CONFIG_GDIUM_PWM_CLOCK_MODULE) + else if (pending & CAUSEF_IP4) /* SM501 PWM clock */ + do_IRQ(MIPS_CPU_IRQ_BASE + 4); +#endif + else + spurious_interrupt(); +} + +static irqreturn_t ip6_action(int cpl, void *dev_id) +{ + return IRQ_HANDLED; +} + +struct irqaction ip6_irqaction = { + .handler = ip6_action, + .name = "cascade", + .flags = IRQF_SHARED, +}; + +void __init mach_init_irq(void) +{ + /* setup north bridge irq (bonito) */ + setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction); +} diff --git a/arch/mips/loongson/gdium/platform.c b/arch/mips/loongson/gdium/platform.c new file mode 100644 index 0000000..ffafba4 --- /dev/null +++ b/arch/mips/loongson/gdium/platform.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2009 Philippe Vachon + * + * 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 + +#define GDIUM_GPIO_BASE 224 + +static struct i2c_board_info __initdata sm502dev_i2c_devices[] = { + { + I2C_BOARD_INFO("lm75", 0x48), + }, + { + I2C_BOARD_INFO("m41t83", 0x68), + }, + { + I2C_BOARD_INFO("gdium-laptop", 0x40), + }, +}; + +static int sm502dev_backlight_init(struct device *dev) +{ + /* Add gpio request stuff here */ + return 0; +} + +static void sm502dev_backlight_exit(struct device *dev) +{ + /* Add gpio free stuff here */ +} + +static struct platform_pwm_backlight_data backlight_data = { + .pwm_id = 0, + .max_brightness = 15, + .dft_brightness = 8, + .pwm_period_ns = 50000, /* 20 kHz */ + .init = sm502dev_backlight_init, + .exit = sm502dev_backlight_exit, +}; + +static struct platform_device backlight = { + .name = "pwm-backlight", + .dev = { + .platform_data = &backlight_data, + }, + .id = -1, +}; + +/* + * Warning this stunt is very dangerous + * as the sm501 gpio have dynamic numbers... + */ +/* bus 0 is the one for the ST7, DS75 etc... */ +static struct i2c_gpio_platform_data i2c_gpio0_data = { +#if CONFIG_GDIUM_VERSION > 2 + .sda_pin = GDIUM_GPIO_BASE + 13, + .scl_pin = GDIUM_GPIO_BASE + 6, +#else + .sda_pin = 192+15, + .scl_pin = 192+14, +#endif + .udelay = 5, + .timeout = HZ / 10, + .sda_is_open_drain = 0, + .scl_is_open_drain = 0, +}; + +static struct platform_device i2c_gpio0_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { .platform_data = &i2c_gpio0_data, }, +}; + +/* bus 1 is for the CRT/VGA external screen */ +static struct i2c_gpio_platform_data i2c_gpio1_data = { + .sda_pin = GDIUM_GPIO_BASE + 10, + .scl_pin = GDIUM_GPIO_BASE + 9, + .udelay = 5, + .timeout = HZ / 10, + .sda_is_open_drain = 0, + .scl_is_open_drain = 0, +}; + +static struct platform_device i2c_gpio1_device = { + .name = "i2c-gpio", + .id = 1, + .dev = { .platform_data = &i2c_gpio1_data, }, +}; + +static struct platform_device gdium_clock = { + .name = "gdium-pwmclk", + .id = -1, +}; + +static struct platform_device *devices[] __initdata = { + &i2c_gpio0_device, + &i2c_gpio1_device, + &backlight, + &gdium_clock, +}; + +static int __init gdium_platform_devices_setup(void) +{ + int ret; + + pr_info("Registering gdium platform devices\n"); + + ret = i2c_register_board_info(0, sm502dev_i2c_devices, + ARRAY_SIZE(sm502dev_i2c_devices)); + + if (ret != 0) { + pr_info("Error while registering platform devices: %d\n", ret); + return ret; + } + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + return 0; +} + +/* + * some devices are on the pwm stuff which is behind the mfd which is + * behind the pci bus so arch_initcall can't work because too early + */ +late_initcall(gdium_platform_devices_setup); diff --git a/arch/mips/loongson/gdium/reset.c b/arch/mips/loongson/gdium/reset.c new file mode 100644 index 0000000..8289f95 --- /dev/null +++ b/arch/mips/loongson/gdium/reset.c @@ -0,0 +1,22 @@ +/* Board-specific reboot/shutdown routines + * + * Copyright (C) 2010 yajin + * + * 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 + +void mach_prepare_shutdown(void) +{ + LOONGSON_GPIOIE &= ~(1<<1); + LOONGSON_GPIODATA |= (1<<1); +} + +void mach_prepare_reboot(void) +{ + LOONGSON_GPIOIE &= ~(1<<2); + LOONGSON_GPIODATA &= ~(1<<2); +} diff --git a/arch/mips/loongson/gdium/sm501-pwm.c b/arch/mips/loongson/gdium/sm501-pwm.c new file mode 100644 index 0000000..5af3b23 --- /dev/null +++ b/arch/mips/loongson/gdium/sm501-pwm.c @@ -0,0 +1,465 @@ +/* + * SM501 PWM clock + * Copyright (C) 2009-2010 Arnaud Patard + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char drv_name[] = "sm501-pwm"; + +#define INPUT_CLOCK 96 /* MHz */ +#define PWM_COUNT 3 + +#define SM501PWM_HIGH_COUNTER (1<<20) +#define SM501PWM_LOW_COUNTER (1<<8) +#define SM501PWM_CLOCK_DIVIDE (1>>4) +#define SM501PWM_IP (1<<3) +#define SM501PWM_I (1<<2) +#define SM501PWM_E (1<<0) + +struct pwm_device { + struct list_head node; + struct device *dev; + void __iomem *regs; + int duty_ns; + int period_ns; + char enabled; + void (*handler)(struct pwm_device *pwm); + + const char *label; + unsigned int use_count; + unsigned int pwm_id; +}; + +struct sm501pwm_info { + void __iomem *regs; + int irq; + struct resource *res; + struct device *dev; + struct dentry *debugfs; + + struct pwm_device pwm[3]; +}; + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + unsigned int high, low, divider; + int divider1, divider2; + unsigned long long delay; + + if (!pwm || !pwm->regs || period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + /* Get delay + * We're loosing some precision but multiplying then dividing + * will overflow + */ + if (period_ns > 1000) { + delay = period_ns / 1000; + delay *= INPUT_CLOCK; + } else { + delay = period_ns * 96; + delay /= 1000; + } + + /* Get the number of clock low and high */ + high = delay * duty_ns / period_ns; + low = delay - high; + + /* Get divider to make 'low' and 'high' fit into 12 bits */ + /* No need to say that the divider must be >= 0 */ + divider1 = fls(low)-12; + divider2 = fls(high)-12; + + if (divider1 < 0) + divider1 = 0; + if (divider2 < 0) + divider2 = 0; + + divider = max(divider1, divider2); + + low >>= divider; + high >>= divider; + + pwm->duty_ns = duty_ns; + pwm->period_ns = period_ns; + + writel((high<<20)|(low<<8)|(divider<<4), pwm->regs); + return 0; +} +EXPORT_SYMBOL(pwm_config); + +int pwm_enable(struct pwm_device *pwm) +{ + u32 reg; + + if (!pwm) + return -EINVAL; + + switch (pwm->pwm_id) { + case 0: + sm501_configure_gpio(pwm->dev->parent, 29, 1); + break; + case 1: + sm501_configure_gpio(pwm->dev->parent, 30, 1); + break; + case 2: + sm501_configure_gpio(pwm->dev->parent, 31, 1); + break; + default: + return -EINVAL; + } + + reg = readl(pwm->regs); + reg |= (SM501PWM_IP | SM501PWM_E); + writel(reg, pwm->regs); + pwm->enabled = 1; + + return 0; +} +EXPORT_SYMBOL(pwm_enable); + +void pwm_disable(struct pwm_device *pwm) +{ + u32 reg; + + if (!pwm) + return; + + reg = readl(pwm->regs); + reg &= ~(SM501PWM_IP | SM501PWM_E); + writel(reg, pwm->regs); + + switch (pwm->pwm_id) { + case 0: + sm501_configure_gpio(pwm->dev->parent, 29, 0); + break; + case 1: + sm501_configure_gpio(pwm->dev->parent, 30, 0); + break; + case 2: + sm501_configure_gpio(pwm->dev->parent, 31, 0); + break; + default: + break; + } + pwm->enabled = 0; +} +EXPORT_SYMBOL(pwm_disable); + +static DEFINE_MUTEX(pwm_lock); +static LIST_HEAD(pwm_list); + +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + struct pwm_device *pwm; + int found = 0; + + mutex_lock(&pwm_lock); + + list_for_each_entry(pwm, &pwm_list, node) { + if (pwm->pwm_id == pwm_id && pwm->use_count == 0) { + pwm->use_count++; + pwm->label = label; + found = 1; + break; + } + } + + mutex_unlock(&pwm_lock); + + return (found) ? pwm : NULL; +} +EXPORT_SYMBOL(pwm_request); + +void pwm_free(struct pwm_device *pwm) +{ + mutex_lock(&pwm_lock); + + if (pwm->use_count) { + pwm->use_count--; + pwm->label = NULL; + } else + dev_warn(pwm->dev, "PWM device already freed\n"); + + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL(pwm_free); + +int pwm_int_enable(struct pwm_device *pwm) +{ + unsigned long conf; + + if (!pwm || !pwm->regs || !pwm->handler) + return -EINVAL; + + conf = readl(pwm->regs); + conf |= SM501PWM_I; + writel(conf, pwm->regs); + return 0; +} +EXPORT_SYMBOL(pwm_int_enable); + +int pwm_int_disable(struct pwm_device *pwm) +{ + unsigned long conf; + + if (!pwm || !pwm->regs || !pwm->handler) + return -EINVAL; + + conf = readl(pwm->regs); + conf &= ~SM501PWM_I; + writel(conf, pwm->regs); + return 0; +} +EXPORT_SYMBOL(pwm_int_disable); + +int pwm_set_handler(struct pwm_device *pwm, + void (*handler)(struct pwm_device *pwm)) +{ + if (!pwm || !handler) + return -EINVAL; + pwm->handler = handler; + return 0; +} +EXPORT_SYMBOL(pwm_set_handler); + +static irqreturn_t sm501pwm_irq(int irq, void *dev_id) +{ + unsigned long value; + struct sm501pwm_info *info = (struct sm501pwm_info *)dev_id; + struct pwm_device *pwm; + int i; + + value = sm501_modify_reg(info->dev->parent, SM501_IRQ_STATUS, 0, 0); + + /* Check is the interrupt is for us */ + if (value & (1<<22)) { + for (i = 0 ; i < PWM_COUNT ; i++) { + /* + * Find which pwm triggered the interrupt + * and ack + */ + value = readl(info->regs + i*4); + if (value & SM501PWM_IP) + writel(value | SM501PWM_IP, info->regs + i*4); + + pwm = &info->pwm[i]; + if (pwm->handler) + pwm->handler(pwm); + } + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void add_pwm(int id, struct sm501pwm_info *info) +{ + struct pwm_device *pwm = &info->pwm[id]; + + pwm->use_count = 0; + pwm->pwm_id = id; + pwm->dev = info->dev; + pwm->regs = info->regs + id * 4; + + mutex_lock(&pwm_lock); + list_add_tail(&pwm->node, &pwm_list); + mutex_unlock(&pwm_lock); +} + +static void del_pwm(int id, struct sm501pwm_info *info) +{ + struct pwm_device *pwm = &info->pwm[id]; + + pwm->use_count = 0; + pwm->pwm_id = -1; + mutex_lock(&pwm_lock); + list_del(&pwm->node); + mutex_unlock(&pwm_lock); +} + +/* Debug fs */ +static int sm501pwm_show(struct seq_file *s, void *p) +{ + struct pwm_device *pwm; + + mutex_lock(&pwm_lock); + list_for_each_entry(pwm, &pwm_list, node) { + if (pwm->use_count) { + seq_printf(s, "pwm-%d (%12s) %d %d %s\n", + pwm->pwm_id, pwm->label, + pwm->duty_ns, pwm->period_ns, + pwm->enabled ? "on" : "off"); + seq_printf(s, " %08x\n", readl(pwm->regs)); + } + } + mutex_unlock(&pwm_lock); + + return 0; +} + +static int sm501pwm_open(struct inode *inode, struct file *file) +{ + return single_open(file, sm501pwm_show, inode->i_private); +} + +static const struct file_operations sm501pwm_fops = { + .open = sm501pwm_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int __init sm501pwm_probe(struct platform_device *pdev) +{ + struct sm501pwm_info *info; + struct device *dev = &pdev->dev; + struct resource *res; + int ret = 0; + int res_len; + int i; + + info = kzalloc(sizeof(struct sm501pwm_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "Allocation failure\n"); + ret = -ENOMEM; + goto err; + } + info->dev = dev; + platform_set_drvdata(pdev, info); + + /* Get irq number */ + info->irq = platform_get_irq(pdev, 0); + if (!info->irq) { + dev_err(dev, "no irq found\n"); + ret = -ENODEV; + goto err_alloc; + } + + /* Get regs address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(dev, "No memory resource found\n"); + ret = -ENODEV; + goto err_alloc; + } + info->res = res; + res_len = (res->end - res->start)+1; + + if (!request_mem_region(res->start, res_len, drv_name)) { + dev_err(dev, "Can't request iomem resource\n"); + ret = -EBUSY; + goto err_alloc; + } + + info->regs = ioremap(res->start, res_len); + if (!info->regs) { + dev_err(dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_mem; + } + + ret = request_irq(info->irq, sm501pwm_irq, IRQF_SHARED, drv_name, info); + if (ret != 0) { + dev_err(dev, "can't get irq\n"); + goto err_map; + } + + + sm501_unit_power(info->dev->parent, SM501_GATE_GPIO, 1); + + for (i = 0; i < 3; i++) + add_pwm(i, info); + + dev_info(dev, "SM501 PWM Found at %lx irq %d\n", + (unsigned long)info->res->start, info->irq); + + info->debugfs = debugfs_create_file("pwm", S_IFREG | S_IRUGO, + NULL, info, &sm501pwm_fops); + + + return 0; + +err_map: + iounmap(info->regs); + +err_mem: + release_mem_region(res->start, res_len); + +err_alloc: + kfree(info); + platform_set_drvdata(pdev, NULL); +err: + return ret; +} + +static int sm501pwm_remove(struct platform_device *pdev) +{ + struct sm501pwm_info *info = platform_get_drvdata(pdev); + int i; + + if (info->debugfs) + debugfs_remove(info->debugfs); + + for (i = 0; i < 3; i++) { + pwm_disable(&info->pwm[i]); + del_pwm(i, info); + } + + sm501_unit_power(info->dev->parent, SM501_GATE_GPIO, 0); + sm501_modify_reg(info->dev->parent, SM501_IRQ_STATUS, 0, 1<<22); + + free_irq(info->irq, info); + iounmap(info->regs); + release_mem_region(info->res->start, + (info->res->end - info->res->start)+1); + kfree(info); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver sm501pwm_driver = { + .probe = sm501pwm_probe, + .remove = sm501pwm_remove, + .driver = { + .name = drv_name, + .owner = THIS_MODULE, + }, +}; + +static int __devinit sm501pwm_init(void) +{ + return platform_driver_register(&sm501pwm_driver); +} + +static void __exit sm501pwm_cleanup(void) +{ + platform_driver_unregister(&sm501pwm_driver); +} + +module_init(sm501pwm_init); +module_exit(sm501pwm_cleanup); + +MODULE_AUTHOR("Arnaud Patard "); +MODULE_DESCRIPTION("SM501 PWM driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sm501-pwm"); diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile index 8699a53..10e9eb7 100644 --- a/arch/mips/loongson/lemote-2f/Makefile +++ b/arch/mips/loongson/lemote-2f/Makefile @@ -2,7 +2,7 @@ # Makefile for lemote loongson2f family machines # -obj-y += machtype.o irq.o reset.o ec_kb3310b.o +obj-y += machtype.o irq.o reset.o ec_kb3310b.o platform.o # # Suspend Support diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.c b/arch/mips/loongson/lemote-2f/ec_kb3310b.c index 2b666d3..88ef558 100644 --- a/arch/mips/loongson/lemote-2f/ec_kb3310b.c +++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.c @@ -14,7 +14,7 @@ #include #include -#include "ec_kb3310b.h" +#include static DEFINE_SPINLOCK(index_access_lock); static DEFINE_SPINLOCK(port_access_lock); @@ -76,12 +76,9 @@ int ec_query_seq(unsigned char cmd) spin_unlock_irqrestore(&port_access_lock, flags); if (timeout <= 0) { - printk(KERN_ERR "%s: deadable error : timeout...\n", __func__); + pr_err("%s: deadable error : timeout...\n", __func__); ret = -EINVAL; - } else - printk(KERN_INFO - "(%x/%d)ec issued command %d status : 0x%x\n", - timeout, EC_CMD_TIMEOUT - timeout, cmd, status); + } return ret; } @@ -116,8 +113,7 @@ int ec_get_event_num(void) udelay(EC_REG_DELAY); } if (timeout <= 0) { - pr_info("%s: get event number timeout.\n", __func__); - + pr_err("%s: get event number timeout.\n", __func__); return -EINVAL; } value = inb(EC_DAT_PORT); diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.h b/arch/mips/loongson/lemote-2f/ec_kb3310b.h deleted file mode 100644 index 1595a21..0000000 --- a/arch/mips/loongson/lemote-2f/ec_kb3310b.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * KB3310B Embedded Controller - * - * Copyright (C) 2008 Lemote Inc. - * Author: liujl , 2008-03-14 - * - * 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 _EC_KB3310B_H -#define _EC_KB3310B_H - -extern unsigned char ec_read(unsigned short addr); -extern void ec_write(unsigned short addr, unsigned char val); -extern int ec_query_seq(unsigned char cmd); -extern int ec_query_event_num(void); -extern int ec_get_event_num(void); - -typedef int (*sci_handler) (int status); -extern sci_handler yeeloong_report_lid_status; - -#define SCI_IRQ_NUM 0x0A - -/* - * The following registers are determined by the EC index configuration. - * 1, fill the PORT_HIGH as EC register high part. - * 2, fill the PORT_LOW as EC register low part. - * 3, fill the PORT_DATA as EC register write data or get the data from it. - */ -#define EC_IO_PORT_HIGH 0x0381 -#define EC_IO_PORT_LOW 0x0382 -#define EC_IO_PORT_DATA 0x0383 - -/* - * EC delay time is 500us for register and status access - */ -#define EC_REG_DELAY 500 /* unit : us */ -#define EC_CMD_TIMEOUT 0x1000 - -/* - * EC access port for SCI communication - */ -#define EC_CMD_PORT 0x66 -#define EC_STS_PORT 0x66 -#define EC_DAT_PORT 0x62 -#define CMD_INIT_IDLE_MODE 0xdd -#define CMD_EXIT_IDLE_MODE 0xdf -#define CMD_INIT_RESET_MODE 0xd8 -#define CMD_REBOOT_SYSTEM 0x8c -#define CMD_GET_EVENT_NUM 0x84 -#define CMD_PROGRAM_PIECE 0xda - -/* temperature & fan registers */ -#define REG_TEMPERATURE_VALUE 0xF458 -#define REG_FAN_AUTO_MAN_SWITCH 0xF459 -#define BIT_FAN_AUTO 0 -#define BIT_FAN_MANUAL 1 -#define REG_FAN_CONTROL 0xF4D2 -#define BIT_FAN_CONTROL_ON (1 << 0) -#define BIT_FAN_CONTROL_OFF (0 << 0) -#define REG_FAN_STATUS 0xF4DA -#define BIT_FAN_STATUS_ON (1 << 0) -#define BIT_FAN_STATUS_OFF (0 << 0) -#define REG_FAN_SPEED_HIGH 0xFE22 -#define REG_FAN_SPEED_LOW 0xFE23 -#define REG_FAN_SPEED_LEVEL 0xF4CC -/* fan speed divider */ -#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/ - -/* battery registers */ -#define REG_BAT_DESIGN_CAP_HIGH 0xF77D -#define REG_BAT_DESIGN_CAP_LOW 0xF77E -#define REG_BAT_FULLCHG_CAP_HIGH 0xF780 -#define REG_BAT_FULLCHG_CAP_LOW 0xF781 -#define REG_BAT_DESIGN_VOL_HIGH 0xF782 -#define REG_BAT_DESIGN_VOL_LOW 0xF783 -#define REG_BAT_CURRENT_HIGH 0xF784 -#define REG_BAT_CURRENT_LOW 0xF785 -#define REG_BAT_VOLTAGE_HIGH 0xF786 -#define REG_BAT_VOLTAGE_LOW 0xF787 -#define REG_BAT_TEMPERATURE_HIGH 0xF788 -#define REG_BAT_TEMPERATURE_LOW 0xF789 -#define REG_BAT_RELATIVE_CAP_HIGH 0xF492 -#define REG_BAT_RELATIVE_CAP_LOW 0xF493 -#define REG_BAT_VENDOR 0xF4C4 -#define FLAG_BAT_VENDOR_SANYO 0x01 -#define FLAG_BAT_VENDOR_SIMPLO 0x02 -#define REG_BAT_CELL_COUNT 0xF4C6 -#define FLAG_BAT_CELL_3S1P 0x03 -#define FLAG_BAT_CELL_3S2P 0x06 -#define REG_BAT_CHARGE 0xF4A2 -#define FLAG_BAT_CHARGE_DISCHARGE 0x01 -#define FLAG_BAT_CHARGE_CHARGE 0x02 -#define FLAG_BAT_CHARGE_ACPOWER 0x00 -#define REG_BAT_STATUS 0xF4B0 -#define BIT_BAT_STATUS_LOW (1 << 5) -#define BIT_BAT_STATUS_DESTROY (1 << 2) -#define BIT_BAT_STATUS_FULL (1 << 1) -#define BIT_BAT_STATUS_IN (1 << 0) -#define REG_BAT_CHARGE_STATUS 0xF4B1 -#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2) -#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1) -#define REG_BAT_STATE 0xF482 -#define BIT_BAT_STATE_CHARGING (1 << 1) -#define BIT_BAT_STATE_DISCHARGING (1 << 0) -#define REG_BAT_POWER 0xF440 -#define BIT_BAT_POWER_S3 (1 << 2) -#define BIT_BAT_POWER_ON (1 << 1) -#define BIT_BAT_POWER_ACIN (1 << 0) - -/* other registers */ -/* Audio: rd/wr */ -#define REG_AUDIO_VOLUME 0xF46C -#define REG_AUDIO_MUTE 0xF4E7 -#define REG_AUDIO_BEEP 0xF4D0 -/* USB port power or not: rd/wr */ -#define REG_USB0_FLAG 0xF461 -#define REG_USB1_FLAG 0xF462 -#define REG_USB2_FLAG 0xF463 -#define BIT_USB_FLAG_ON 1 -#define BIT_USB_FLAG_OFF 0 -/* LID */ -#define REG_LID_DETECT 0xF4BD -#define BIT_LID_DETECT_ON 1 -#define BIT_LID_DETECT_OFF 0 -/* CRT */ -#define REG_CRT_DETECT 0xF4AD -#define BIT_CRT_DETECT_PLUG 1 -#define BIT_CRT_DETECT_UNPLUG 0 -/* LCD backlight brightness adjust: 9 levels */ -#define REG_DISPLAY_BRIGHTNESS 0xF4F5 -/* Black screen Status */ -#define BIT_DISPLAY_LCD_ON 1 -#define BIT_DISPLAY_LCD_OFF 0 -/* LCD backlight control: off/restore */ -#define REG_BACKLIGHT_CTRL 0xF7BD -#define BIT_BACKLIGHT_ON 1 -#define BIT_BACKLIGHT_OFF 0 -/* Reset the machine auto-clear: rd/wr */ -#define REG_RESET 0xF4EC -#define BIT_RESET_ON 1 -/* Light the led: rd/wr */ -#define REG_LED 0xF4C8 -#define BIT_LED_RED_POWER (1 << 0) -#define BIT_LED_ORANGE_POWER (1 << 1) -#define BIT_LED_GREEN_CHARGE (1 << 2) -#define BIT_LED_RED_CHARGE (1 << 3) -#define BIT_LED_NUMLOCK (1 << 4) -/* Test led mode, all led on/off */ -#define REG_LED_TEST 0xF4C2 -#define BIT_LED_TEST_IN 1 -#define BIT_LED_TEST_OUT 0 -/* Camera on/off */ -#define REG_CAMERA_STATUS 0xF46A -#define BIT_CAMERA_STATUS_ON 1 -#define BIT_CAMERA_STATUS_OFF 0 -#define REG_CAMERA_CONTROL 0xF7B7 -#define BIT_CAMERA_CONTROL_OFF 0 -#define BIT_CAMERA_CONTROL_ON 1 -/* Wlan Status */ -#define REG_WLAN 0xF4FA -#define BIT_WLAN_ON 1 -#define BIT_WLAN_OFF 0 -#define REG_DISPLAY_LCD 0xF79F - -/* SCI Event Number from EC */ -enum { - EVENT_LID = 0x23, /* LID open/close */ - EVENT_DISPLAY_TOGGLE, /* Fn+F3 for display switch */ - EVENT_SLEEP, /* Fn+F1 for entering sleep mode */ - EVENT_OVERTEMP, /* Over-temperature happened */ - EVENT_CRT_DETECT, /* CRT is connected */ - EVENT_CAMERA, /* Camera on/off */ - EVENT_USB_OC2, /* USB2 Over Current occurred */ - EVENT_USB_OC0, /* USB0 Over Current occurred */ - EVENT_BLACK_SCREEN, /* Turn on/off backlight */ - EVENT_AUDIO_MUTE, /* Mute on/off */ - EVENT_DISPLAY_BRIGHTNESS,/* LCD backlight brightness adjust */ - EVENT_AC_BAT, /* AC & Battery relative issue */ - EVENT_AUDIO_VOLUME, /* Volume adjust */ - EVENT_WLAN, /* Wlan on/off */ - EVENT_END -}; - -#endif /* !_EC_KB3310B_H */ diff --git a/arch/mips/loongson/lemote-2f/irq.c b/arch/mips/loongson/lemote-2f/irq.c index 14b0818..d95487c 100644 --- a/arch/mips/loongson/lemote-2f/irq.c +++ b/arch/mips/loongson/lemote-2f/irq.c @@ -11,9 +11,7 @@ #include #include -#include #include -#include #include #include @@ -107,21 +105,10 @@ struct irqaction cascade_irqaction = { void __init mach_init_irq(void) { - /* init all controller - * 0-15 ------> i8259 interrupt - * 16-23 ------> mips cpu interrupt - * 32-63 ------> bonito irq - */ - /* setup cs5536 as high level trigger */ LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1; LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1); - /* Sets the first-level interrupt dispatcher. */ - mips_cpu_irq_init(); - init_i8259_irqs(); - bonito_irq_init(); - /* setup north bridge irq (bonito) */ setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction); /* setup source bridge irq (i8259) */ diff --git a/arch/mips/loongson/lemote-2f/platform.c b/arch/mips/loongson/lemote-2f/platform.c new file mode 100644 index 0000000..5316360 --- /dev/null +++ b/arch/mips/loongson/lemote-2f/platform.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin, wuzhangjin@gmail.com + * + * 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 + +static struct platform_device yeeloong_pdev = { + .name = "yeeloong_laptop", + .id = -1, +}; + +static struct platform_device lynloong_pdev = { + .name = "lynloong_pc", + .id = -1, +}; + +static int __init lemote2f_platform_init(void) +{ + struct platform_device *pdev = NULL; + + switch (mips_machtype) { + case MACH_LEMOTE_YL2F89: + pdev = &yeeloong_pdev; + break; + case MACH_LEMOTE_LL2F: + pdev = &lynloong_pdev; + break; + default: + break; + + } + + if (pdev != NULL) + return platform_device_register(pdev); + + return -ENODEV; +} + +arch_initcall(lemote2f_platform_init); diff --git a/arch/mips/loongson/lemote-2f/pm.c b/arch/mips/loongson/lemote-2f/pm.c index cac4d38..b82ab17 100644 --- a/arch/mips/loongson/lemote-2f/pm.c +++ b/arch/mips/loongson/lemote-2f/pm.c @@ -23,7 +23,7 @@ #include #include -#include "ec_kb3310b.h" +#include #define I8042_KBD_IRQ 1 #define I8042_CTR_KBDINT 0x01 @@ -88,7 +88,7 @@ EXPORT_SYMBOL(yeeloong_report_lid_status); static void yeeloong_lid_update_task(struct work_struct *work) { if (yeeloong_report_lid_status) - yeeloong_report_lid_status(BIT_LID_DETECT_ON); + yeeloong_report_lid_status(ON); } int wakeup_loongson(void) @@ -100,7 +100,7 @@ int wakeup_loongson(void) if (irq < 0) return 0; - printk(KERN_INFO "%s: irq = %d\n", __func__, irq); + pr_debug("%s: irq = %d\n", __func__, irq); if (irq == I8042_KBD_IRQ) return 1; @@ -118,7 +118,7 @@ int wakeup_loongson(void) /* check the LID status */ lid_status = ec_read(REG_LID_DETECT); /* wakeup cpu when people open the LID */ - if (lid_status == BIT_LID_DETECT_ON) { + if (lid_status == ON) { /* If we call it directly here, the WARNING * will be sent out by getnstimeofday * via "WARN_ON(timekeeping_suspended);" @@ -140,10 +140,10 @@ int wakeup_loongson(void) void __weak mach_suspend(void) { - disable_mfgpt0_counter(); + disable_mfgpt_counter(); } void __weak mach_resume(void) { - enable_mfgpt0_counter(); + enable_mfgpt_counter(); } diff --git a/arch/mips/loongson/lemote-2f/reset.c b/arch/mips/loongson/lemote-2f/reset.c index 36020a0..039c2ce 100644 --- a/arch/mips/loongson/lemote-2f/reset.c +++ b/arch/mips/loongson/lemote-2f/reset.c @@ -20,15 +20,14 @@ #include #include -#include "ec_kb3310b.h" +#include static void reset_cpu(void) { /* - * reset cpu to full speed, this is needed when enabling cpu frequency - * scalling + * reset cpu to full speed */ - LOONGSON_CHIPCFG0 |= 0x7; + LOONGSON_CHIPCFG0 |= 7; } /* reset support for fuloong2f */ @@ -81,7 +80,7 @@ void ml2f_reboot(void) reset_cpu(); /* sending an reset signal to EC(embedded controller) */ - ec_write(REG_RESET, BIT_RESET_ON); + ec_write(REG_RESET, ON); } #define yl2f89_reboot ml2f_reboot diff --git a/arch/mips/mm/c-octeon.c b/arch/mips/mm/c-octeon.c index 16c4d25..8182758 100644 --- a/arch/mips/mm/c-octeon.c +++ b/arch/mips/mm/c-octeon.c @@ -182,7 +182,7 @@ static void __cpuinit probe_octeon(void) struct cpuinfo_mips *c = ¤t_cpu_data; config1 = read_c0_config1(); - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_CAVIUM_OCTEON: case CPU_CAVIUM_OCTEON_PLUS: c->icache.linesz = 2 << ((config1 >> 19) & 7); diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index b9aabb9..22a94f1 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -766,7 +766,7 @@ static void __cpuinit probe_pcache(void) unsigned long config1; unsigned int lsize; - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_R4600: /* QED style two way caches? */ case CPU_R4700: case CPU_R5000: @@ -854,10 +854,10 @@ static void __cpuinit probe_pcache(void) write_c0_config(config & ~VR41_CONF_P4K); case CPU_VR4131: /* Workaround for cache instruction bug of VR4131 */ - if (c->processor_id == 0x0c80U || c->processor_id == 0x0c81U || - c->processor_id == 0x0c82U) { + if (current_cpu_prid() == 0x0c80U || current_cpu_prid() == 0x0c81U || + current_cpu_prid() == 0x0c82U) { config |= 0x00400000U; - if (c->processor_id == 0x0c80U) + if (current_cpu_prid() == 0x0c80U) config |= VR41_CONF_BP; write_c0_config(config); } else @@ -1005,7 +1005,7 @@ static void __cpuinit probe_pcache(void) * normally they'd suffer from aliases but magic in the hardware deals * with that for us so we don't need to take care ourselves. */ - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_20KC: case CPU_25KF: case CPU_SB1: @@ -1034,7 +1034,7 @@ static void __cpuinit probe_pcache(void) c->dcache.flags |= MIPS_CACHE_ALIASES; } - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_20KC: /* * Some older 20Kc chips doesn't have the 'VI' bit in @@ -1163,7 +1163,7 @@ static void __cpuinit setup_scache(void) * processors don't have a S-cache that would be relevant to the * Linux memory management. */ - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_R4000SC: case CPU_R4000MC: case CPU_R4400SC: @@ -1358,7 +1358,7 @@ void __cpuinit r4k_cache_init(void) extern char __weak except_vec2_sb1; struct cpuinfo_mips *c = ¤t_cpu_data; - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_SB1: case CPU_SB1A: set_uncached_handler(0x100, &except_vec2_sb1, 0x80); diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index 4608491..1e20758 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -310,7 +310,7 @@ int mips_dma_supported(struct device *dev, u64 mask) return plat_dma_supported(dev, mask); } -void dma_cache_sync(struct device *dev, void *vaddr, size_t size, +void mips_dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); @@ -320,8 +320,6 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size, __dma_sync_virtual(vaddr, size, direction); } -EXPORT_SYMBOL(dma_cache_sync); - static struct dma_map_ops mips_default_dma_map_ops = { .alloc_coherent = mips_dma_alloc_coherent, .free_coherent = mips_dma_free_coherent, diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c index 36272f7..24cdc98 100644 --- a/arch/mips/mm/page.c +++ b/arch/mips/mm/page.c @@ -212,7 +212,7 @@ static void __cpuinit set_prefetch_parameters(void) * hints are broken. */ if (current_cpu_type() == CPU_SB1 && - (current_cpu_data.processor_id & 0xff) < 0x02) { + cpu_prid_rev() < 0x02) { pref_src_mode = Pref_Load; pref_dst_mode = Pref_Store; } else { diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c index 9cca8de..0e633db 100644 --- a/arch/mips/mm/sc-mips.c +++ b/arch/mips/mm/sc-mips.c @@ -72,7 +72,7 @@ static inline int mips_sc_is_activated(struct cpuinfo_mips *c) unsigned int tmp; /* Check the bypass bit (L2B) */ - switch (c->cputype) { + switch (current_cpu_type()) { case CPU_34K: case CPU_74K: case CPU_1004K: diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index e06370f..ef36dcf 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -136,7 +136,7 @@ static int scratchpad_offset(int i) */ static int __cpuinit m4kc_tlbp_war(void) { - return (current_cpu_data.processor_id & 0xffff00) == + return (current_cpu_prid() & 0xffff00) == (PRID_COMP_MIPS | PRID_IMP_4KC); } @@ -575,7 +575,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, default: panic("No TLB refill handler yet (CPU type: %d)", - current_cpu_data.cputype); + current_cpu_type()); break; } } diff --git a/arch/mips/oprofile/op_model_mipsxx.c b/arch/mips/oprofile/op_model_mipsxx.c index 54759f1..9d12fac 100644 --- a/arch/mips/oprofile/op_model_mipsxx.c +++ b/arch/mips/oprofile/op_model_mipsxx.c @@ -349,7 +349,7 @@ static int __init mipsxx_init(void) break; case CPU_R10000: - if ((current_cpu_data.processor_id & 0xff) == 0x20) + if (current_cpu_prid() == 0x20) op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x"; else op_model_mipsxx_ops.cpu_type = "mips/r10000"; diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile index 4df8799..7361d01 100644 --- a/arch/mips/pci/Makefile +++ b/arch/mips/pci/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_SOC_AU1550) += fixup-au1000.o ops-au1000.o obj-$(CONFIG_SOC_PNX8550) += fixup-pnx8550.o ops-pnx8550.o obj-$(CONFIG_LEMOTE_FULOONG2E) += fixup-fuloong2e.o ops-loongson2.o obj-$(CONFIG_LEMOTE_MACH2F) += fixup-lemote2f.o ops-loongson2.o +obj-$(CONFIG_DEXXON_GDIUM) += fixup-gdium.o ops-loongson2.o obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o obj-$(CONFIG_PMC_MSP7120_GW) += fixup-pmcmsp.o ops-pmcmsp.o obj-$(CONFIG_PMC_MSP7120_EVAL) += fixup-pmcmsp.o ops-pmcmsp.o diff --git a/arch/mips/pci/fixup-gdium.c b/arch/mips/pci/fixup-gdium.c new file mode 100644 index 0000000..b296220 --- /dev/null +++ b/arch/mips/pci/fixup-gdium.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010 yajin + * + * 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 +/* + * http://www.pcidatabase.com + * GDIUM has different PCI mapping + * slot 13 (0x1814/0x0301) -> RaLink rt2561 Wireless-G PCI + * slog 14 (0x126f/0x0501) -> sm501 + * slot 15 (0x1033/0x0035) -> NEC Dual OHCI controllers + * plus Single EHCI controller + * slot 16 (0x10ec/0x8139) -> Realtek 8139c + * slot 17 (0x1033/0x00e0) -> NEC USB 2.0 Host Controller + */ + +#undef INT_IRQA +#undef INT_IRQB +#undef INT_IRQC +#undef INT_IRQD +#define INT_IRQA 36 +#define INT_IRQB 37 +#define INT_IRQC 38 +#define INT_IRQD 39 + +int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + int irq = 0; + + switch (slot) { + case 13: + irq = INT_IRQC + ((pin - 1) & 3); + break; + case 14: + irq = INT_IRQA; + break; + case 15: +#if CONFIG_GDIUM_VERSION > 2 + irq = INT_IRQB; +#else + irq = INT_IRQA + ((pin - 1) & 3); +#endif + break; + case 16: + irq = INT_IRQD; + break; +#if CONFIG_GDIUM_VERSION > 2 + case 17: + irq = INT_IRQC; + break; +#endif + default: + pr_info(" strange pci slot number %d on gdium.\n", slot); + break; + } + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +/* Fixups for the USB host controllers */ +static void __init gdium_usb_host_fixup(struct pci_dev *dev) +{ + unsigned int val; + pci_read_config_dword(dev, 0xe0, &val); +#if CONFIG_GDIUM_VERSION > 2 + pci_write_config_dword(dev, 0xe0, (val & ~3) | 0x3); +#else + pci_write_config_dword(dev, 0xe0, (val & ~7) | 0x5); + pci_write_config_dword(dev, 0xe4, 1<<5); +#endif +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB, + gdium_usb_host_fixup); +#if CONFIG_GDIUM_VERSION > 2 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_CT_65550, + gdium_usb_host_fixup); +#endif diff --git a/arch/mips/pci/pci-vr41xx.c b/arch/mips/pci/pci-vr41xx.c index 444b8d8..e105c49 100644 --- a/arch/mips/pci/pci-vr41xx.c +++ b/arch/mips/pci/pci-vr41xx.c @@ -147,7 +147,7 @@ static int __init vr41xx_pciu_init(void) pciu_write(PCICLKSELREG, EQUAL_VTCLOCK); else if ((vtclock / 2) < pci_clock_max) pciu_write(PCICLKSELREG, HALF_VTCLOCK); - else if (current_cpu_data.processor_id >= PRID_VR4131_REV2_1 && + else if (current_cpu_prid() >= PRID_VR4131_REV2_1 && (vtclock / 3) < pci_clock_max) pciu_write(PCICLKSELREG, ONE_THIRD_VTCLOCK); else if ((vtclock / 4) < pci_clock_max) diff --git a/arch/mips/vr41xx/common/init.c b/arch/mips/vr41xx/common/init.c index 2391632..aca5c76 100644 --- a/arch/mips/vr41xx/common/init.c +++ b/arch/mips/vr41xx/common/init.c @@ -43,8 +43,8 @@ void __init plat_time_init(void) vr41xx_calculate_clock_frequency(); tclock = vr41xx_get_tclock_frequency(); - if (current_cpu_data.processor_id == PRID_VR4131_REV2_0 || - current_cpu_data.processor_id == PRID_VR4131_REV2_1) + if (current_cpu_prid() == PRID_VR4131_REV2_0 || + current_cpu_prid() == PRID_VR4131_REV2_1) mips_hpt_frequency = tclock / 2; else mips_hpt_frequency = tclock / 4; diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index 628c8fa..a6e6c96 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -44,8 +44,6 @@ static int use_msr; module_param_named(msr, use_msr, int, 0644); MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); #else -#undef rdmsr /* avoid accidental MSR usage on, e.g. x86-64 */ -#undef wrmsr #define rdmsr(x, y, z) do { } while (0) #define wrmsr(x, y, z) do { } while (0) #define use_msr 0 diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1130a89..1ab3076 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -613,6 +613,13 @@ config HID_ZYDACRON ---help--- Support for Zydacron remote control. +config HID_GDIUM + bool "Gdium Fn keys support" if EMBEDDED + depends on USB_HID && DEXXON_GDIUM + default !EMBEDDED + ---help--- + Support for Functions keys available on Gdiums. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0a0a38e..8085eef 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o +obj-$(CONFIG_HID_GDIUM) += hid-gdium.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o obj-$(CONFIG_USB_HID) += usbhid/ diff --git a/drivers/hid/hid-gdium.c b/drivers/hid/hid-gdium.c new file mode 100644 index 0000000..67cc095 --- /dev/null +++ b/drivers/hid/hid-gdium.c @@ -0,0 +1,210 @@ +/* + * hid-gdium -- Gdium laptop function keys + * + * Arnaud Patard + * + * Based on hid-apple.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define GDIUM_FN_ON 1 + +static int fnmode = GDIUM_FN_ON; +module_param(fnmode, int, 0644); +MODULE_PARM_DESC(fnmode, "Mode of fn key on Gdium (0 = disabled, 1 = Enabled)"); + +struct gdium_data { + unsigned int fn_on; +}; + + +struct gdium_key_translation { + u16 from; + u16 to; +}; + +static struct gdium_key_translation gdium_fn_keys[] = { + { KEY_F1, KEY_CAMERA }, + { KEY_F2, KEY_CONNECT }, + { KEY_F3, KEY_MUTE }, + { KEY_F4, KEY_VOLUMEUP}, + { KEY_F5, KEY_VOLUMEDOWN }, + { KEY_F6, KEY_SWITCHVIDEOMODE }, + { KEY_F7, KEY_F19 }, /* F7+12. Have to use existant keycodes */ + { KEY_F8, KEY_BRIGHTNESSUP }, + { KEY_F9, KEY_BRIGHTNESSDOWN }, + { KEY_F10, KEY_SLEEP }, + { KEY_F11, KEY_PROG1 }, + { KEY_F12, KEY_PROG2 }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_INSERT, KEY_NUMLOCK }, + { KEY_DELETE, KEY_SCROLLLOCK }, + { KEY_T, KEY_STOPCD }, + { KEY_F, KEY_PREVIOUSSONG }, + { KEY_H, KEY_NEXTSONG }, + { KEY_G, KEY_PLAYPAUSE }, + { } +}; + +static struct gdium_key_translation *gdium_find_translation( + struct gdium_key_translation *table, u16 from) +{ + struct gdium_key_translation *trans; + + /* Look for the translation */ + for (trans = table; trans->from; trans++) + if (trans->from == from) + return trans; + return NULL; +} + +static int hidinput_gdium_event(struct hid_device *hid, struct input_dev *input, + struct hid_usage *usage, __s32 value) +{ + struct gdium_data *data = hid_get_drvdata(hid); + struct gdium_key_translation *trans; + int do_translate; + + if (usage->type != EV_KEY) + return 0; + + if ((usage->code == KEY_FN)) { + data->fn_on = !!value; + input_event(input, usage->type, usage->code, value); + return 1; + } + + if (fnmode) { + trans = gdium_find_translation(gdium_fn_keys, usage->code); + if (trans) { + do_translate = data->fn_on; + if (do_translate) { + input_event(input, usage->type, trans->to, value); + return 1; + } + } + } + + return 0; +} + +static int gdium_input_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || !usage->type) + return 0; + + if (hidinput_gdium_event(hdev, field->hidinput->input, usage, value)) + return 1; + + return 0; +} + + +static void gdium_input_setup(struct input_dev *input) +{ + struct gdium_key_translation *trans; + + set_bit(KEY_NUMLOCK, input->keybit); + + /* Enable all needed keys */ + for (trans = gdium_fn_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); +} + +static int gdium_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) + && ((usage->hid & HID_USAGE) == 0x82)) { + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN); + gdium_input_setup(hi->input); + return 1; + } + return 0; +} + +static int gdium_input_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct gdium_data *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&hdev->dev, "can't alloc gdium keyboard data\n"); + return -ENOMEM; + } + + hid_set_drvdata(hdev, data); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + kfree(data); + return ret; +} +static void gdium_input_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); +} + +static const struct hid_device_id gdium_input_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GDIUM, USB_DEVICE_ID_GDIUM) }, + {} +}; +MODULE_DEVICE_TABLE(hid, gdium_input_devices); + +static struct hid_driver gdium_input_driver = { + .name = "gdium-fnkeys", + .id_table = gdium_input_devices, + .probe = gdium_input_probe, + .remove = gdium_input_remove, + .event = gdium_input_event, + .input_mapping = gdium_input_mapping, +}; + +static int gdium_input_init(void) +{ + int ret; + + ret = hid_register_driver(&gdium_input_driver); + if (ret) + pr_err("can't register gdium keyboard driver\n"); + + return ret; +} +static void gdium_input_exit(void) +{ + hid_unregister_driver(&gdium_input_driver); +} + +module_init(gdium_input_init); +module_exit(gdium_input_exit); +MODULE_LICENSE("GPL"); + diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7484e1b..7983a4a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -693,4 +693,6 @@ #define USB_VENDOR_ID_ZYDACRON 0x13EC #define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006 +#define USB_VENDOR_ID_GDIUM 0x04B4 +#define USB_DEVICE_ID_GDIUM 0xe001 #endif diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 646068e..f5873f3 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -867,7 +867,7 @@ config SCx200_I2C_SDA config SCx200_ACB tristate "Geode ACCESS.bus support" - depends on X86_32 && PCI + depends on PCI help Enable the use of the ACCESS.bus controllers on the Geode SCx200 and SC1100 processors and the CS5535 and CS5536 Geode companion devices. diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 376f2dc..b576801 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -27,6 +27,10 @@ #include #include +#ifdef CONFIG_LEMOTE_MACH2F +#include +#endif + void SELECT_MASK(ide_drive_t *drive, int mask) { const struct ide_port_ops *port_ops = drive->hwif->port_ops; @@ -300,6 +304,11 @@ void ide_check_nien_quirk_list(ide_drive_t *drive) { const char **list, *m = (char *)&drive->id[ATA_ID_PROD]; +#ifdef CONFIG_LEMOTE_MACH2F + if (mips_machtype != MACH_LEMOTE_YL2F89) + return; +#endif + for (list = nien_quirk_list; *list != NULL; list++) if (strstr(m, *list) != NULL) { drive->dev_flags |= IDE_DFLAG_NIEN_QUIRK; diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index df3702c..7242f3a 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -58,7 +58,7 @@ struct sm501_gpio { struct sm501_gpio { /* no gpio support, empty definition for sm501_devdata. */ }; -#endif +#endif /* CONFIG_MFD_SM501_GPIO */ struct sm501_devdata { spinlock_t reg_lock; @@ -1143,6 +1143,22 @@ static inline int sm501_gpio_isregistered(struct sm501_devdata *sm) { return sm->gpio.registered; } + +void sm501_configure_gpio(struct device *dev, unsigned int gpio, unsigned + char mode) +{ + unsigned long set, reg, offset = gpio; + + if (offset >= 32) { + reg = SM501_GPIO63_32_CONTROL; + offset = gpio - 32; + } else + reg = SM501_GPIO31_0_CONTROL; + + set = mode ? 1 << offset : 0; + + sm501_modify_reg(dev, reg, set, 0); +} #else static inline int sm501_register_gpio(struct sm501_devdata *sm) { @@ -1162,7 +1178,13 @@ static inline int sm501_gpio_isregistered(struct sm501_devdata *sm) { return 0; } -#endif + +void sm501_configure_gpio(struct device *dev, unsigned int gpio, + unsigned char mode) +{ +} +#endif /* CONFIG_MFD_SM501_GPIO */ +EXPORT_SYMBOL_GPL(sm501_configure_gpio); static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, struct sm501_platdata_gpio_i2c *iic) @@ -1217,6 +1239,20 @@ static int sm501_register_gpio_i2c(struct sm501_devdata *sm, return 0; } +/* register sm501 PWM device */ +static int sm501_register_pwm(struct sm501_devdata *sm) +{ + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "sm501-pwm", 2, 0); + if (!pdev) + return -ENOMEM; + sm501_create_subio(sm, &pdev->resource[0], 0x10020, 0xC); + sm501_create_irq(sm, &pdev->resource[1]); + + return sm501_register_device(sm, pdev); +} + /* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers @@ -1375,6 +1411,8 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm) sm501_register_uart(sm, idata->devices); if (idata->devices & SM501_USE_GPIO) sm501_register_gpio(sm); + if (idata->devices & SM501_USE_PWM) + sm501_register_pwm(sm); } if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) { @@ -1561,10 +1599,15 @@ static struct sm501_initdata sm501_pci_initdata = { .devices = SM501_USE_ALL, /* Errata AB-3 says that 72MHz is the fastest available - * for 33MHZ PCI with proper bus-mastering operation */ - + * for 33MHZ PCI with proper bus-mastering operation + * For gdium, it works under 84&112M clock freq.*/ +#ifdef CONFIG_DEXXON_GDIUM + .mclk = 84 * MHZ, + .m1xclk = 112 * MHZ, +#else .mclk = 72 * MHZ, .m1xclk = 144 * MHZ, +#endif }; static struct sm501_platdata_fbsub sm501_pdata_fbsub = { diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a44874e..874c90f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2458,6 +2458,13 @@ config MV643XX_ETH Some boards that use the Discovery chipset are the Momenco Ocelot C and Jaguar ATX and Pegasos II. +config TITAN_GE + bool "PMC-Sierra TITAN Gigabit Ethernet Support" + depends on PMC_YOSEMITE + help + This enables support for the the integrated ethernet of + PMC-Sierra's Titan SoC. + config XILINX_LL_TEMAC tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" depends on PPC || MICROBLAZE diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e1eca2a..e2b0b56 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -158,6 +158,8 @@ obj-$(CONFIG_QLA3XXX) += qla3xxx.o obj-$(CONFIG_QLCNIC) += qlcnic/ obj-$(CONFIG_QLGE) += qlge/ +obj-$(CONFIG_TITAN_GE) += titan_mdio.o titan_ge.o + obj-$(CONFIG_PPP) += ppp_generic.o obj-$(CONFIG_PPP_ASYNC) += ppp_async.o obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o diff --git a/drivers/net/titan_ge.c b/drivers/net/titan_ge.c new file mode 100644 index 0000000..dc137bf8 --- /dev/null +++ b/drivers/net/titan_ge.c @@ -0,0 +1,2069 @@ +/* + * drivers/net/titan_ge.c - Driver for Titan ethernet ports + * + * Copyright (C) 2003 PMC-Sierra Inc. + * Author : Manish Lachwani (lachwani@pmc-sierra.com) + * + * 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. + */ + +/* + * The MAC unit of the Titan consists of the following: + * + * -> XDMA Engine to move data to from the memory to the MAC packet FIFO + * -> FIFO is where the incoming and outgoing data is placed + * -> TRTG is the unit that pulls the data from the FIFO for Tx and pushes + * the data into the FIFO for Rx + * -> TMAC is the outgoing MAC interface and RMAC is the incoming. + * -> AFX is the address filtering block + * -> GMII block to communicate with the PHY + * + * Rx will look like the following: + * GMII --> RMAC --> AFX --> TRTG --> Rx FIFO --> XDMA --> CPU memory + * + * Tx will look like the following: + * CPU memory --> XDMA --> Tx FIFO --> TRTG --> TMAC --> GMII + * + * The Titan driver has support for the following performance features: + * -> Rx side checksumming + * -> Jumbo Frames + * -> Interrupt Coalscing + * -> Rx NAPI + * -> SKB Recycling + * -> Transmit/Receive descriptors in SRAM + * -> Fast routing for IP forwarding + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For MII specifc registers, titan_mdio.h should be included */ +#include + +#include +#include +#include +#include +#include +#include + +#include "titan_ge.h" +#include "titan_mdio.h" + +/* Static Function Declarations */ +static int titan_ge_eth_open(struct net_device *); +static void titan_ge_eth_stop(struct net_device *); +static struct net_device_stats *titan_ge_get_stats(struct net_device *); +static int titan_ge_init_rx_desc_ring(titan_ge_port_info *, int, int, + unsigned long, unsigned long, + unsigned long); +static int titan_ge_init_tx_desc_ring(titan_ge_port_info *, int, + unsigned long, unsigned long); + +static int titan_ge_open(struct net_device *); +static int titan_ge_start_xmit(struct sk_buff *, struct net_device *); +static int titan_ge_stop(struct net_device *); + +static unsigned long titan_ge_tx_coal(unsigned long, int); + +static void titan_ge_port_reset(unsigned int); +static int titan_ge_free_tx_queue(titan_ge_port_info *); +static int titan_ge_rx_task(struct net_device *, titan_ge_port_info *); +static int titan_ge_port_start(struct net_device *, titan_ge_port_info *); + +static int titan_ge_return_tx_desc(titan_ge_port_info *, int); + +/* + * Some configuration for the FIFO and the XDMA channel needs + * to be done only once for all the ports. This flag controls + * that + */ +static unsigned long config_done; + +/* + * One time out of memory flag + */ +static unsigned int oom_flag; + +static int titan_ge_poll(struct net_device *netdev, int *budget); + +static int titan_ge_receive_queue(struct net_device *, unsigned int); + +static struct platform_device *titan_ge_device[3]; + +/* MAC Address */ +extern unsigned char titan_ge_mac_addr_base[6]; + +unsigned long titan_ge_base; +static unsigned long titan_ge_sram; + +static char titan_string[] = "titan"; + +/* + * The Titan GE has two alignment requirements: + * -> skb->data to be cacheline aligned (32 byte) + * -> IP header alignment to 16 bytes + * + * The latter is not implemented. So, that results in an extra copy on + * the Rx. This is a big performance hog. For the former case, the + * dev_alloc_skb() has been replaced with titan_ge_alloc_skb(). The size + * requested is calculated: + * + * Ethernet Frame Size : 1518 + * Ethernet Header : 14 + * Future Titan change for IP header alignment : 2 + * + * Hence, we allocate (1518 + 14 + 2+ 64) = 1580 bytes. For IP header + * alignment, we use skb_reserve(). + */ + +#define ALIGNED_RX_SKB_ADDR(addr) \ + ((((unsigned long)(addr) + (64UL - 1UL)) \ + & ~(64UL - 1UL)) - (unsigned long)(addr)) + +#define titan_ge_alloc_skb(__length, __gfp_flags) \ +({ struct sk_buff *__skb; \ + __skb = alloc_skb((__length) + 64, (__gfp_flags)); \ + if(__skb) { \ + int __offset = (int) ALIGNED_RX_SKB_ADDR(__skb->data); \ + if(__offset) \ + skb_reserve(__skb, __offset); \ + } \ + __skb; \ +}) + +/* + * Configure the GMII block of the Titan based on what the PHY tells us + */ +static void titan_ge_gmii_config(int port_num) +{ + unsigned int reg_data = 0, phy_reg; + int err; + + err = titan_ge_mdio_read(port_num, TITAN_GE_MDIO_PHY_STATUS, &phy_reg); + + if (err == TITAN_GE_MDIO_ERROR) { + printk(KERN_ERR + "Could not read PHY control register 0x11 \n"); + printk(KERN_ERR + "Setting speed to 1000 Mbps and Duplex to Full \n"); + + return; + } + + err = titan_ge_mdio_write(port_num, TITAN_GE_MDIO_PHY_IE, 0); + + if (phy_reg & 0x8000) { + if (phy_reg & 0x2000) { + /* Full Duplex and 1000 Mbps */ + TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE + + (port_num << 12)), 0x201); + } else { + /* Half Duplex and 1000 Mbps */ + TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE + + (port_num << 12)), 0x2201); + } + } + if (phy_reg & 0x4000) { + if (phy_reg & 0x2000) { + /* Full Duplex and 100 Mbps */ + TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE + + (port_num << 12)), 0x100); + } else { + /* Half Duplex and 100 Mbps */ + TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE + + (port_num << 12)), 0x2100); + } + } + reg_data = TITAN_GE_READ(TITAN_GE_GMII_CONFIG_GENERAL + + (port_num << 12)); + reg_data |= 0x3; + TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_GENERAL + + (port_num << 12)), reg_data); +} + +/* + * Enable the TMAC if it is not + */ +static void titan_ge_enable_tx(unsigned int port_num) +{ + unsigned long reg_data; + + reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + (port_num << 12)); + if (!(reg_data & 0x8000)) { + printk("TMAC disabled for port %d!! \n", port_num); + + reg_data |= 0x0001; /* Enable TMAC */ + reg_data |= 0x4000; /* CRC Check Enable */ + reg_data |= 0x2000; /* Padding enable */ + reg_data |= 0x0800; /* CRC Add enable */ + reg_data |= 0x0080; /* PAUSE frame */ + + TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 + + (port_num << 12)), reg_data); + } +} + +/* + * Tx Timeout function + */ +static void titan_ge_tx_timeout(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + + printk(KERN_INFO "%s: TX timeout ", netdev->name); + printk(KERN_INFO "Resetting card \n"); + + /* Do the reset outside of interrupt context */ + schedule_work(&titan_ge_eth->tx_timeout_task); +} + +/* + * Update the AFX tables for UC and MC for slice 0 only + */ +static void titan_ge_update_afx(titan_ge_port_info * titan_ge_eth) +{ + int port = titan_ge_eth->port_num; + unsigned int i; + volatile unsigned long reg_data = 0; + u8 p_addr[6]; + + memcpy(p_addr, titan_ge_eth->port_mac_addr, 6); + + /* Set the MAC address here for TMAC and RMAC */ + TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_HI + (port << 12)), + ((p_addr[5] << 8) | p_addr[4])); + TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_MID + (port << 12)), + ((p_addr[3] << 8) | p_addr[2])); + TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_LOW + (port << 12)), + ((p_addr[1] << 8) | p_addr[0])); + + TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_HI + (port << 12)), + ((p_addr[5] << 8) | p_addr[4])); + TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_MID + (port << 12)), + ((p_addr[3] << 8) | p_addr[2])); + TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_LOW + (port << 12)), + ((p_addr[1] << 8) | p_addr[0])); + + TITAN_GE_WRITE((0x112c | (port << 12)), 0x1); + /* Configure the eight address filters */ + for (i = 0; i < 8; i++) { + /* Select each of the eight filters */ + TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_2 + + (port << 12)), i); + + /* Configure the match */ + reg_data = 0x9; /* Forward Enable Bit */ + TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_0 + + (port << 12)), reg_data); + + /* Finally, AFX Exact Match Address Registers */ + TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_LOW + (port << 12)), + ((p_addr[1] << 8) | p_addr[0])); + TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_MID + (port << 12)), + ((p_addr[3] << 8) | p_addr[2])); + TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_HIGH + (port << 12)), + ((p_addr[5] << 8) | p_addr[4])); + + /* VLAN id set to 0 */ + TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_VID + + (port << 12)), 0); + } +} + +/* + * Actual Routine to reset the adapter when the timeout occurred + */ +static void titan_ge_tx_timeout_task(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + int port = titan_ge_eth->port_num; + + printk("Titan GE: Transmit timed out. Resetting ... \n"); + + /* Dump debug info */ + printk(KERN_ERR "TRTG cause : %x \n", + TITAN_GE_READ(0x100c + (port << 12))); + + /* Fix this for the other ports */ + printk(KERN_ERR "FIFO cause : %x \n", TITAN_GE_READ(0x482c)); + printk(KERN_ERR "IE cause : %x \n", TITAN_GE_READ(0x0040)); + printk(KERN_ERR "XDMA GDI ERROR : %x \n", + TITAN_GE_READ(0x5008 + (port << 8))); + printk(KERN_ERR "CHANNEL ERROR: %x \n", + TITAN_GE_READ(TITAN_GE_CHANNEL0_INTERRUPT + + (port << 8))); + + netif_device_detach(netdev); + titan_ge_port_reset(titan_ge_eth->port_num); + titan_ge_port_start(netdev, titan_ge_eth); + netif_device_attach(netdev); +} + +/* + * Change the MTU of the Ethernet Device + */ +static int titan_ge_change_mtu(struct net_device *netdev, int new_mtu) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned long flags; + + if ((new_mtu > 9500) || (new_mtu < 64)) + return -EINVAL; + + spin_lock_irqsave(&titan_ge_eth->lock, flags); + + netdev->mtu = new_mtu; + + /* Now we have to reopen the interface so that SKBs with the new + * size will be allocated */ + + if (netif_running(netdev)) { + titan_ge_eth_stop(netdev); + + if (titan_ge_eth_open(netdev) != TITAN_OK) { + printk(KERN_ERR + "%s: Fatal error on opening device\n", + netdev->name); + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + return -1; + } + } + + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + return 0; +} + +/* + * Titan Gbe Interrupt Handler. All the three ports send interrupt to one line + * only. Once an interrupt is triggered, figure out the port and then check + * the channel. + */ +static irqreturn_t titan_ge_int_handler(int irq, void *dev_id) +{ + struct net_device *netdev = (struct net_device *) dev_id; + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + unsigned int reg_data; + unsigned int eth_int_cause_error = 0, is; + unsigned long eth_int_cause1; + int err = 0; +#ifdef CONFIG_SMP + unsigned long eth_int_cause2; +#endif + + /* Ack the CPU interrupt */ + switch (port_num) { + case 0: + is = OCD_READ(RM9000x2_OCD_INTP0STATUS1); + OCD_WRITE(RM9000x2_OCD_INTP0CLEAR1, is); + +#ifdef CONFIG_SMP + is = OCD_READ(RM9000x2_OCD_INTP1STATUS1); + OCD_WRITE(RM9000x2_OCD_INTP1CLEAR1, is); +#endif + break; + + case 1: + is = OCD_READ(RM9000x2_OCD_INTP0STATUS0); + OCD_WRITE(RM9000x2_OCD_INTP0CLEAR0, is); + +#ifdef CONFIG_SMP + is = OCD_READ(RM9000x2_OCD_INTP1STATUS0); + OCD_WRITE(RM9000x2_OCD_INTP1CLEAR0, is); +#endif + break; + + case 2: + is = OCD_READ(RM9000x2_OCD_INTP0STATUS4); + OCD_WRITE(RM9000x2_OCD_INTP0CLEAR4, is); + +#ifdef CONFIG_SMP + is = OCD_READ(RM9000x2_OCD_INTP1STATUS4); + OCD_WRITE(RM9000x2_OCD_INTP1CLEAR4, is); +#endif + } + + eth_int_cause1 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_A); +#ifdef CONFIG_SMP + eth_int_cause2 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_B); +#endif + + /* Spurious interrupt */ +#ifdef CONFIG_SMP + if ( (eth_int_cause1 == 0) && (eth_int_cause2 == 0)) { +#else + if (eth_int_cause1 == 0) { +#endif + eth_int_cause_error = TITAN_GE_READ(TITAN_GE_CHANNEL0_INTERRUPT + + (port_num << 8)); + + if (eth_int_cause_error == 0) + return IRQ_NONE; + } + + /* Handle Tx first. No need to ack interrupts */ +#ifdef CONFIG_SMP + if ( (eth_int_cause1 & 0x20202) || + (eth_int_cause2 & 0x20202) ) +#else + if (eth_int_cause1 & 0x20202) +#endif + titan_ge_free_tx_queue(titan_ge_eth); + + /* Handle the Rx next */ +#ifdef CONFIG_SMP + if ( (eth_int_cause1 & 0x10101) || + (eth_int_cause2 & 0x10101)) { +#else + if (eth_int_cause1 & 0x10101) { +#endif + if (netif_rx_schedule_prep(netdev)) { + unsigned int ack; + + ack = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE); + /* Disable Tx and Rx both */ + if (port_num == 0) + ack &= ~(0x3); + if (port_num == 1) + ack &= ~(0x300); + + if (port_num == 2) + ack &= ~(0x30000); + + /* Interrupts have been disabled */ + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, ack); + + __netif_rx_schedule(netdev); + } + } + + /* Handle error interrupts */ + if (eth_int_cause_error && (eth_int_cause_error != 0x2)) { + printk(KERN_ERR + "XDMA Channel Error : %x on port %d\n", + eth_int_cause_error, port_num); + + printk(KERN_ERR + "XDMA GDI Hardware error : %x on port %d\n", + TITAN_GE_READ(0x5008 + (port_num << 8)), port_num); + + printk(KERN_ERR + "XDMA currently has %d Rx descriptors \n", + TITAN_GE_READ(0x5048 + (port_num << 8))); + + printk(KERN_ERR + "XDMA currently has prefetcted %d Rx descriptors \n", + TITAN_GE_READ(0x505c + (port_num << 8))); + + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_INTERRUPT + + (port_num << 8)), eth_int_cause_error); + } + + /* + * PHY interrupt to inform abt the changes. Reading the + * PHY Status register will clear the interrupt + */ + if ((!(eth_int_cause1 & 0x30303)) && + (eth_int_cause_error == 0)) { + err = + titan_ge_mdio_read(port_num, + TITAN_GE_MDIO_PHY_IS, ®_data); + + if (reg_data & 0x0400) { + /* Link status change */ + titan_ge_mdio_read(port_num, + TITAN_GE_MDIO_PHY_STATUS, ®_data); + if (!(reg_data & 0x0400)) { + /* Link is down */ + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } else { + /* Link is up */ + netif_carrier_on(netdev); + netif_wake_queue(netdev); + + /* Enable the queue */ + titan_ge_enable_tx(port_num); + } + } + } + + return IRQ_HANDLED; +} + +/* + * Multicast and Promiscuous mode set. The + * set_multi entry point is called whenever the + * multicast address list or the network interface + * flags are updated. + */ +static void titan_ge_set_multi(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + unsigned long reg_data; + + reg_data = TITAN_GE_READ(TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 + + (port_num << 12)); + + if (netdev->flags & IFF_PROMISC) { + reg_data |= 0x2; + } + else if (netdev->flags & IFF_ALLMULTI) { + reg_data |= 0x01; + reg_data |= 0x400; /* Use the 64-bit Multicast Hash bin */ + } + else { + reg_data = 0x2; + } + + TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 + + (port_num << 12)), reg_data); + if (reg_data & 0x01) { + TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_LOW + + (port_num << 12)), 0xffff); + TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_MIDLOW + + (port_num << 12)), 0xffff); + TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_MIDHI + + (port_num << 12)), 0xffff); + TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_HI + + (port_num << 12)), 0xffff); + } +} + +/* + * Open the network device + */ +static int titan_ge_open(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + unsigned int irq = TITAN_ETH_PORT_IRQ - port_num; + int retval; + + retval = request_irq(irq, titan_ge_int_handler, + SA_INTERRUPT | SA_SAMPLE_RANDOM , netdev->name, netdev); + + if (retval != 0) { + printk(KERN_ERR "Cannot assign IRQ number to TITAN GE \n"); + return -1; + } + + netdev->irq = irq; + printk(KERN_INFO "Assigned IRQ %d to port %d\n", irq, port_num); + + spin_lock_irq(&(titan_ge_eth->lock)); + + if (titan_ge_eth_open(netdev) != TITAN_OK) { + spin_unlock_irq(&(titan_ge_eth->lock)); + printk("%s: Error opening interface \n", netdev->name); + free_irq(netdev->irq, netdev); + return -EBUSY; + } + + spin_unlock_irq(&(titan_ge_eth->lock)); + + return 0; +} + +/* + * Allocate the SKBs for the Rx ring. Also used + * for refilling the queue + */ +static int titan_ge_rx_task(struct net_device *netdev, + titan_ge_port_info *titan_ge_port) +{ + struct device *device = &titan_ge_device[titan_ge_port->port_num]->dev; + volatile titan_ge_rx_desc *rx_desc; + struct sk_buff *skb; + int rx_used_desc; + int count = 0; + + while (titan_ge_port->rx_ring_skbs < titan_ge_port->rx_ring_size) { + + /* First try to get the skb from the recycler */ +#ifdef TITAN_GE_JUMBO_FRAMES + skb = titan_ge_alloc_skb(TITAN_GE_JUMBO_BUFSIZE, GFP_ATOMIC); +#else + skb = titan_ge_alloc_skb(TITAN_GE_STD_BUFSIZE, GFP_ATOMIC); +#endif + if (unlikely(!skb)) { + /* OOM, set the flag */ + printk("OOM \n"); + oom_flag = 1; + break; + } + count++; + skb->dev = netdev; + + titan_ge_port->rx_ring_skbs++; + + rx_used_desc = titan_ge_port->rx_used_desc_q; + rx_desc = &(titan_ge_port->rx_desc_area[rx_used_desc]); + +#ifdef TITAN_GE_JUMBO_FRAMES + rx_desc->buffer_addr = dma_map_single(device, skb->data, + TITAN_GE_JUMBO_BUFSIZE - 2, DMA_FROM_DEVICE); +#else + rx_desc->buffer_addr = dma_map_single(device, skb->data, + TITAN_GE_STD_BUFSIZE - 2, DMA_FROM_DEVICE); +#endif + + titan_ge_port->rx_skb[rx_used_desc] = skb; + rx_desc->cmd_sts = TITAN_GE_RX_BUFFER_OWNED; + + titan_ge_port->rx_used_desc_q = + (rx_used_desc + 1) % TITAN_GE_RX_QUEUE; + } + + return count; +} + +/* + * Actual init of the Tital GE port. There is one register for + * the channel configuration + */ +static void titan_port_init(struct net_device *netdev, + titan_ge_port_info * titan_ge_eth) +{ + unsigned long reg_data; + + titan_ge_port_reset(titan_ge_eth->port_num); + + /* First reset the TMAC */ + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG); + reg_data |= 0x80000000; + TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data); + + udelay(30); + + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG); + reg_data &= ~(0xc0000000); + TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data); + + /* Now reset the RMAC */ + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG); + reg_data |= 0x00080000; + TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data); + + udelay(30); + + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG); + reg_data &= ~(0x000c0000); + TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data); +} + +/* + * Start the port. All the hardware specific configuration + * for the XDMA, Tx FIFO, Rx FIFO, TMAC, RMAC, TRTG and AFX + * go here + */ +static int titan_ge_port_start(struct net_device *netdev, + titan_ge_port_info * titan_port) +{ + volatile unsigned long reg_data, reg_data1; + int port_num = titan_port->port_num; + int count = 0; + unsigned long reg_data_1; + + if (config_done == 0) { + reg_data = TITAN_GE_READ(0x0004); + reg_data |= 0x100; + TITAN_GE_WRITE(0x0004, reg_data); + + reg_data &= ~(0x100); + TITAN_GE_WRITE(0x0004, reg_data); + + /* Turn on GMII/MII mode and turn off TBI mode */ + reg_data = TITAN_GE_READ(TITAN_GE_TSB_CTRL_1); + reg_data |= 0x00000700; + reg_data &= ~(0x00800000); /* Fencing */ + + TITAN_GE_WRITE(0x000c, 0x00001100); + + TITAN_GE_WRITE(TITAN_GE_TSB_CTRL_1, reg_data); + + /* Set the CPU Resource Limit register */ + TITAN_GE_WRITE(0x00f8, 0x8); + + /* Be conservative when using the BIU buffers */ + TITAN_GE_WRITE(0x0068, 0x4); + } + + titan_port->tx_threshold = 0; + titan_port->rx_threshold = 0; + + /* We need to write the descriptors for Tx and Rx */ + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_TX_DESC + (port_num << 8)), + (unsigned long) titan_port->tx_dma); + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_RX_DESC + (port_num << 8)), + (unsigned long) titan_port->rx_dma); + + if (config_done == 0) { + /* Step 1: XDMA config */ + reg_data = TITAN_GE_READ(TITAN_GE_XDMA_CONFIG); + reg_data &= ~(0x80000000); /* clear reset */ + reg_data |= 0x1 << 29; /* sparse tx descriptor spacing */ + reg_data |= 0x1 << 28; /* sparse rx descriptor spacing */ + reg_data |= (0x1 << 23) | (0x1 << 24); /* Descriptor Coherency */ + reg_data |= (0x1 << 21) | (0x1 << 22); /* Data Coherency */ + TITAN_GE_WRITE(TITAN_GE_XDMA_CONFIG, reg_data); + } + + /* IR register for the XDMA */ + reg_data = TITAN_GE_READ(TITAN_GE_GDI_INTERRUPT_ENABLE + (port_num << 8)); + reg_data |= 0x80068000; /* No Rx_OOD */ + TITAN_GE_WRITE((TITAN_GE_GDI_INTERRUPT_ENABLE + (port_num << 8)), reg_data); + + /* Start the Tx and Rx XDMA controller */ + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG + (port_num << 8)); + reg_data &= 0x4fffffff; /* Clear tx reset */ + reg_data &= 0xfff4ffff; /* Clear rx reset */ + +#ifdef TITAN_GE_JUMBO_FRAMES + reg_data |= 0xa0 | 0x30030000; +#else + reg_data |= 0x40 | 0x20030000; +#endif + +#ifndef CONFIG_SMP + reg_data &= ~(0x10); + reg_data |= 0x0f; /* All of the packet */ +#endif + + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG + (port_num << 8)), reg_data); + + /* Rx desc count */ + count = titan_ge_rx_task(netdev, titan_port); + TITAN_GE_WRITE((0x5048 + (port_num << 8)), count); + count = TITAN_GE_READ(0x5048 + (port_num << 8)); + + udelay(30); + + /* + * Step 2: Configure the SDQPF, i.e. FIFO + */ + if (config_done == 0) { + reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_CTL); + reg_data = 0x1; + TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data); + reg_data &= ~(0x1); + TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data); + reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_CTL); + TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data); + + reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_CTL); + reg_data = 0x1; + TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data); + reg_data &= ~(0x1); + TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data); + reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_CTL); + TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data); + } + /* + * Enable RX FIFO 0, 4 and 8 + */ + if (port_num == 0) { + reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_0); + + reg_data |= 0x100000; + reg_data |= (0xff << 10); + + TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_0, reg_data); + /* + * BAV2,BAV and DAV settings for the Rx FIFO + */ + reg_data1 = TITAN_GE_READ(0x4844); + reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1); + TITAN_GE_WRITE(0x4844, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_0, reg_data); + + reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_0); + reg_data |= 0x100000; + + TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data); + + reg_data |= (0xff << 10); + + TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data); + + /* + * BAV2, BAV and DAV settings for the Tx FIFO + */ + reg_data1 = TITAN_GE_READ(0x4944); + reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10); + + TITAN_GE_WRITE(0x4944, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data); + + } + + if (port_num == 1) { + reg_data = TITAN_GE_READ(0x4870); + + reg_data |= 0x100000; + reg_data |= (0xff << 10) | (0xff + 1); + + TITAN_GE_WRITE(0x4870, reg_data); + /* + * BAV2,BAV and DAV settings for the Rx FIFO + */ + reg_data1 = TITAN_GE_READ(0x4874); + reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1); + TITAN_GE_WRITE(0x4874, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(0x4870, reg_data); + + reg_data = TITAN_GE_READ(0x494c); + reg_data |= 0x100000; + + TITAN_GE_WRITE(0x494c, reg_data); + reg_data |= (0xff << 10) | (0xff + 1); + TITAN_GE_WRITE(0x494c, reg_data); + + /* + * BAV2, BAV and DAV settings for the Tx FIFO + */ + reg_data1 = TITAN_GE_READ(0x4950); + reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10); + + TITAN_GE_WRITE(0x4950, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(0x494c, reg_data); + } + + /* + * Titan 1.2 revision does support port #2 + */ + if (port_num == 2) { + /* + * Put the descriptors in the SRAM + */ + reg_data = TITAN_GE_READ(0x48a0); + + reg_data |= 0x100000; + reg_data |= (0xff << 10) | (2*(0xff + 1)); + + TITAN_GE_WRITE(0x48a0, reg_data); + /* + * BAV2,BAV and DAV settings for the Rx FIFO + */ + reg_data1 = TITAN_GE_READ(0x48a4); + reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1); + TITAN_GE_WRITE(0x48a4, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(0x48a0, reg_data); + + reg_data = TITAN_GE_READ(0x4958); + reg_data |= 0x100000; + + TITAN_GE_WRITE(0x4958, reg_data); + reg_data |= (0xff << 10) | (2*(0xff + 1)); + TITAN_GE_WRITE(0x4958, reg_data); + + /* + * BAV2, BAV and DAV settings for the Tx FIFO + */ + reg_data1 = TITAN_GE_READ(0x495c); + reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10); + + TITAN_GE_WRITE(0x495c, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(0x4958, reg_data); + } + + if (port_num == 2) { + reg_data = TITAN_GE_READ(0x48a0); + + reg_data |= 0x100000; + reg_data |= (0xff << 10) | (2*(0xff + 1)); + + TITAN_GE_WRITE(0x48a0, reg_data); + /* + * BAV2,BAV and DAV settings for the Rx FIFO + */ + reg_data1 = TITAN_GE_READ(0x48a4); + reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1); + TITAN_GE_WRITE(0x48a4, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(0x48a0, reg_data); + + reg_data = TITAN_GE_READ(0x4958); + reg_data |= 0x100000; + + TITAN_GE_WRITE(0x4958, reg_data); + reg_data |= (0xff << 10) | (2*(0xff + 1)); + TITAN_GE_WRITE(0x4958, reg_data); + + /* + * BAV2, BAV and DAV settings for the Tx FIFO + */ + reg_data1 = TITAN_GE_READ(0x495c); + reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10); + + TITAN_GE_WRITE(0x495c, reg_data1); + + reg_data &= ~(0x00100000); + reg_data |= 0x200000; + + TITAN_GE_WRITE(0x4958, reg_data); + } + + /* + * Step 3: TRTG block enable + */ + reg_data = TITAN_GE_READ(TITAN_GE_TRTG_CONFIG + (port_num << 12)); + + /* + * This is the 1.2 revision of the chip. It has fix for the + * IP header alignment. Now, the IP header begins at an + * aligned address and this wont need an extra copy in the + * driver. This performance drawback existed in the previous + * versions of the silicon + */ + reg_data_1 = TITAN_GE_READ(0x103c + (port_num << 12)); + reg_data_1 |= 0x40000000; + TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1); + + reg_data_1 |= 0x04000000; + TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1); + + mdelay(5); + + reg_data_1 &= ~(0x04000000); + TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1); + + mdelay(5); + + reg_data |= 0x0001; + TITAN_GE_WRITE((TITAN_GE_TRTG_CONFIG + (port_num << 12)), reg_data); + + /* + * Step 4: Start the Tx activity + */ + TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_2 + (port_num << 12)), 0xe197); +#ifdef TITAN_GE_JUMBO_FRAMES + TITAN_GE_WRITE((0x1258 + (port_num << 12)), 0x4000); +#endif + reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + (port_num << 12)); + reg_data |= 0x0001; /* Enable TMAC */ + reg_data |= 0x6c70; /* PAUSE also set */ + + TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 + (port_num << 12)), reg_data); + + udelay(30); + + /* Destination Address drop bit */ + reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_2 + (port_num << 12)); + reg_data |= 0x218; /* DA_DROP bit and pause */ + TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_2 + (port_num << 12)), reg_data); + + TITAN_GE_WRITE((0x1218 + (port_num << 12)), 0x3); + +#ifdef TITAN_GE_JUMBO_FRAMES + TITAN_GE_WRITE((0x1208 + (port_num << 12)), 0x4000); +#endif + /* Start the Rx activity */ + reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)); + reg_data |= 0x0001; /* RMAC Enable */ + reg_data |= 0x0010; /* CRC Check enable */ + reg_data |= 0x0040; /* Min Frame check enable */ + reg_data |= 0x4400; /* Max Frame check enable */ + + TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)), reg_data); + + udelay(30); + + /* + * Enable the Interrupts for Tx and Rx + */ + reg_data1 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE); + + if (port_num == 0) { + reg_data1 |= 0x3; +#ifdef CONFIG_SMP + TITAN_GE_WRITE(0x0038, 0x003); +#else + TITAN_GE_WRITE(0x0038, 0x303); +#endif + } + + if (port_num == 1) { + reg_data1 |= 0x300; + } + + if (port_num == 2) + reg_data1 |= 0x30000; + + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, reg_data1); + TITAN_GE_WRITE(0x003c, 0x300); + + if (config_done == 0) { + TITAN_GE_WRITE(0x0024, 0x04000024); /* IRQ vector */ + TITAN_GE_WRITE(0x0020, 0x000fb000); /* INTMSG base */ + } + + /* Priority */ + reg_data = TITAN_GE_READ(0x1038 + (port_num << 12)); + reg_data &= ~(0x00f00000); + TITAN_GE_WRITE((0x1038 + (port_num << 12)), reg_data); + + /* Step 5: GMII config */ + titan_ge_gmii_config(port_num); + + if (config_done == 0) { + TITAN_GE_WRITE(0x1a80, 0); + config_done = 1; + } + + return TITAN_OK; +} + +/* + * Function to queue the packet for the Ethernet device + */ +static void titan_ge_tx_queue(titan_ge_port_info * titan_ge_eth, + struct sk_buff * skb) +{ + struct device *device = &titan_ge_device[titan_ge_eth->port_num]->dev; + unsigned int curr_desc = titan_ge_eth->tx_curr_desc_q; + volatile titan_ge_tx_desc *tx_curr; + int port_num = titan_ge_eth->port_num; + + tx_curr = &(titan_ge_eth->tx_desc_area[curr_desc]); + tx_curr->buffer_addr = + dma_map_single(device, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + + titan_ge_eth->tx_skb[curr_desc] = (struct sk_buff *) skb; + tx_curr->buffer_len = skb_headlen(skb); + + /* Last descriptor enables interrupt and changes ownership */ + tx_curr->cmd_sts = 0x1 | (1 << 15) | (1 << 5); + + /* Kick the XDMA to start the transfer from memory to the FIFO */ + TITAN_GE_WRITE((0x5044 + (port_num << 8)), 0x1); + + /* Current descriptor updated */ + titan_ge_eth->tx_curr_desc_q = (curr_desc + 1) % TITAN_GE_TX_QUEUE; + + /* Prefetch the next descriptor */ + prefetch((const void *) + &titan_ge_eth->tx_desc_area[titan_ge_eth->tx_curr_desc_q]); +} + +/* + * Actually does the open of the Ethernet device + */ +static int titan_ge_eth_open(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + struct device *device = &titan_ge_device[port_num]->dev; + unsigned long reg_data; + unsigned int phy_reg; + int err = 0; + + /* Stop the Rx activity */ + reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)); + reg_data &= ~(0x00000001); + TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)), reg_data); + + /* Clear the port interrupts */ + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_INTERRUPT + (port_num << 8)), 0x0); + + if (config_done == 0) { + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0); + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_B, 0); + } + + /* Set the MAC Address */ + memcpy(titan_ge_eth->port_mac_addr, netdev->dev_addr, 6); + + if (config_done == 0) + titan_port_init(netdev, titan_ge_eth); + + titan_ge_update_afx(titan_ge_eth); + + /* Allocate the Tx ring now */ + titan_ge_eth->tx_ring_skbs = 0; + titan_ge_eth->tx_ring_size = TITAN_GE_TX_QUEUE; + + /* Allocate space in the SRAM for the descriptors */ + titan_ge_eth->tx_desc_area = (titan_ge_tx_desc *) + (titan_ge_sram + TITAN_TX_RING_BYTES * port_num); + titan_ge_eth->tx_dma = TITAN_SRAM_BASE + TITAN_TX_RING_BYTES * port_num; + + if (!titan_ge_eth->tx_desc_area) { + printk(KERN_ERR + "%s: Cannot allocate Tx Ring (size %d bytes) for port %d\n", + netdev->name, TITAN_TX_RING_BYTES, port_num); + return -ENOMEM; + } + + memset(titan_ge_eth->tx_desc_area, 0, titan_ge_eth->tx_desc_area_size); + + /* Now initialize the Tx descriptor ring */ + titan_ge_init_tx_desc_ring(titan_ge_eth, + titan_ge_eth->tx_ring_size, + (unsigned long) titan_ge_eth->tx_desc_area, + (unsigned long) titan_ge_eth->tx_dma); + + /* Allocate the Rx ring now */ + titan_ge_eth->rx_ring_size = TITAN_GE_RX_QUEUE; + titan_ge_eth->rx_ring_skbs = 0; + + titan_ge_eth->rx_desc_area = + (titan_ge_rx_desc *)(titan_ge_sram + 0x1000 + TITAN_RX_RING_BYTES * port_num); + + titan_ge_eth->rx_dma = TITAN_SRAM_BASE + 0x1000 + TITAN_RX_RING_BYTES * port_num; + + if (!titan_ge_eth->rx_desc_area) { + printk(KERN_ERR "%s: Cannot allocate Rx Ring (size %d bytes)\n", + netdev->name, TITAN_RX_RING_BYTES); + + printk(KERN_ERR "%s: Freeing previously allocated TX queues...", + netdev->name); + + dma_free_coherent(device, titan_ge_eth->tx_desc_area_size, + (void *) titan_ge_eth->tx_desc_area, + titan_ge_eth->tx_dma); + + return -ENOMEM; + } + + memset(titan_ge_eth->rx_desc_area, 0, titan_ge_eth->rx_desc_area_size); + + /* Now initialize the Rx ring */ +#ifdef TITAN_GE_JUMBO_FRAMES + if ((titan_ge_init_rx_desc_ring + (titan_ge_eth, titan_ge_eth->rx_ring_size, TITAN_GE_JUMBO_BUFSIZE, + (unsigned long) titan_ge_eth->rx_desc_area, 0, + (unsigned long) titan_ge_eth->rx_dma)) == 0) +#else + if ((titan_ge_init_rx_desc_ring + (titan_ge_eth, titan_ge_eth->rx_ring_size, TITAN_GE_STD_BUFSIZE, + (unsigned long) titan_ge_eth->rx_desc_area, 0, + (unsigned long) titan_ge_eth->rx_dma)) == 0) +#endif + panic("%s: Error initializing RX Ring\n", netdev->name); + + /* Fill the Rx ring with the SKBs */ + titan_ge_port_start(netdev, titan_ge_eth); + + /* + * Check if Interrupt Coalscing needs to be turned on. The + * values specified in the register is multiplied by + * (8 x 64 nanoseconds) to determine when an interrupt should + * be sent to the CPU. + */ + + if (TITAN_GE_TX_COAL) { + titan_ge_eth->tx_int_coal = + titan_ge_tx_coal(TITAN_GE_TX_COAL, port_num); + } + + err = titan_ge_mdio_read(port_num, TITAN_GE_MDIO_PHY_STATUS, &phy_reg); + if (err == TITAN_GE_MDIO_ERROR) { + printk(KERN_ERR + "Could not read PHY control register 0x11 \n"); + return TITAN_ERROR; + } + if (!(phy_reg & 0x0400)) { + netif_carrier_off(netdev); + netif_stop_queue(netdev); + return TITAN_ERROR; + } else { + netif_carrier_on(netdev); + netif_start_queue(netdev); + } + + return TITAN_OK; +} + +/* + * Queue the packet for Tx. Currently no support for zero copy, + * checksum offload and Scatter Gather. The chip does support + * Scatter Gather only. But, that wont help here since zero copy + * requires support for Tx checksumming also. + */ +int titan_ge_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned long flags; + struct net_device_stats *stats; +//printk("titan_ge_start_xmit\n"); + + stats = &titan_ge_eth->stats; + spin_lock_irqsave(&titan_ge_eth->lock, flags); + + if ((TITAN_GE_TX_QUEUE - titan_ge_eth->tx_ring_skbs) <= + (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(netdev); + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + printk(KERN_ERR "Tx OOD \n"); + return 1; + } + + titan_ge_tx_queue(titan_ge_eth, skb); + titan_ge_eth->tx_ring_skbs++; + + if (TITAN_GE_TX_QUEUE <= (titan_ge_eth->tx_ring_skbs + 4)) { + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + titan_ge_free_tx_queue(titan_ge_eth); + spin_lock_irqsave(&titan_ge_eth->lock, flags); + } + + stats->tx_bytes += skb->len; + stats->tx_packets++; + + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + + netdev->trans_start = jiffies; + + return 0; +} + +/* + * Actually does the Rx. Rx side checksumming supported. + */ +static int titan_ge_rx(struct net_device *netdev, int port_num, + titan_ge_port_info * titan_ge_port, + titan_ge_packet * packet) +{ + int rx_curr_desc, rx_used_desc; + volatile titan_ge_rx_desc *rx_desc; + + rx_curr_desc = titan_ge_port->rx_curr_desc_q; + rx_used_desc = titan_ge_port->rx_used_desc_q; + + if (((rx_curr_desc + 1) % TITAN_GE_RX_QUEUE) == rx_used_desc) + return TITAN_ERROR; + + rx_desc = &(titan_ge_port->rx_desc_area[rx_curr_desc]); + + if (rx_desc->cmd_sts & TITAN_GE_RX_BUFFER_OWNED) + return TITAN_ERROR; + + packet->skb = titan_ge_port->rx_skb[rx_curr_desc]; + packet->len = (rx_desc->cmd_sts & 0x7fff); + + /* + * At this point, we dont know if the checksumming + * actually helps relieve CPU. So, keep it for + * port 0 only + */ + packet->checksum = ntohs((rx_desc->buffer & 0xffff0000) >> 16); + packet->cmd_sts = rx_desc->cmd_sts; + + titan_ge_port->rx_curr_desc_q = (rx_curr_desc + 1) % TITAN_GE_RX_QUEUE; + + /* Prefetch the next descriptor */ + prefetch((const void *) + &titan_ge_port->rx_desc_area[titan_ge_port->rx_curr_desc_q + 1]); + + return TITAN_OK; +} + +/* + * Free the Tx queue of the used SKBs + */ +static int titan_ge_free_tx_queue(titan_ge_port_info *titan_ge_eth) +{ + unsigned long flags; + + /* Take the lock */ + spin_lock_irqsave(&(titan_ge_eth->lock), flags); + + while (titan_ge_return_tx_desc(titan_ge_eth, titan_ge_eth->port_num) == 0) + if (titan_ge_eth->tx_ring_skbs != 1) + titan_ge_eth->tx_ring_skbs--; + + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + + return TITAN_OK; +} + +/* + * Threshold beyond which we do the cleaning of + * Tx queue and new allocation for the Rx + * queue + */ +#define TX_THRESHOLD 4 +#define RX_THRESHOLD 10 + +/* + * Receive the packets and send it to the kernel. + */ +static int titan_ge_receive_queue(struct net_device *netdev, unsigned int max) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + titan_ge_packet packet; + struct net_device_stats *stats; + struct sk_buff *skb; + unsigned long received_packets = 0; + unsigned int ack; + + stats = &titan_ge_eth->stats; + + while ((--max) + && (titan_ge_rx(netdev, port_num, titan_ge_eth, &packet) == TITAN_OK)) { + skb = (struct sk_buff *) packet.skb; + + titan_ge_eth->rx_ring_skbs--; + + if (--titan_ge_eth->rx_work_limit < 0) + break; + received_packets++; + + stats->rx_packets++; + stats->rx_bytes += packet.len; + + if ((packet.cmd_sts & TITAN_GE_RX_PERR) || + (packet.cmd_sts & TITAN_GE_RX_OVERFLOW_ERROR) || + (packet.cmd_sts & TITAN_GE_RX_TRUNC) || + (packet.cmd_sts & TITAN_GE_RX_CRC_ERROR)) { + stats->rx_dropped++; + dev_kfree_skb_any(skb); + + continue; + } + /* + * Either support fast path or slow path. Decision + * making can really slow down the performance. The + * idea is to cut down the number of checks and improve + * the fastpath. + */ + + skb_put(skb, packet.len - 2); + + /* + * Increment data pointer by two since thats where + * the MAC starts + */ + skb_reserve(skb, 2); + skb->protocol = eth_type_trans(skb, netdev); + netif_receive_skb(skb); + + if (titan_ge_eth->rx_threshold > RX_THRESHOLD) { + ack = titan_ge_rx_task(netdev, titan_ge_eth); + TITAN_GE_WRITE((0x5048 + (port_num << 8)), ack); + titan_ge_eth->rx_threshold = 0; + } else + titan_ge_eth->rx_threshold++; + + if (titan_ge_eth->tx_threshold > TX_THRESHOLD) { + titan_ge_eth->tx_threshold = 0; + titan_ge_free_tx_queue(titan_ge_eth); + } + else + titan_ge_eth->tx_threshold++; + + } + return received_packets; +} + + +/* + * Enable the Rx side interrupts + */ +static void titan_ge_enable_int(unsigned int port_num, + titan_ge_port_info *titan_ge_eth, + struct net_device *netdev) +{ + unsigned long reg_data = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE); + + if (port_num == 0) + reg_data |= 0x3; + if (port_num == 1) + reg_data |= 0x300; + if (port_num == 2) + reg_data |= 0x30000; + + /* Re-enable interrupts */ + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, reg_data); +} + +/* + * Main function to handle the polling for Rx side NAPI. + * Receive interrupts have been disabled at this point. + * The poll schedules the transmit followed by receive. + */ +static int titan_ge_poll(struct net_device *netdev, int *budget) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + int port_num = titan_ge_eth->port_num; + int work_done = 0; + unsigned long flags, status; + + titan_ge_eth->rx_work_limit = *budget; + if (titan_ge_eth->rx_work_limit > netdev->quota) + titan_ge_eth->rx_work_limit = netdev->quota; + + do { + /* Do the transmit cleaning work here */ + titan_ge_free_tx_queue(titan_ge_eth); + + /* Ack the Rx interrupts */ + if (port_num == 0) + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x3); + if (port_num == 1) + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x300); + if (port_num == 2) + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x30000); + + work_done += titan_ge_receive_queue(netdev, 0); + + /* Out of quota and there is work to be done */ + if (titan_ge_eth->rx_work_limit < 0) + goto not_done; + + /* Receive alloc_skb could lead to OOM */ + if (oom_flag == 1) { + oom_flag = 0; + goto oom; + } + + status = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_A); + } while (status & 0x30300); + + /* If we are here, then no more interrupts to process */ + goto done; + +not_done: + *budget -= work_done; + netdev->quota -= work_done; + return 1; + +oom: + printk(KERN_ERR "OOM \n"); + netif_rx_complete(netdev); + return 0; + +done: + /* + * No more packets on the poll list. Turn the interrupts + * back on and we should be able to catch the new + * packets in the interrupt handler + */ + if (!work_done) + work_done = 1; + + *budget -= work_done; + netdev->quota -= work_done; + + spin_lock_irqsave(&titan_ge_eth->lock, flags); + + /* Remove us from the poll list */ + netif_rx_complete(netdev); + + /* Re-enable interrupts */ + titan_ge_enable_int(port_num, titan_ge_eth, netdev); + + spin_unlock_irqrestore(&titan_ge_eth->lock, flags); + + return 0; +} + +/* + * Close the network device + */ +int titan_ge_stop(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + + spin_lock_irq(&(titan_ge_eth->lock)); + titan_ge_eth_stop(netdev); + free_irq(netdev->irq, netdev); + spin_unlock_irq(&titan_ge_eth->lock); + + return TITAN_OK; +} + +/* + * Free the Tx ring + */ +static void titan_ge_free_tx_rings(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + unsigned int curr; + unsigned long reg_data; + + /* Stop the Tx DMA */ + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG + + (port_num << 8)); + reg_data |= 0xc0000000; + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG + + (port_num << 8)), reg_data); + + /* Disable the TMAC */ + reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + + (port_num << 12)); + reg_data &= ~(0x00000001); + TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 + + (port_num << 12)), reg_data); + + for (curr = 0; + (titan_ge_eth->tx_ring_skbs) && (curr < TITAN_GE_TX_QUEUE); + curr++) { + if (titan_ge_eth->tx_skb[curr]) { + dev_kfree_skb(titan_ge_eth->tx_skb[curr]); + titan_ge_eth->tx_ring_skbs--; + } + } + + if (titan_ge_eth->tx_ring_skbs != 0) + printk + ("%s: Error on Tx descriptor free - could not free %d" + " descriptors\n", netdev->name, + titan_ge_eth->tx_ring_skbs); + +#ifndef TITAN_RX_RING_IN_SRAM + dma_free_coherent(&titan_ge_device[port_num]->dev, + titan_ge_eth->tx_desc_area_size, + (void *) titan_ge_eth->tx_desc_area, + titan_ge_eth->tx_dma); +#endif +} + +/* + * Free the Rx ring + */ +static void titan_ge_free_rx_rings(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + unsigned int curr; + unsigned long reg_data; + + /* Stop the Rx DMA */ + reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG + + (port_num << 8)); + reg_data |= 0x000c0000; + TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG + + (port_num << 8)), reg_data); + + /* Disable the RMAC */ + reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + + (port_num << 12)); + reg_data &= ~(0x00000001); + TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + + (port_num << 12)), reg_data); + + for (curr = 0; + titan_ge_eth->rx_ring_skbs && (curr < TITAN_GE_RX_QUEUE); + curr++) { + if (titan_ge_eth->rx_skb[curr]) { + dev_kfree_skb(titan_ge_eth->rx_skb[curr]); + titan_ge_eth->rx_ring_skbs--; + } + } + + if (titan_ge_eth->rx_ring_skbs != 0) + printk(KERN_ERR + "%s: Error in freeing Rx Ring. %d skb's still" + " stuck in RX Ring - ignoring them\n", netdev->name, + titan_ge_eth->rx_ring_skbs); + +#ifndef TITAN_RX_RING_IN_SRAM + dma_free_coherent(&titan_ge_device[port_num]->dev, + titan_ge_eth->rx_desc_area_size, + (void *) titan_ge_eth->rx_desc_area, + titan_ge_eth->rx_dma); +#endif +} + +/* + * Actually does the stop of the Ethernet device + */ +static void titan_ge_eth_stop(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + + netif_stop_queue(netdev); + + titan_ge_port_reset(titan_ge_eth->port_num); + + titan_ge_free_tx_rings(netdev); + titan_ge_free_rx_rings(netdev); + + /* Disable the Tx and Rx Interrupts for all channels */ + TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, 0x0); +} + +/* + * Update the MAC address. Note that we have to write the + * address in three station registers, 16 bits each. And this + * has to be done for TMAC and RMAC + */ +static void titan_ge_update_mac_address(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + unsigned int port_num = titan_ge_eth->port_num; + u8 p_addr[6]; + + memcpy(titan_ge_eth->port_mac_addr, netdev->dev_addr, 6); + memcpy(p_addr, netdev->dev_addr, 6); + + /* Update the Address Filtering Match tables */ + titan_ge_update_afx(titan_ge_eth); + + printk("Station MAC : %d %d %d %d %d %d \n", + p_addr[5], p_addr[4], p_addr[3], + p_addr[2], p_addr[1], p_addr[0]); + + /* Set the MAC address here for TMAC and RMAC */ + TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_HI + (port_num << 12)), + ((p_addr[5] << 8) | p_addr[4])); + TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_MID + (port_num << 12)), + ((p_addr[3] << 8) | p_addr[2])); + TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_LOW + (port_num << 12)), + ((p_addr[1] << 8) | p_addr[0])); + + TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_HI + (port_num << 12)), + ((p_addr[5] << 8) | p_addr[4])); + TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_MID + (port_num << 12)), + ((p_addr[3] << 8) | p_addr[2])); + TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_LOW + (port_num << 12)), + ((p_addr[1] << 8) | p_addr[0])); +} + +/* + * Set the MAC address of the Ethernet device + */ +static int titan_ge_set_mac_address(struct net_device *dev, void *addr) +{ + titan_ge_port_info *tp = netdev_priv(dev); + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + spin_lock_irq(&tp->lock); + titan_ge_update_mac_address(dev); + spin_unlock_irq(&tp->lock); + + return 0; +} + +/* + * Get the Ethernet device stats + */ +static struct net_device_stats *titan_ge_get_stats(struct net_device *netdev) +{ + titan_ge_port_info *titan_ge_eth = netdev_priv(netdev); + + return &titan_ge_eth->stats; +} + +/* + * Initialize the Rx descriptor ring for the Titan Ge + */ +static int titan_ge_init_rx_desc_ring(titan_ge_port_info * titan_eth_port, + int rx_desc_num, + int rx_buff_size, + unsigned long rx_desc_base_addr, + unsigned long rx_buff_base_addr, + unsigned long rx_dma) +{ + volatile titan_ge_rx_desc *rx_desc; + unsigned long buffer_addr; + int index; + unsigned long titan_ge_rx_desc_bus = rx_dma; + + buffer_addr = rx_buff_base_addr; + rx_desc = (titan_ge_rx_desc *) rx_desc_base_addr; + + /* Check alignment */ + if (rx_buff_base_addr & 0xF) + return 0; + + /* Check Rx buffer size */ + if ((rx_buff_size < 8) || (rx_buff_size > TITAN_GE_MAX_RX_BUFFER)) + return 0; + + /* 64-bit alignment + if ((rx_buff_base_addr + rx_buff_size) & 0x7) + return 0; */ + + /* Initialize the Rx desc ring */ + for (index = 0; index < rx_desc_num; index++) { + titan_ge_rx_desc_bus += sizeof(titan_ge_rx_desc); + rx_desc[index].cmd_sts = 0; + rx_desc[index].buffer_addr = buffer_addr; + titan_eth_port->rx_skb[index] = NULL; + buffer_addr += rx_buff_size; + } + + titan_eth_port->rx_curr_desc_q = 0; + titan_eth_port->rx_used_desc_q = 0; + + titan_eth_port->rx_desc_area = (titan_ge_rx_desc *) rx_desc_base_addr; + titan_eth_port->rx_desc_area_size = + rx_desc_num * sizeof(titan_ge_rx_desc); + + titan_eth_port->rx_dma = rx_dma; + + return TITAN_OK; +} + +/* + * Initialize the Tx descriptor ring. Descriptors in the SRAM + */ +static int titan_ge_init_tx_desc_ring(titan_ge_port_info * titan_ge_port, + int tx_desc_num, + unsigned long tx_desc_base_addr, + unsigned long tx_dma) +{ + titan_ge_tx_desc *tx_desc; + int index; + unsigned long titan_ge_tx_desc_bus = tx_dma; + + if (tx_desc_base_addr & 0xF) + return 0; + + tx_desc = (titan_ge_tx_desc *) tx_desc_base_addr; + + for (index = 0; index < tx_desc_num; index++) { + titan_ge_port->tx_dma_array[index] = + (dma_addr_t) titan_ge_tx_desc_bus; + titan_ge_tx_desc_bus += sizeof(titan_ge_tx_desc); + tx_desc[index].cmd_sts = 0x0000; + tx_desc[index].buffer_len = 0; + tx_desc[index].buffer_addr = 0x00000000; + titan_ge_port->tx_skb[index] = NULL; + } + + titan_ge_port->tx_curr_desc_q = 0; + titan_ge_port->tx_used_desc_q = 0; + + titan_ge_port->tx_desc_area = (titan_ge_tx_desc *) tx_desc_base_addr; + titan_ge_port->tx_desc_area_size = + tx_desc_num * sizeof(titan_ge_tx_desc); + + titan_ge_port->tx_dma = tx_dma; + return TITAN_OK; +} + +/* + * Initialize the device as an Ethernet device + */ +static int __init titan_ge_probe(struct device *device) +{ + titan_ge_port_info *titan_ge_eth; + struct net_device *netdev; + int port = to_platform_device(device)->id; + int err; + + netdev = alloc_etherdev(sizeof(titan_ge_port_info)); + if (!netdev) { + err = -ENODEV; + goto out; + } + + netdev->open = titan_ge_open; + netdev->stop = titan_ge_stop; + netdev->hard_start_xmit = titan_ge_start_xmit; + netdev->get_stats = titan_ge_get_stats; + netdev->set_multicast_list = titan_ge_set_multi; + netdev->set_mac_address = titan_ge_set_mac_address; + + /* Tx timeout */ + netdev->tx_timeout = titan_ge_tx_timeout; + netdev->watchdog_timeo = 2 * HZ; + + /* Set these to very high values */ + netdev->poll = titan_ge_poll; + netdev->weight = 64; + + netdev->tx_queue_len = TITAN_GE_TX_QUEUE; + netif_carrier_off(netdev); + netdev->base_addr = 0; + + netdev->change_mtu = titan_ge_change_mtu; + + titan_ge_eth = netdev_priv(netdev); + /* Allocation of memory for the driver structures */ + + titan_ge_eth->port_num = port; + + /* Configure the Tx timeout handler */ + INIT_WORK(&titan_ge_eth->tx_timeout_task, + (void (*)(void *)) titan_ge_tx_timeout_task, netdev); + + spin_lock_init(&titan_ge_eth->lock); + + /* set MAC addresses */ + memcpy(netdev->dev_addr, titan_ge_mac_addr_base, 6); + netdev->dev_addr[5] += port; + + err = register_netdev(netdev); + + if (err) + goto out_free_netdev; + + printk(KERN_NOTICE + "%s: port %d with MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + netdev->name, port, netdev->dev_addr[0], + netdev->dev_addr[1], netdev->dev_addr[2], + netdev->dev_addr[3], netdev->dev_addr[4], + netdev->dev_addr[5]); + + printk(KERN_NOTICE "Rx NAPI supported, Tx Coalescing ON \n"); + + return 0; + +out_free_netdev: + kfree(netdev); + +out: + return err; +} + +static void __devexit titan_device_remove(struct device *device) +{ +} + +/* + * Reset the Ethernet port + */ +static void titan_ge_port_reset(unsigned int port_num) +{ + unsigned int reg_data; + + /* Stop the Tx port activity */ + reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + + (port_num << 12)); + reg_data &= ~(0x0001); + TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 + + (port_num << 12)), reg_data); + + /* Stop the Rx port activity */ + reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + + (port_num << 12)); + reg_data &= ~(0x0001); + TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + + (port_num << 12)), reg_data); + + return; +} + +/* + * Return the Tx desc after use by the XDMA + */ +static int titan_ge_return_tx_desc(titan_ge_port_info * titan_ge_eth, int port) +{ + int tx_desc_used; + struct sk_buff *skb; + + tx_desc_used = titan_ge_eth->tx_used_desc_q; + + /* return right away */ + if (tx_desc_used == titan_ge_eth->tx_curr_desc_q) + return TITAN_ERROR; + + /* Now the critical stuff */ + skb = titan_ge_eth->tx_skb[tx_desc_used]; + + dev_kfree_skb_any(skb); + + titan_ge_eth->tx_skb[tx_desc_used] = NULL; + titan_ge_eth->tx_used_desc_q = + (tx_desc_used + 1) % TITAN_GE_TX_QUEUE; + + return 0; +} + +/* + * Coalescing for the Tx path + */ +static unsigned long titan_ge_tx_coal(unsigned long delay, int port) +{ + unsigned long rx_delay; + + rx_delay = TITAN_GE_READ(TITAN_GE_INT_COALESCING); + delay = (delay << 16) | rx_delay; + + TITAN_GE_WRITE(TITAN_GE_INT_COALESCING, delay); + TITAN_GE_WRITE(0x5038, delay); + + return delay; +} + +static struct device_driver titan_soc_driver = { + .name = titan_string, + .bus = &platform_bus_type, + .probe = titan_ge_probe, + .remove = __devexit_p(titan_device_remove), +}; + +static void titan_platform_release (struct device *device) +{ + struct platform_device *pldev; + + /* free device */ + pldev = to_platform_device (device); + kfree (pldev); +} + +/* + * Register the Titan GE with the kernel + */ +static int __init titan_ge_init_module(void) +{ + struct platform_device *pldev; + unsigned int version, device; + int i; + + printk(KERN_NOTICE + "PMC-Sierra TITAN 10/100/1000 Ethernet Driver \n"); + + titan_ge_base = (unsigned long) ioremap(TITAN_GE_BASE, TITAN_GE_SIZE); + if (!titan_ge_base) { + printk("Mapping Titan GE failed\n"); + goto out; + } + + device = TITAN_GE_READ(TITAN_GE_DEVICE_ID); + version = (device & 0x000f0000) >> 16; + device &= 0x0000ffff; + + printk(KERN_NOTICE "Device Id : %x, Version : %x \n", device, version); + +#ifdef TITAN_RX_RING_IN_SRAM + titan_ge_sram = (unsigned long) ioremap(TITAN_SRAM_BASE, + TITAN_SRAM_SIZE); + if (!titan_ge_sram) { + printk("Mapping Titan SRAM failed\n"); + goto out_unmap_ge; + } +#endif + + if (driver_register(&titan_soc_driver)) { + printk(KERN_ERR "Driver registration failed\n"); + goto out_unmap_sram; + } + + for (i = 0; i < 3; i++) { + titan_ge_device[i] = NULL; + + if (!(pldev = kmalloc (sizeof (*pldev), GFP_KERNEL))) + continue; + + memset (pldev, 0, sizeof (*pldev)); + pldev->name = titan_string; + pldev->id = i; + pldev->dev.release = titan_platform_release; + titan_ge_device[i] = pldev; + + if (platform_device_register (pldev)) { + kfree (pldev); + titan_ge_device[i] = NULL; + continue; + } + + if (!pldev->dev.driver) { + /* + * The driver was not bound to this device, there was + * no hardware at this address. Unregister it, as the + * release fuction will take care of freeing the + * allocated structure + */ + titan_ge_device[i] = NULL; + platform_device_unregister (pldev); + } + } + + return 0; + +out_unmap_sram: + iounmap((void *)titan_ge_sram); + +out_unmap_ge: + iounmap((void *)titan_ge_base); + +out: + return -ENOMEM; +} + +/* + * Unregister the Titan GE from the kernel + */ +static void __exit titan_ge_cleanup_module(void) +{ + int i; + + driver_unregister(&titan_soc_driver); + + for (i = 0; i < 3; i++) { + if (titan_ge_device[i]) { + platform_device_unregister (titan_ge_device[i]); + titan_ge_device[i] = NULL; + } + } + + iounmap((void *)titan_ge_sram); + iounmap((void *)titan_ge_base); +} + +MODULE_AUTHOR("Manish Lachwani "); +MODULE_DESCRIPTION("Titan GE Ethernet driver"); +MODULE_LICENSE("GPL"); + +module_init(titan_ge_init_module); +module_exit(titan_ge_cleanup_module); diff --git a/drivers/net/titan_ge.h b/drivers/net/titan_ge.h new file mode 100644 index 0000000..3719f78 --- /dev/null +++ b/drivers/net/titan_ge.h @@ -0,0 +1,415 @@ +#ifndef _TITAN_GE_H_ +#define _TITAN_GE_H_ + +#include +#include +#include +#include + +/* + * These functions should be later moved to a more generic location since there + * will be others accessing it also + */ + +/* + * This is the way it works: LKB5 Base is at 0x0128. TITAN_BASE is defined in + * include/asm/titan_dep.h. TITAN_GE_BASE is the value in the TITAN_GE_LKB5 + * register. + */ + +#define TITAN_GE_BASE 0xfe000000UL +#define TITAN_GE_SIZE 0x10000UL + +extern unsigned long titan_ge_base; + +#define TITAN_GE_WRITE(offset, data) \ + *(volatile u32 *)(titan_ge_base + (offset)) = (data) + +#define TITAN_GE_READ(offset) *(volatile u32 *)(titan_ge_base + (offset)) + +#ifndef msec_delay +#define msec_delay(x) do { if(in_interrupt()) { \ + /* Don't mdelay in interrupt context! */ \ + BUG(); \ + } else { \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x * HZ)/1000); \ + } } while(0) +#endif + +#define TITAN_GE_PORT_0 + +#define TITAN_SRAM_BASE ((OCD_READ(RM9000x2_OCD_LKB13) & ~1) << 4) +#define TITAN_SRAM_SIZE 0x2000UL + +/* + * We may need these constants + */ +#define TITAN_BIT0 0x00000001 +#define TITAN_BIT1 0x00000002 +#define TITAN_BIT2 0x00000004 +#define TITAN_BIT3 0x00000008 +#define TITAN_BIT4 0x00000010 +#define TITAN_BIT5 0x00000020 +#define TITAN_BIT6 0x00000040 +#define TITAN_BIT7 0x00000080 +#define TITAN_BIT8 0x00000100 +#define TITAN_BIT9 0x00000200 +#define TITAN_BIT10 0x00000400 +#define TITAN_BIT11 0x00000800 +#define TITAN_BIT12 0x00001000 +#define TITAN_BIT13 0x00002000 +#define TITAN_BIT14 0x00004000 +#define TITAN_BIT15 0x00008000 +#define TITAN_BIT16 0x00010000 +#define TITAN_BIT17 0x00020000 +#define TITAN_BIT18 0x00040000 +#define TITAN_BIT19 0x00080000 +#define TITAN_BIT20 0x00100000 +#define TITAN_BIT21 0x00200000 +#define TITAN_BIT22 0x00400000 +#define TITAN_BIT23 0x00800000 +#define TITAN_BIT24 0x01000000 +#define TITAN_BIT25 0x02000000 +#define TITAN_BIT26 0x04000000 +#define TITAN_BIT27 0x08000000 +#define TITAN_BIT28 0x10000000 +#define TITAN_BIT29 0x20000000 +#define TITAN_BIT30 0x40000000 +#define TITAN_BIT31 0x80000000 + +/* Flow Control */ +#define TITAN_GE_FC_NONE 0x0 +#define TITAN_GE_FC_FULL 0x1 +#define TITAN_GE_FC_TX_PAUSE 0x2 +#define TITAN_GE_FC_RX_PAUSE 0x3 + +/* Duplex Settings */ +#define TITAN_GE_FULL_DUPLEX 0x1 +#define TITAN_GE_HALF_DUPLEX 0x2 + +/* Speed settings */ +#define TITAN_GE_SPEED_1000 0x1 +#define TITAN_GE_SPEED_100 0x2 +#define TITAN_GE_SPEED_10 0x3 + +/* Debugging info only */ +#undef TITAN_DEBUG + +/* Keep the rings in the Titan's SSRAM */ +#define TITAN_RX_RING_IN_SRAM + +#ifdef CONFIG_64BIT +#define TITAN_GE_IE_MASK 0xfffffffffb001b64 +#define TITAN_GE_IE_STATUS 0xfffffffffb001b60 +#else +#define TITAN_GE_IE_MASK 0xfb001b64 +#define TITAN_GE_IE_STATUS 0xfb001b60 +#endif + +/* Support for Jumbo Frames */ +#undef TITAN_GE_JUMBO_FRAMES + +/* Rx buffer size */ +#ifdef TITAN_GE_JUMBO_FRAMES +#define TITAN_GE_JUMBO_BUFSIZE 9080 +#else +#define TITAN_GE_STD_BUFSIZE 1580 +#endif + +/* + * Tx and Rx Interrupt Coalescing parameter. These values are + * for 1 Ghz processor. Rx coalescing can be taken care of + * by NAPI. NAPI is adaptive and hence useful. Tx coalescing + * is not adaptive. Hence, these values need to be adjusted + * based on load, CPU speed etc. + */ +#define TITAN_GE_RX_COAL 150 +#define TITAN_GE_TX_COAL 300 + +#if defined(__BIG_ENDIAN) + +/* Define the Rx descriptor */ +typedef struct eth_rx_desc { + u32 reserved; /* Unused */ + u32 buffer_addr; /* CPU buffer address */ + u32 cmd_sts; /* Command and Status */ + u32 buffer; /* XDMA buffer address */ +} titan_ge_rx_desc; + +/* Define the Tx descriptor */ +typedef struct eth_tx_desc { + u16 cmd_sts; /* Command, Status and Buffer count */ + u16 buffer_len; /* Length of the buffer */ + u32 buffer_addr; /* Physical address of the buffer */ +} titan_ge_tx_desc; + +#elif defined(__LITTLE_ENDIAN) + +/* Define the Rx descriptor */ +typedef struct eth_rx_desc { + u32 buffer_addr; /* CPU buffer address */ + u32 reserved; /* Unused */ + u32 buffer; /* XDMA buffer address */ + u32 cmd_sts; /* Command and Status */ +} titan_ge_rx_desc; + +/* Define the Tx descriptor */ +typedef struct eth_tx_desc { + u32 buffer_addr; /* Physical address of the buffer */ + u16 buffer_len; /* Length of the buffer */ + u16 cmd_sts; /* Command, Status and Buffer count */ +} titan_ge_tx_desc; +#endif + +/* Default Tx Queue Size */ +#define TITAN_GE_TX_QUEUE 128 +#define TITAN_TX_RING_BYTES (TITAN_GE_TX_QUEUE * sizeof(struct eth_tx_desc)) + +/* Default Rx Queue Size */ +#define TITAN_GE_RX_QUEUE 64 +#define TITAN_RX_RING_BYTES (TITAN_GE_RX_QUEUE * sizeof(struct eth_rx_desc)) + +/* Packet Structure */ +typedef struct _pkt_info { + unsigned int len; + unsigned int cmd_sts; + unsigned int buffer; + struct sk_buff *skb; + unsigned int checksum; +} titan_ge_packet; + + +#define PHYS_CNT 3 + +/* Titan Port specific data structure */ +typedef struct _eth_port_ctrl { + unsigned int port_num; + u8 port_mac_addr[6]; + + /* Rx descriptor pointers */ + int rx_curr_desc_q, rx_used_desc_q; + + /* Tx descriptor pointers */ + int tx_curr_desc_q, tx_used_desc_q; + + /* Rx descriptor area */ + volatile titan_ge_rx_desc *rx_desc_area; + unsigned int rx_desc_area_size; + struct sk_buff* rx_skb[TITAN_GE_RX_QUEUE]; + + /* Tx Descriptor area */ + volatile titan_ge_tx_desc *tx_desc_area; + unsigned int tx_desc_area_size; + struct sk_buff* tx_skb[TITAN_GE_TX_QUEUE]; + + /* Timeout task */ + struct work_struct tx_timeout_task; + + /* DMA structures and handles */ + dma_addr_t tx_dma; + dma_addr_t rx_dma; + dma_addr_t tx_dma_array[TITAN_GE_TX_QUEUE]; + + /* Device lock */ + spinlock_t lock; + + unsigned int tx_ring_skbs; + unsigned int rx_ring_size; + unsigned int tx_ring_size; + unsigned int rx_ring_skbs; + + struct net_device_stats stats; + + /* Tx and Rx coalescing */ + unsigned long rx_int_coal; + unsigned long tx_int_coal; + + /* Threshold for replenishing the Rx and Tx rings */ + unsigned int tx_threshold; + unsigned int rx_threshold; + + /* NAPI work limit */ + unsigned int rx_work_limit; +} titan_ge_port_info; + +/* Titan specific constants */ +#define TITAN_ETH_PORT_IRQ 3 + +/* Max Rx buffer */ +#define TITAN_GE_MAX_RX_BUFFER 65536 + +/* Tx and Rx Error */ +#define TITAN_GE_ERROR + +/* Rx Descriptor Command and Status */ + +#define TITAN_GE_RX_CRC_ERROR TITAN_BIT27 /* crc error */ +#define TITAN_GE_RX_OVERFLOW_ERROR TITAN_BIT15 /* overflow */ +#define TITAN_GE_RX_BUFFER_OWNED TITAN_BIT21 /* buffer ownership */ +#define TITAN_GE_RX_STP TITAN_BIT31 /* start of packet */ +#define TITAN_GE_RX_BAM TITAN_BIT30 /* broadcast address match */ +#define TITAN_GE_RX_PAM TITAN_BIT28 /* physical address match */ +#define TITAN_GE_RX_LAFM TITAN_BIT29 /* logical address filter match */ +#define TITAN_GE_RX_VLAN TITAN_BIT26 /* virtual lans */ +#define TITAN_GE_RX_PERR TITAN_BIT19 /* packet error */ +#define TITAN_GE_RX_TRUNC TITAN_BIT20 /* packet size greater than 32 buffers */ + +/* Tx Descriptor Command */ +#define TITAN_GE_TX_BUFFER_OWNED TITAN_BIT5 /* buffer ownership */ +#define TITAN_GE_TX_ENABLE_INTERRUPT TITAN_BIT15 /* Interrupt Enable */ + +/* Return Status */ +#define TITAN_OK 0x1 /* Good Status */ +#define TITAN_ERROR 0x2 /* Error Status */ + +/* MIB specific register offset */ +#define TITAN_GE_MSTATX_STATS_BASE_LOW 0x0800 /* MSTATX COUNTL[15:0] */ +#define TITAN_GE_MSTATX_STATS_BASE_MID 0x0804 /* MSTATX COUNTM[15:0] */ +#define TITAN_GE_MSTATX_STATS_BASE_HI 0x0808 /* MSTATX COUNTH[7:0] */ +#define TITAN_GE_MSTATX_CONTROL 0x0828 /* MSTATX Control */ +#define TITAN_GE_MSTATX_VARIABLE_SELECT 0x082C /* MSTATX Variable Select */ + +/* MIB counter offsets, add to the TITAN_GE_MSTATX_STATS_BASE_XXX */ +#define TITAN_GE_MSTATX_RXFRAMESOK 0x0040 +#define TITAN_GE_MSTATX_RXOCTETSOK 0x0050 +#define TITAN_GE_MSTATX_RXFRAMES 0x0060 +#define TITAN_GE_MSTATX_RXOCTETS 0x0070 +#define TITAN_GE_MSTATX_RXUNICASTFRAMESOK 0x0080 +#define TITAN_GE_MSTATX_RXBROADCASTFRAMESOK 0x0090 +#define TITAN_GE_MSTATX_RXMULTICASTFRAMESOK 0x00A0 +#define TITAN_GE_MSTATX_RXTAGGEDFRAMESOK 0x00B0 +#define TITAN_GE_MSTATX_RXMACPAUSECONTROLFRAMESOK 0x00C0 +#define TITAN_GE_MSTATX_RXMACCONTROLFRAMESOK 0x00D0 +#define TITAN_GE_MSTATX_RXFCSERROR 0x00E0 +#define TITAN_GE_MSTATX_RXALIGNMENTERROR 0x00F0 +#define TITAN_GE_MSTATX_RXSYMBOLERROR 0x0100 +#define TITAN_GE_MSTATX_RXLAYER1ERROR 0x0110 +#define TITAN_GE_MSTATX_RXINRANGELENGTHERROR 0x0120 +#define TITAN_GE_MSTATX_RXLONGLENGTHERROR 0x0130 +#define TITAN_GE_MSTATX_RXLONGLENGTHCRCERROR 0x0140 +#define TITAN_GE_MSTATX_RXSHORTLENGTHERROR 0x0150 +#define TITAN_GE_MSTATX_RXSHORTLLENGTHCRCERROR 0x0160 +#define TITAN_GE_MSTATX_RXFRAMES64OCTETS 0x0170 +#define TITAN_GE_MSTATX_RXFRAMES65TO127OCTETS 0x0180 +#define TITAN_GE_MSTATX_RXFRAMES128TO255OCTETS 0x0190 +#define TITAN_GE_MSTATX_RXFRAMES256TO511OCTETS 0x01A0 +#define TITAN_GE_MSTATX_RXFRAMES512TO1023OCTETS 0x01B0 +#define TITAN_GE_MSTATX_RXFRAMES1024TO1518OCTETS 0x01C0 +#define TITAN_GE_MSTATX_RXFRAMES1519TOMAXSIZE 0x01D0 +#define TITAN_GE_MSTATX_RXSTATIONADDRESSFILTERED 0x01E0 +#define TITAN_GE_MSTATX_RXVARIABLE 0x01F0 +#define TITAN_GE_MSTATX_GENERICADDRESSFILTERED 0x0200 +#define TITAN_GE_MSTATX_UNICASTFILTERED 0x0210 +#define TITAN_GE_MSTATX_MULTICASTFILTERED 0x0220 +#define TITAN_GE_MSTATX_BROADCASTFILTERED 0x0230 +#define TITAN_GE_MSTATX_HASHFILTERED 0x0240 +#define TITAN_GE_MSTATX_TXFRAMESOK 0x0250 +#define TITAN_GE_MSTATX_TXOCTETSOK 0x0260 +#define TITAN_GE_MSTATX_TXOCTETS 0x0270 +#define TITAN_GE_MSTATX_TXTAGGEDFRAMESOK 0x0280 +#define TITAN_GE_MSTATX_TXMACPAUSECONTROLFRAMESOK 0x0290 +#define TITAN_GE_MSTATX_TXFCSERROR 0x02A0 +#define TITAN_GE_MSTATX_TXSHORTLENGTHERROR 0x02B0 +#define TITAN_GE_MSTATX_TXLONGLENGTHERROR 0x02C0 +#define TITAN_GE_MSTATX_TXSYSTEMERROR 0x02D0 +#define TITAN_GE_MSTATX_TXMACERROR 0x02E0 +#define TITAN_GE_MSTATX_TXCARRIERSENSEERROR 0x02F0 +#define TITAN_GE_MSTATX_TXSQETESTERROR 0x0300 +#define TITAN_GE_MSTATX_TXUNICASTFRAMESOK 0x0310 +#define TITAN_GE_MSTATX_TXBROADCASTFRAMESOK 0x0320 +#define TITAN_GE_MSTATX_TXMULTICASTFRAMESOK 0x0330 +#define TITAN_GE_MSTATX_TXUNICASTFRAMESATTEMPTED 0x0340 +#define TITAN_GE_MSTATX_TXBROADCASTFRAMESATTEMPTED 0x0350 +#define TITAN_GE_MSTATX_TXMULTICASTFRAMESATTEMPTED 0x0360 +#define TITAN_GE_MSTATX_TXFRAMES64OCTETS 0x0370 +#define TITAN_GE_MSTATX_TXFRAMES65TO127OCTETS 0x0380 +#define TITAN_GE_MSTATX_TXFRAMES128TO255OCTETS 0x0390 +#define TITAN_GE_MSTATX_TXFRAMES256TO511OCTETS 0x03A0 +#define TITAN_GE_MSTATX_TXFRAMES512TO1023OCTETS 0x03B0 +#define TITAN_GE_MSTATX_TXFRAMES1024TO1518OCTETS 0x03C0 +#define TITAN_GE_MSTATX_TXFRAMES1519TOMAXSIZE 0x03D0 +#define TITAN_GE_MSTATX_TXVARIABLE 0x03E0 +#define TITAN_GE_MSTATX_RXSYSTEMERROR 0x03F0 +#define TITAN_GE_MSTATX_SINGLECOLLISION 0x0400 +#define TITAN_GE_MSTATX_MULTIPLECOLLISION 0x0410 +#define TITAN_GE_MSTATX_DEFERREDXMISSIONS 0x0420 +#define TITAN_GE_MSTATX_LATECOLLISIONS 0x0430 +#define TITAN_GE_MSTATX_ABORTEDDUETOXSCOLLS 0x0440 + +/* Interrupt specific defines */ +#define TITAN_GE_DEVICE_ID 0x0000 /* Device ID */ +#define TITAN_GE_RESET 0x0004 /* Reset reg */ +#define TITAN_GE_TSB_CTRL_0 0x000C /* TSB Control reg 0 */ +#define TITAN_GE_TSB_CTRL_1 0x0010 /* TSB Control reg 1 */ +#define TITAN_GE_INTR_GRP0_STATUS 0x0040 /* General Interrupt Group 0 Status */ +#define TITAN_GE_INTR_XDMA_CORE_A 0x0048 /* XDMA Channel Interrupt Status, Core A*/ +#define TITAN_GE_INTR_XDMA_CORE_B 0x004C /* XDMA Channel Interrupt Status, Core B*/ +#define TITAN_GE_INTR_XDMA_IE 0x0058 /* XDMA Channel Interrupt Enable */ +#define TITAN_GE_SDQPF_ECC_INTR 0x480C /* SDQPF ECC Interrupt Status */ +#define TITAN_GE_SDQPF_RXFIFO_CTL 0x4828 /* SDQPF RxFifo Control and Interrupt Enb*/ +#define TITAN_GE_SDQPF_RXFIFO_INTR 0x482C /* SDQPF RxFifo Interrupt Status */ +#define TITAN_GE_SDQPF_TXFIFO_CTL 0x4928 /* SDQPF TxFifo Control and Interrupt Enb*/ +#define TITAN_GE_SDQPF_TXFIFO_INTR 0x492C /* SDQPF TxFifo Interrupt Status */ +#define TITAN_GE_SDQPF_RXFIFO_0 0x4840 /* SDQPF RxFIFO Enable */ +#define TITAN_GE_SDQPF_TXFIFO_0 0x4940 /* SDQPF TxFIFO Enable */ +#define TITAN_GE_XDMA_CONFIG 0x5000 /* XDMA Global Configuration */ +#define TITAN_GE_XDMA_INTR_SUMMARY 0x5010 /* XDMA Interrupt Summary */ +#define TITAN_GE_XDMA_BUFADDRPRE 0x5018 /* XDMA Buffer Address Prefix */ +#define TITAN_GE_XDMA_DESCADDRPRE 0x501C /* XDMA Descriptor Address Prefix */ +#define TITAN_GE_XDMA_PORTWEIGHT 0x502C /* XDMA Port Weight Configuration */ + +/* Rx MAC defines */ +#define TITAN_GE_RMAC_CONFIG_1 0x1200 /* RMAC Configuration 1 */ +#define TITAN_GE_RMAC_CONFIG_2 0x1204 /* RMAC Configuration 2 */ +#define TITAN_GE_RMAC_MAX_FRAME_LEN 0x1208 /* RMAC Max Frame Length */ +#define TITAN_GE_RMAC_STATION_HI 0x120C /* Rx Station Address High */ +#define TITAN_GE_RMAC_STATION_MID 0x1210 /* Rx Station Address Middle */ +#define TITAN_GE_RMAC_STATION_LOW 0x1214 /* Rx Station Address Low */ +#define TITAN_GE_RMAC_LINK_CONFIG 0x1218 /* RMAC Link Configuration */ + +/* Tx MAC defines */ +#define TITAN_GE_TMAC_CONFIG_1 0x1240 /* TMAC Configuration 1 */ +#define TITAN_GE_TMAC_CONFIG_2 0x1244 /* TMAC Configuration 2 */ +#define TITAN_GE_TMAC_IPG 0x1248 /* TMAC Inter-Packet Gap */ +#define TITAN_GE_TMAC_STATION_HI 0x124C /* Tx Station Address High */ +#define TITAN_GE_TMAC_STATION_MID 0x1250 /* Tx Station Address Middle */ +#define TITAN_GE_TMAC_STATION_LOW 0x1254 /* Tx Station Address Low */ +#define TITAN_GE_TMAC_MAX_FRAME_LEN 0x1258 /* TMAC Max Frame Length */ +#define TITAN_GE_TMAC_MIN_FRAME_LEN 0x125C /* TMAC Min Frame Length */ +#define TITAN_GE_TMAC_PAUSE_FRAME_TIME 0x1260 /* TMAC Pause Frame Time */ +#define TITAN_GE_TMAC_PAUSE_FRAME_INTERVAL 0x1264 /* TMAC Pause Frame Interval */ + +/* GMII register */ +#define TITAN_GE_GMII_INTERRUPT_STATUS 0x1348 /* GMII Interrupt Status */ +#define TITAN_GE_GMII_CONFIG_GENERAL 0x134C /* GMII Configuration General */ +#define TITAN_GE_GMII_CONFIG_MODE 0x1350 /* GMII Configuration Mode */ + +/* Tx and Rx XDMA defines */ +#define TITAN_GE_INT_COALESCING 0x5030 /* Interrupt Coalescing */ +#define TITAN_GE_CHANNEL0_CONFIG 0x5040 /* Channel 0 XDMA config */ +#define TITAN_GE_CHANNEL0_INTERRUPT 0x504c /* Channel 0 Interrupt Status */ +#define TITAN_GE_GDI_INTERRUPT_ENABLE 0x5050 /* IE for the GDI Errors */ +#define TITAN_GE_CHANNEL0_PACKET 0x5060 /* Channel 0 Packet count */ +#define TITAN_GE_CHANNEL0_BYTE 0x5064 /* Channel 0 Byte count */ +#define TITAN_GE_CHANNEL0_TX_DESC 0x5054 /* Channel 0 Tx first desc */ +#define TITAN_GE_CHANNEL0_RX_DESC 0x5058 /* Channel 0 Rx first desc */ + +/* AFX (Address Filter Exact) register offsets for Slice 0 */ +#define TITAN_GE_AFX_EXACT_MATCH_LOW 0x1100 /* AFX Exact Match Address Low*/ +#define TITAN_GE_AFX_EXACT_MATCH_MID 0x1104 /* AFX Exact Match Address Mid*/ +#define TITAN_GE_AFX_EXACT_MATCH_HIGH 0x1108 /* AFX Exact Match Address Hi */ +#define TITAN_GE_AFX_EXACT_MATCH_VID 0x110C /* AFX Exact Match VID */ +#define TITAN_GE_AFX_MULTICAST_HASH_LOW 0x1110 /* AFX Multicast HASH Low */ +#define TITAN_GE_AFX_MULTICAST_HASH_MIDLOW 0x1114 /* AFX Multicast HASH MidLow */ +#define TITAN_GE_AFX_MULTICAST_HASH_MIDHI 0x1118 /* AFX Multicast HASH MidHi */ +#define TITAN_GE_AFX_MULTICAST_HASH_HI 0x111C /* AFX Multicast HASH Hi */ +#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_0 0x1120 /* AFX Address Filter Ctrl 0 */ +#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 0x1124 /* AFX Address Filter Ctrl 1 */ +#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_2 0x1128 /* AFX Address Filter Ctrl 2 */ + +/* Traffic Groomer block */ +#define TITAN_GE_TRTG_CONFIG 0x1000 /* TRTG Config */ + +#endif /* _TITAN_GE_H_ */ + diff --git a/drivers/net/titan_mdio.c b/drivers/net/titan_mdio.c new file mode 100644 index 0000000..8a8785b --- /dev/null +++ b/drivers/net/titan_mdio.c @@ -0,0 +1,217 @@ +/* + * drivers/net/titan_mdio.c - Driver for Titan ethernet ports + * + * Copyright (C) 2003 PMC-Sierra Inc. + * Author : Manish Lachwani (lachwani@pmc-sierra.com) + * + * 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. + * + * Management Data IO (MDIO) driver for the Titan GMII. Interacts with the Marvel PHY + * on the Titan. No support for the TBI as yet. + * + */ + +#include "titan_mdio.h" + +#define MDIO_DEBUG + +/* + * Local constants + */ +#define MAX_CLKA 1023 +#define MAX_PHY_DEV 31 +#define MAX_PHY_REG 31 +#define WRITEADDRS_OPCODE 0x0 +#define READ_OPCODE 0x2 +#define WRITE_OPCODE 0x1 +#define MAX_MDIO_POLL 100 + +/* + * Titan MDIO and SCMB registers + */ +#define TITAN_GE_SCMB_CONTROL 0x01c0 /* SCMB Control */ +#define TITAN_GE_SCMB_CLKA 0x01c4 /* SCMB Clock A */ +#define TITAN_GE_MDIO_COMMAND 0x01d0 /* MDIO Command */ +#define TITAN_GE_MDIO_DEVICE_PORT_ADDRESS 0x01d4 /* MDIO Device and Port addrs */ +#define TITAN_GE_MDIO_DATA 0x01d8 /* MDIO Data */ +#define TITAN_GE_MDIO_INTERRUPTS 0x01dC /* MDIO Interrupts */ + +/* + * Function to poll the MDIO + */ +static int titan_ge_mdio_poll(void) +{ + int i, val; + + for (i = 0; i < MAX_MDIO_POLL; i++) { + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); + + if (!(val & 0x8000)) + return TITAN_GE_MDIO_GOOD; + } + + return TITAN_GE_MDIO_ERROR; +} + + +/* + * Initialize and configure the MDIO + */ +int titan_ge_mdio_setup(titan_ge_mdio_config *titan_mdio) +{ + unsigned long val; + + /* Reset the SCMB and program into MDIO mode*/ + TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x9000); + TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x1000); + + /* CLK A */ + val = TITAN_GE_MDIO_READ(TITAN_GE_SCMB_CLKA); + val = ( (val & ~(0x03ff)) | (titan_mdio->clka & 0x03ff)); + TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CLKA, val); + + /* Preamble Suppresion */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); + val = ( (val & ~(0x0001)) | (titan_mdio->mdio_spre & 0x0001)); + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); + + /* MDIO mode */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); + val = ( (val & ~(0x4000)) | (titan_mdio->mdio_mode & 0x4000)); + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); + + return TITAN_GE_MDIO_GOOD; +} + +/* + * Set the PHY address in indirect mode + */ +int titan_ge_mdio_inaddrs(int dev_addr, int reg_addr) +{ + volatile unsigned long val; + + /* Setup the PHY device */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); + val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00)); + val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f)); + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); + + /* Write the new address */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); + val = ( (val & ~(0x0300)) | ( (WRITEADDRS_OPCODE << 8) & 0x0300)); + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); + + return TITAN_GE_MDIO_GOOD; +} + +/* + * Read the MDIO register. This is what the individual parametes mean: + * + * dev_addr : PHY ID + * reg_addr : register offset + * + * See the spec for the Titan MAC. We operate in the Direct Mode. + */ + +#define MAX_RETRIES 2 + +int titan_ge_mdio_read(int dev_addr, int reg_addr, unsigned int *pdata) +{ + volatile unsigned long val; + int retries = 0; + + /* Setup the PHY device */ + +again: + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); + val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00)); + val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f)); + val |= 0x4000; + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); + + udelay(30); + + /* Issue the read command */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); + val = ( (val & ~(0x0300)) | ( (READ_OPCODE << 8) & 0x0300)); + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); + + udelay(30); + + if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD) + return TITAN_GE_MDIO_ERROR; + + *pdata = (unsigned int)TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DATA); + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS); + + udelay(30); + + if (val & 0x2) { + if (retries == MAX_RETRIES) + return TITAN_GE_MDIO_ERROR; + else { + retries++; + goto again; + } + } + + return TITAN_GE_MDIO_GOOD; +} + +/* + * Write to the MDIO register + * + * dev_addr : PHY ID + * reg_addr : register that needs to be written to + * + */ +int titan_ge_mdio_write(int dev_addr, int reg_addr, unsigned int data) +{ + volatile unsigned long val; + + if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD) + return TITAN_GE_MDIO_ERROR; + + /* Setup the PHY device */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); + val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00)); + val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f)); + val |= 0x4000; + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); + + udelay(30); + + /* Setup the data to write */ + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DATA, data); + + udelay(30); + + /* Issue the write command */ + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); + val = ( (val & ~(0x0300)) | ( (WRITE_OPCODE << 8) & 0x0300)); + TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); + + udelay(30); + + if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD) + return TITAN_GE_MDIO_ERROR; + + val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS); + if (val & 0x2) + return TITAN_GE_MDIO_ERROR; + + return TITAN_GE_MDIO_GOOD; +} + diff --git a/drivers/net/titan_mdio.h b/drivers/net/titan_mdio.h new file mode 100644 index 0000000..5d23344 --- /dev/null +++ b/drivers/net/titan_mdio.h @@ -0,0 +1,56 @@ +/* + * MDIO used to interact with the PHY when using GMII/MII + */ +#ifndef _TITAN_MDIO_H +#define _TITAN_MDIO_H + +#include +#include +#include +#include "titan_ge.h" + + +#define TITAN_GE_MDIO_ERROR (-9000) +#define TITAN_GE_MDIO_GOOD 0 + +#define TITAN_GE_MDIO_BASE titan_ge_base + +#define TITAN_GE_MDIO_READ(offset) \ + *(volatile u32 *)(titan_ge_base + (offset)) + +#define TITAN_GE_MDIO_WRITE(offset, data) \ + *(volatile u32 *)(titan_ge_base + (offset)) = (data) + + +/* GMII specific registers */ +#define TITAN_GE_MARVEL_PHY_ID 0x00 +#define TITAN_PHY_AUTONEG_ADV 0x04 +#define TITAN_PHY_LP_ABILITY 0x05 +#define TITAN_GE_MDIO_MII_CTRL 0x09 +#define TITAN_GE_MDIO_MII_EXTENDED 0x0f +#define TITAN_GE_MDIO_PHY_CTRL 0x10 +#define TITAN_GE_MDIO_PHY_STATUS 0x11 +#define TITAN_GE_MDIO_PHY_IE 0x12 +#define TITAN_GE_MDIO_PHY_IS 0x13 +#define TITAN_GE_MDIO_PHY_LED 0x18 +#define TITAN_GE_MDIO_PHY_LED_OVER 0x19 +#define PHY_ANEG_TIME_WAIT 45 /* 45 seconds wait time */ + +/* + * MDIO Config Structure + */ +typedef struct { + unsigned int clka; + int mdio_spre; + int mdio_mode; +} titan_ge_mdio_config; + +/* + * Function Prototypes + */ +int titan_ge_mdio_setup(titan_ge_mdio_config *); +int titan_ge_mdio_inaddrs(int, int); +int titan_ge_mdio_read(int, int, unsigned int *); +int titan_ge_mdio_write(int, int, unsigned int); + +#endif /* _TITAN_MDIO_H */ diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f354bd4..eab6234 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -217,8 +217,106 @@ config USB_NET_RNDIS_WLAN If you choose to build a module, it'll be called rndis_wlan. +config RTL8180 + tristate "Realtek 8180/8185 PCI support" + depends on MAC80211 && PCI && EXPERIMENTAL + select EEPROM_93CX6 + ---help--- + This is a driver for RTL8180 and RTL8185 based cards. + These are PCI based chips found in cards such as: + + (RTL8185 802.11g) + A-Link WL54PC + + (RTL8180 802.11b) + Belkin F5D6020 v3 + Belkin F5D6020 v3 + Dlink DWL-610 + Dlink DWL-510 + Netgear MA521 + Level-One WPC-0101 + Acer Aspire 1357 LMi + VCTnet PC-11B1 + Ovislink AirLive WL-1120PCM + Mentor WL-PCI + Linksys WPC11 v4 + TrendNET TEW-288PI + D-Link DWL-520 Rev D + Repotec RP-WP7126 + TP-Link TL-WN250/251 + Zonet ZEW1000 + Longshine LCS-8031-R + HomeLine HLW-PCC200 + GigaFast WF721-AEX + Planet WL-3553 + Encore ENLWI-PCI1-NT + TrendNET TEW-266PC + Gigabyte GN-WLMR101 + Siemens-fujitsu Amilo D1840W + Edimax EW-7126 + PheeNet WL-11PCIR + Tonze PC-2100T + Planet WL-8303 + Dlink DWL-650 v M1 + Edimax EW-7106 + Q-Tec 770WC + Topcom Skyr@cer 4011b + Roper FreeLan 802.11b (edition 2004) + Wistron Neweb Corp CB-200B + Pentagram HorNET + QTec 775WC + TwinMOS Booming B Series + Micronet SP906BB + Sweex LC700010 + Surecom EP-9428 + Safecom SWLCR-1100 + + Thanks to Realtek for their support! + +config RTL8187 + tristate "Realtek 8187 and 8187B USB support" + depends on MAC80211 && USB && !LEMOTE_MACH2F + select EEPROM_93CX6 + ---help--- + This is a driver for RTL8187 and RTL8187B based cards. + These are USB based chips found in devices such as: + + Netgear WG111v2 + Level 1 WNC-0301USB + Micronet SP907GK V5 + Encore ENUWI-G2 + Trendnet TEW-424UB + ASUS P5B Deluxe/P5K Premium motherboards + Toshiba Satellite Pro series of laptops + Asus Wireless Link + Linksys WUSB54GC-EU v2 + (v1 = rt73usb; v3 is rt2070-based, + use staging/rt3070 or try rt2800usb) + + Thanks to Realtek for their support! + +# If possible, automatically enable LEDs for RTL8187. + +config RTL8187_LEDS + bool + depends on RTL8187 && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = RTL8187) + default y + source "drivers/net/wireless/rtl818x/Kconfig" +config RTL8187B + tristate "Realtek 8187B wifi support for yeeloong2f laptop" + depends on MAC80211 && USB && LEMOTE_MACH2F + depends on RFKILL || !RFKILL + select CRYPTO + select WIRELESS_EXT + select WEXT_PRIV + ---help--- + This is a driver for RTL8187B based cards, this driver is especially + for yeeloon2f laptop. + + Thanks to Realtek for their support! + config ADM8211 tristate "ADMtek ADM8211 support" depends on MAC80211 && PCI && EXPERIMENTAL diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7bba6a8..7ef4ecf 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -24,7 +24,8 @@ obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_ZD1211RW) += zd1211rw/ obj-$(CONFIG_RTL8180) += rtl818x/ obj-$(CONFIG_RTL8187) += rtl818x/ -obj-$(CONFIG_RTLWIFI) += rtlwifi/ +obj-$(CONFIG_RTL8187B) += rtl8187b/ +obj-$(CONFIG_RTL8192CE) += rtlwifi/ # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o diff --git a/drivers/net/wireless/rtl8187b/Makefile b/drivers/net/wireless/rtl8187b/Makefile new file mode 100644 index 0000000..c688cc9 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/Makefile @@ -0,0 +1,41 @@ +obj-$(CONFIG_RTL8187B) += rtl8187b.o + +rtl8187b-objs := r8187_core.o \ + r8180_93cx6.o \ + r8180_wx.o \ + r8180_rtl8225.o \ + r8180_rtl8225z2.o \ + r8180_pm.o \ + r8180_dm.o \ + r8187_led.o \ + r8187_rfkill.o \ + ieee80211/dot11d.o \ + ieee80211/ieee80211_softmac.o \ + ieee80211/ieee80211_rx.o \ + ieee80211/ieee80211_tx.o \ + ieee80211/ieee80211_wx.o \ + ieee80211/ieee80211_module.o \ + ieee80211/ieee80211_softmac_wx.o \ + ieee80211/ieee80211_crypt.o \ + ieee80211/ieee80211_crypt_tkip.o \ + ieee80211/ieee80211_crypt_ccmp.o \ + ieee80211/ieee80211_crypt_wep.o + +EXTRA_CFLAGS += -DCONFIG_RTL8180_PM +EXTRA_CFLAGS += -DJACKSON_NEW_8187 -DJACKSON_NEW_RX +EXTRA_CFLAGS += -DTHOMAS_BEACON -DTHOMAS_TASKLET -DTHOMAS_SKB -DTHOMAS_TURBO +EXTRA_CFLAGS += -DJOHN_IOCTL +EXTRA_CFLAGS += -DLED +#EXTRA_CFLAGS += -DLED_SHIN +#EXTRA_CFLAGS += -DSW_ANTE_DIVERSITY +EXTRA_CFLAGS += -DCPU_64BIT +EXTRA_CFLAGS += -DCONFIG_IPS +#CFLAGS += -DJOHN_HWSEC -DJOHN_TKIP +#CFLAGS += -DJOHN_DUMP_TX +#EXTRA_CFLAGS += -DJOHN_DUMP_TXPKT + +#Radio On/Off debug +#EXTRA_CFLAGS += -DCONFIG_RADIO_DEBUG + +#for dot11d +EXTRA_CFLAGS += -DENABLE_DOT11D diff --git a/drivers/net/wireless/rtl8187b/dot11d.h b/drivers/net/wireless/rtl8187b/dot11d.h new file mode 100644 index 0000000..99010be --- /dev/null +++ b/drivers/net/wireless/rtl8187b/dot11d.h @@ -0,0 +1,102 @@ +#ifndef __INC_DOT11D_H +#define __INC_DOT11D_H + +#include "ieee80211/ieee80211.h" + +//#define ENABLE_DOT11D + +//#define DOT11D_MAX_CHNL_NUM 83 + +typedef struct _CHNL_TXPOWER_TRIPLE { + u8 FirstChnl; + u8 NumChnls; + u8 MaxTxPowerInDbm; +}CHNL_TXPOWER_TRIPLE, *PCHNL_TXPOWER_TRIPLE; + +typedef enum _DOT11D_STATE { + DOT11D_STATE_NONE = 0, + DOT11D_STATE_LEARNED, + DOT11D_STATE_DONE, +}DOT11D_STATE; + +typedef struct _RT_DOT11D_INFO { + //DECLARE_RT_OBJECT(RT_DOT11D_INFO); + + bool bEnabled; // dot11MultiDomainCapabilityEnabled + + u16 CountryIeLen; // > 0 if CountryIeBuf[] contains valid country information element. + u8 CountryIeBuf[MAX_IE_LEN]; + u8 CountryIeSrcAddr[6]; // Source AP of the country IE. + u8 CountryIeWatchdog; + + u8 channel_map[MAX_CHANNEL_NUMBER+1]; //!!!Value 0: Invalid, 1: Valid (active scan), 2: Valid (passive scan) + //u8 ChnlListLen; // #Bytes valid in ChnlList[]. + //u8 ChnlList[DOT11D_MAX_CHNL_NUM]; + u8 MaxTxPwrDbmList[MAX_CHANNEL_NUMBER+1]; + + DOT11D_STATE State; +}RT_DOT11D_INFO, *PRT_DOT11D_INFO; +#define eqMacAddr(a,b) ( ((a)[0]==(b)[0] && (a)[1]==(b)[1] && (a)[2]==(b)[2] && (a)[3]==(b)[3] && (a)[4]==(b)[4] && (a)[5]==(b)[5]) ? 1:0 ) +#define cpMacAddr(des,src) ((des)[0]=(src)[0],(des)[1]=(src)[1],(des)[2]=(src)[2],(des)[3]=(src)[3],(des)[4]=(src)[4],(des)[5]=(src)[5]) +#define GET_DOT11D_INFO(__pIeeeDev) ((PRT_DOT11D_INFO)((__pIeeeDev)->pDot11dInfo)) + +#define IS_DOT11D_ENABLE(__pIeeeDev) GET_DOT11D_INFO(__pIeeeDev)->bEnabled +#define IS_COUNTRY_IE_VALID(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen > 0) + +#define IS_EQUAL_CIE_SRC(__pIeeeDev, __pTa) eqMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa) +#define UPDATE_CIE_SRC(__pIeeeDev, __pTa) cpMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa) + +#define IS_COUNTRY_IE_CHANGED(__pIeeeDev, __Ie) \ + (((__Ie).Length == 0 || (__Ie).Length != GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen) ? \ + FALSE : \ + (!memcmp(GET_DOT11D_INFO(__pIeeeDev)->CountryIeBuf, (__Ie).Octet, (__Ie).Length))) + +#define CIE_WATCHDOG_TH 1 +#define GET_CIE_WATCHDOG(__pIeeeDev) GET_DOT11D_INFO(__pIeeeDev)->CountryIeWatchdog +#define RESET_CIE_WATCHDOG(__pIeeeDev) GET_CIE_WATCHDOG(__pIeeeDev) = 0 +#define UPDATE_CIE_WATCHDOG(__pIeeeDev) ++GET_CIE_WATCHDOG(__pIeeeDev) + +#define IS_DOT11D_STATE_DONE(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->State == DOT11D_STATE_DONE) + + +void +Dot11d_Init( + struct ieee80211_device *dev + ); + +void +Dot11d_Reset( + struct ieee80211_device *dev + ); + +void +Dot11d_UpdateCountryIe( + struct ieee80211_device *dev, + u8 * pTaddr, + u16 CoutryIeLen, + u8 * pCoutryIe + ); + +u8 +DOT11D_GetMaxTxPwrInDbm( + struct ieee80211_device *dev, + u8 Channel + ); + +void +DOT11D_ScanComplete( + struct ieee80211_device * dev + ); + +int IsLegalChannel( + struct ieee80211_device * dev, + u8 channel +); + +int ToLegalChannel( + struct ieee80211_device * dev, + u8 channel +); + +void dump_chnl_map(u8 * channel_map); +#endif // #ifndef __INC_DOT11D_H diff --git a/drivers/net/wireless/rtl8187b/ieee80211/arc4.c b/drivers/net/wireless/rtl8187b/ieee80211/arc4.c new file mode 100644 index 0000000..e93e5e2 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/arc4.c @@ -0,0 +1,103 @@ +/* + * Cryptographic API + * + * ARC4 Cipher Algorithm + * + * Jon Oberheide + * + * 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 "rtl_crypto.h" + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 + +struct arc4_ctx { + u8 S[256]; + u8 x, y; +}; + +static int arc4_set_key(void *ctx_arg, const u8 *in_key, unsigned int key_len, u32 *flags) +{ + struct arc4_ctx *ctx = ctx_arg; + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for(i = 0; i < 256; i++) + ctx->S[i] = i; + + for(i = 0; i < 256; i++) + { + u8 a = ctx->S[i]; + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if(++k >= key_len) + k = 0; + } + + return 0; +} + +static void arc4_crypt(void *ctx_arg, u8 *out, const u8 *in) +{ + struct arc4_ctx *ctx = ctx_arg; + + u8 *const S = ctx->S; + u8 x = ctx->x; + u8 y = ctx->y; + u8 a, b; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + S[x] = b; + S[y] = a; + x = (x + 1) & 0xff; + *out++ = *in ^ S[(a + b) & 0xff]; + + ctx->x = x; + ctx->y = y; +} + +static struct crypto_alg arc4_alg = { + .cra_name = "arc4", + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = ARC4_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct arc4_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(arc4_alg.cra_list), + .cra_u = { .cipher = { + .cia_min_keysize = ARC4_MIN_KEY_SIZE, + .cia_max_keysize = ARC4_MAX_KEY_SIZE, + .cia_setkey = arc4_set_key, + .cia_encrypt = arc4_crypt, + .cia_decrypt = arc4_crypt } } +}; + +static int __init arc4_init(void) +{ + return crypto_register_alg(&arc4_alg); +} + + +static void __exit arc4_exit(void) +{ + crypto_unregister_alg(&arc4_alg); +} + +module_init(arc4_init); +module_exit(arc4_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARC4 Cipher Algorithm"); +MODULE_AUTHOR("Jon Oberheide "); diff --git a/drivers/net/wireless/rtl8187b/ieee80211/dot11d.c b/drivers/net/wireless/rtl8187b/ieee80211/dot11d.c new file mode 100644 index 0000000..8d662b5 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/dot11d.c @@ -0,0 +1,244 @@ +#ifdef ENABLE_DOT11D +//----------------------------------------------------------------------------- +// File: +// Dot11d.c +// +// Description: +// Implement 802.11d. +// +//----------------------------------------------------------------------------- + +#include "dot11d.h" + +void +Dot11d_Init(struct ieee80211_device *ieee) +{ + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(ieee); + + pDot11dInfo->bEnabled = 0; + + pDot11dInfo->State = DOT11D_STATE_NONE; + pDot11dInfo->CountryIeLen = 0; + memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1); + memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1); + RESET_CIE_WATCHDOG(ieee); + + //printk("Dot11d_Init()\n"); +} + +// +// Description: +// Reset to the state as we are just entering a regulatory domain. +// +void +Dot11d_Reset(struct ieee80211_device *ieee) +{ + u32 i; + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(ieee); + + // Clear old channel map + memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1); + memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1); + // Set new channel map + for (i=1; i<=11; i++) { + (pDot11dInfo->channel_map)[i] = 1; + } + for (i=12; i<=14; i++) { + (pDot11dInfo->channel_map)[i] = 2; + } + + pDot11dInfo->State = DOT11D_STATE_NONE; + pDot11dInfo->CountryIeLen = 0; + RESET_CIE_WATCHDOG(ieee); + + //printk("Dot11d_Reset()\n"); +} + +// +// Description: +// Update country IE from Beacon or Probe Resopnse +// and configure PHY for operation in the regulatory domain. +// +// TODO: +// Configure Tx power. +// +// Assumption: +// 1. IS_DOT11D_ENABLE() is TRUE. +// 2. Input IE is an valid one. +// +void +Dot11d_UpdateCountryIe( + struct ieee80211_device *dev, + u8 * pTaddr, + u16 CoutryIeLen, + u8 * pCoutryIe + ) +{ + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev); + u8 i, j, NumTriples, MaxChnlNum; + PCHNL_TXPOWER_TRIPLE pTriple; + + if((CoutryIeLen - 3)%3 != 0) + { + printk("Dot11d_UpdateCountryIe(): Invalid country IE, skip it........1\n"); + Dot11d_Reset(dev); + return; + } + + memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1); + memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1); + MaxChnlNum = 0; + NumTriples = (CoutryIeLen - 3) / 3; // skip 3-byte country string. + pTriple = (PCHNL_TXPOWER_TRIPLE)(pCoutryIe + 3); + for(i = 0; i < NumTriples; i++) + { + if(MaxChnlNum >= pTriple->FirstChnl) + { // It is not in a monotonically increasing order, so stop processing. + printk("Dot11d_UpdateCountryIe(): Invalid country IE, skip it........1\n"); + Dot11d_Reset(dev); + return; + } + if(MAX_CHANNEL_NUMBER < (pTriple->FirstChnl + pTriple->NumChnls)) + { // It is not a valid set of channel id, so stop processing. + printk("Dot11d_UpdateCountryIe(): Invalid country IE, skip it........2\n"); + Dot11d_Reset(dev); + return; + } + + for(j = 0 ; j < pTriple->NumChnls; j++) + { + pDot11dInfo->channel_map[pTriple->FirstChnl + j] = 1; + pDot11dInfo->MaxTxPwrDbmList[pTriple->FirstChnl + j] = pTriple->MaxTxPowerInDbm; + MaxChnlNum = pTriple->FirstChnl + j; + } + + pTriple = (PCHNL_TXPOWER_TRIPLE)((u8*)pTriple + 3); + } +#if 1 + //printk("Dot11d_UpdateCountryIe(): Channel List:\n"); + printk("Channel List:"); + for(i=1; i<= MAX_CHANNEL_NUMBER; i++) + if(pDot11dInfo->channel_map[i] > 0) + printk(" %d", i); + printk("\n"); +#endif + + UPDATE_CIE_SRC(dev, pTaddr); + + pDot11dInfo->CountryIeLen = CoutryIeLen; + memcpy(pDot11dInfo->CountryIeBuf, pCoutryIe,CoutryIeLen); + pDot11dInfo->State = DOT11D_STATE_LEARNED; +} + +void dump_chnl_map(u8 * channel_map) +{ + int i; + printk("Channel List:"); + for(i=1; i<= MAX_CHANNEL_NUMBER; i++) + if(channel_map[i] > 0) + printk(" %d(%d)", i, channel_map[i]); + printk("\n"); +} + +u8 +DOT11D_GetMaxTxPwrInDbm( + struct ieee80211_device *dev, + u8 Channel + ) +{ + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev); + u8 MaxTxPwrInDbm = 255; + + if(MAX_CHANNEL_NUMBER < Channel) + { + printk("DOT11D_GetMaxTxPwrInDbm(): Invalid Channel\n"); + return MaxTxPwrInDbm; + } + if(pDot11dInfo->channel_map[Channel]) + { + MaxTxPwrInDbm = pDot11dInfo->MaxTxPwrDbmList[Channel]; + } + + return MaxTxPwrInDbm; +} + + +void +DOT11D_ScanComplete( + struct ieee80211_device * dev + ) +{ + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev); + + switch(pDot11dInfo->State) + { + case DOT11D_STATE_LEARNED: + pDot11dInfo->State = DOT11D_STATE_DONE; + break; + + case DOT11D_STATE_DONE: + if( GET_CIE_WATCHDOG(dev) == 0 ) + { // Reset country IE if previous one is gone. + Dot11d_Reset(dev); + } + break; + case DOT11D_STATE_NONE: + break; + } +} + +int IsLegalChannel( + struct ieee80211_device * dev, + u8 channel +) +{ + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev); + + if(MAX_CHANNEL_NUMBER < channel) + { + printk("IsLegalChannel(): Invalid Channel\n"); + return 0; + } + if(pDot11dInfo->channel_map[channel] > 0) + return 1; + return 0; +} + +int ToLegalChannel( + struct ieee80211_device * dev, + u8 channel +) +{ + PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev); + u8 default_chn = 0; + u32 i = 0; + + for (i=1; i<= MAX_CHANNEL_NUMBER; i++) + { + if(pDot11dInfo->channel_map[i] > 0) + { + default_chn = i; + break; + } + } + + if(MAX_CHANNEL_NUMBER < channel) + { + printk("IsLegalChannel(): Invalid Channel\n"); + return default_chn; + } + + if(pDot11dInfo->channel_map[channel] > 0) + return channel; + + return default_chn; +} + +EXPORT_SYMBOL(Dot11d_Init); +EXPORT_SYMBOL(Dot11d_Reset); +EXPORT_SYMBOL(Dot11d_UpdateCountryIe); +EXPORT_SYMBOL(DOT11D_GetMaxTxPwrInDbm); +EXPORT_SYMBOL(DOT11D_ScanComplete); +EXPORT_SYMBOL(IsLegalChannel); +EXPORT_SYMBOL(ToLegalChannel); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/dot11d.h b/drivers/net/wireless/rtl8187b/ieee80211/dot11d.h new file mode 100644 index 0000000..64bcf15 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/dot11d.h @@ -0,0 +1,102 @@ +#ifndef __INC_DOT11D_H +#define __INC_DOT11D_H + +#include "ieee80211.h" + +//#define ENABLE_DOT11D + +//#define DOT11D_MAX_CHNL_NUM 83 + +typedef struct _CHNL_TXPOWER_TRIPLE { + u8 FirstChnl; + u8 NumChnls; + u8 MaxTxPowerInDbm; +}CHNL_TXPOWER_TRIPLE, *PCHNL_TXPOWER_TRIPLE; + +typedef enum _DOT11D_STATE { + DOT11D_STATE_NONE = 0, + DOT11D_STATE_LEARNED, + DOT11D_STATE_DONE, +}DOT11D_STATE; + +typedef struct _RT_DOT11D_INFO { + //DECLARE_RT_OBJECT(RT_DOT11D_INFO); + + bool bEnabled; // dot11MultiDomainCapabilityEnabled + + u16 CountryIeLen; // > 0 if CountryIeBuf[] contains valid country information element. + u8 CountryIeBuf[MAX_IE_LEN]; + u8 CountryIeSrcAddr[6]; // Source AP of the country IE. + u8 CountryIeWatchdog; + + u8 channel_map[MAX_CHANNEL_NUMBER+1]; //!!!Value 0: Invalid, 1: Valid (active scan), 2: Valid (passive scan) + //u8 ChnlListLen; // #Bytes valid in ChnlList[]. + //u8 ChnlList[DOT11D_MAX_CHNL_NUM]; + u8 MaxTxPwrDbmList[MAX_CHANNEL_NUMBER+1]; + + DOT11D_STATE State; +}RT_DOT11D_INFO, *PRT_DOT11D_INFO; +#define eqMacAddr(a,b) ( ((a)[0]==(b)[0] && (a)[1]==(b)[1] && (a)[2]==(b)[2] && (a)[3]==(b)[3] && (a)[4]==(b)[4] && (a)[5]==(b)[5]) ? 1:0 ) +#define cpMacAddr(des,src) ((des)[0]=(src)[0],(des)[1]=(src)[1],(des)[2]=(src)[2],(des)[3]=(src)[3],(des)[4]=(src)[4],(des)[5]=(src)[5]) +#define GET_DOT11D_INFO(__pIeeeDev) ((PRT_DOT11D_INFO)((__pIeeeDev)->pDot11dInfo)) + +#define IS_DOT11D_ENABLE(__pIeeeDev) GET_DOT11D_INFO(__pIeeeDev)->bEnabled +#define IS_COUNTRY_IE_VALID(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen > 0) + +#define IS_EQUAL_CIE_SRC(__pIeeeDev, __pTa) eqMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa) +#define UPDATE_CIE_SRC(__pIeeeDev, __pTa) cpMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa) + +#define IS_COUNTRY_IE_CHANGED(__pIeeeDev, __Ie) \ + (((__Ie).Length == 0 || (__Ie).Length != GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen) ? \ + FALSE : \ + (!memcmp(GET_DOT11D_INFO(__pIeeeDev)->CountryIeBuf, (__Ie).Octet, (__Ie).Length))) + +#define CIE_WATCHDOG_TH 1 +#define GET_CIE_WATCHDOG(__pIeeeDev) GET_DOT11D_INFO(__pIeeeDev)->CountryIeWatchdog +#define RESET_CIE_WATCHDOG(__pIeeeDev) GET_CIE_WATCHDOG(__pIeeeDev) = 0 +#define UPDATE_CIE_WATCHDOG(__pIeeeDev) ++GET_CIE_WATCHDOG(__pIeeeDev) + +#define IS_DOT11D_STATE_DONE(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->State == DOT11D_STATE_DONE) + + +void +Dot11d_Init( + struct ieee80211_device *dev + ); + +void +Dot11d_Reset( + struct ieee80211_device *dev + ); + +void +Dot11d_UpdateCountryIe( + struct ieee80211_device *dev, + u8 * pTaddr, + u16 CoutryIeLen, + u8 * pCoutryIe + ); + +u8 +DOT11D_GetMaxTxPwrInDbm( + struct ieee80211_device *dev, + u8 Channel + ); + +void +DOT11D_ScanComplete( + struct ieee80211_device * dev + ); + +int IsLegalChannel( + struct ieee80211_device * dev, + u8 channel +); + +int ToLegalChannel( + struct ieee80211_device * dev, + u8 channel +); + +void dump_chnl_map(u8 * channel_map); +#endif // #ifndef __INC_DOT11D_H diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211.h b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211.h new file mode 100644 index 0000000..e9ea893 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211.h @@ -0,0 +1,1904 @@ +/* + * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 + * remains copyright by the original authors + * + * Portions of the merged code are 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 + * + * Modified for Realtek's wi-fi cards by Andrea Merello + * + * + * 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. + */ +#ifndef IEEE80211_H +#define IEEE80211_H +#include /* ETH_ALEN */ +#include /* ARRAY_SIZE */ +#include +#include +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#include +#else +#include +#include +#endif +#include +#include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)) +#include +#endif +/* +#ifndef bool +#define bool int +#endif + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif +*/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) +#ifndef bool +typedef enum{false = 0, true} bool; +#endif +#endif +//#ifdef JOHN_HWSEC +#define KEY_TYPE_NA 0x0 +#define KEY_TYPE_WEP40 0x1 +#define KEY_TYPE_TKIP 0x2 +#define KEY_TYPE_CCMP 0x4 +#define KEY_TYPE_WEP104 0x5 +//#endif + + +#define aSifsTime 10 + +#define MGMT_QUEUE_NUM 5 + + +#define IEEE_CMD_SET_WPA_PARAM 1 +#define IEEE_CMD_SET_WPA_IE 2 +#define IEEE_CMD_SET_ENCRYPTION 3 +#define IEEE_CMD_MLME 4 + +#define IEEE_PARAM_WPA_ENABLED 1 +#define IEEE_PARAM_TKIP_COUNTERMEASURES 2 +#define IEEE_PARAM_DROP_UNENCRYPTED 3 +#define IEEE_PARAM_PRIVACY_INVOKED 4 +#define IEEE_PARAM_AUTH_ALGS 5 +#define IEEE_PARAM_IEEE_802_1X 6 +//It should consistent with the driver_XXX.c +// David, 2006.9.26 +#define IEEE_PARAM_WPAX_SELECT 7 +//Added for notify the encryption type selection +// David, 2006.9.26 +#define IEEE_PROTO_WPA 1 +#define IEEE_PROTO_RSN 2 +//Added for notify the encryption type selection +// David, 2006.9.26 +#define IEEE_WPAX_USEGROUP 0 +#define IEEE_WPAX_WEP40 1 +#define IEEE_WPAX_TKIP 2 +#define IEEE_WPAX_WRAP 3 +#define IEEE_WPAX_CCMP 4 +#define IEEE_WPAX_WEP104 5 + +#define IEEE_KEY_MGMT_IEEE8021X 1 +#define IEEE_KEY_MGMT_PSK 2 + + + +#define IEEE_MLME_STA_DEAUTH 1 +#define IEEE_MLME_STA_DISASSOC 2 + + +#define IEEE_CRYPT_ERR_UNKNOWN_ALG 2 +#define IEEE_CRYPT_ERR_UNKNOWN_ADDR 3 +#define IEEE_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define IEEE_CRYPT_ERR_KEY_SET_FAILED 5 +#define IEEE_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define IEEE_CRYPT_ERR_CARD_CONF_FAILED 7 + + +#define IEEE_CRYPT_ALG_NAME_LEN 16 + +//#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10)) +#define ieee80211_wx_get_scan ieee80211_wx_get_scan_rtl +#define ieee80211_wx_set_encode ieee80211_wx_set_encode_rtl +#define ieee80211_wx_get_encode ieee80211_wx_get_encode_rtl +//////////////////////////////// +// added for kernel conflict under FC5 +#define ieee80211_wx_get_name ieee80211_wx_get_name_rtl +#define free_ieee80211 free_ieee80211_rtl +#define alloc_ieee80211 alloc_ieee80211_rtl +/////////////////////////////// +//#endif +#define ieee80211_rx ieee80211_rx_rtl +#define ieee80211_wake_queue ieee80211_wake_queue_rtl +#define ieee80211_stop_queue ieee80211_stop_queue_rtl +#define ieee80211_wx_set_auth ieee80211_wx_set_auth_rtl +#define ieee80211_get_crypto_ops ieee80211_get_crypto_ops_rtl +#define ieee80211_crypt_delayed_deinit ieee80211_crypt_delayed_deinit_rtl + +#define ieee80211_start_scan ieee80211_start_scan_rtl +#define ieee80211_register_crypto_ops ieee80211_register_crypto_ops_rtl +#define ieee80211_unregister_crypto_ops ieee80211_unregister_crypto_ops_rtl +#define ieee80211_crypt_deinit_entries ieee80211_crypt_deinit_entries_rtl +#define ieee80211_crypt_deinit_handler ieee80211_crypt_deinit_handler_rtl +typedef struct ieee_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u8 name; + u32 value; + } wpa_param; + struct { + u32 len; + u8 reserved[32]; + u8 data[0]; + } wpa_ie; + struct{ + int command; + int reason_code; + } mlme; + struct { + u8 alg[IEEE_CRYPT_ALG_NAME_LEN]; + u8 set_tx; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + + } u; +}ieee_param; + + +#if WIRELESS_EXT < 17 +#define IW_QUAL_QUAL_INVALID 0x10 +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_QUAL_UPDATED 0x1 +#define IW_QUAL_LEVEL_UPDATED 0x2 +#define IW_QUAL_NOISE_UPDATED 0x4 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +static inline void tq_init(struct tq_struct * task, void(*func)(void *), void *data) +{ + task->routine = func; + task->data = data; + //task->next = NULL; + INIT_LIST_HEAD(&task->list); + task->sync = 0; +} +#endif + +// linux under 2.6.9 release may not support it, so modify it for common use +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)) +//#define MSECS(t) (1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ) +#define MSECS(t) (HZ * ((t) / 1000) + (HZ * ((t) % 1000)) / 1000) +static inline unsigned long msleep_interruptible_rtl(unsigned int msecs) +{ + unsigned long timeout = MSECS(msecs) + 1; + + while (timeout) { + set_current_state(TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); + } + return timeout; +} +#else +#define MSECS(t) msecs_to_jiffies(t) +#define msleep_interruptible_rtl msleep_interruptible +#endif + +#define IEEE80211_DATA_LEN 2304 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + + +#define IEEE80211_HLEN 30 +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +/* this is stolen and modified from the madwifi driver*/ +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define IEEE80211_FC0_TYPE_DATA 0x08 +#define IEEE80211_FC0_SUBTYPE_MASK 0xB0 +#define IEEE80211_FC0_SUBTYPE_QOS 0x80 + +#define IEEE80211_QOS_HAS_SEQ(fc) \ + (((fc) & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == \ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + +/* this is stolen from ipw2200 driver */ +#define IEEE_IBSS_MAC_HASH_SIZE 31 +#define IEEE_MESH_MAC_HASH_SIZE 31 +struct ieee_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num[17]; + u16 frag_num[17]; + unsigned long packet_time[17]; + struct list_head list; +}; + +struct ieee_mesh_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +struct ieee80211_hdr { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_QOS { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; + u16 QOS_ctl; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addr { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addr_QOS { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u16 QOS_ctl; +} __attribute__ ((packed)); + +enum eap_type { + EAP_PACKET = 0, + EAPOL_START, + EAPOL_LOGOFF, + EAPOL_KEY, + EAPOL_ENCAP_ASF_ALERT +}; + +//by lizhaoming for LED 2008.6.23 from r8187_led.h +#ifdef LED +typedef enum _LED_CTL_MODE { + LED_CTL_POWER_ON, + LED_CTL_POWER_OFF, + LED_CTL_LINK, + LED_CTL_NO_LINK, + LED_CTL_TX, + LED_CTL_RX, + LED_CTL_SITE_SURVEY, +} LED_CTL_MODE; +#endif + +static const char *eap_types[] = { + [EAP_PACKET] = "EAP-Packet", + [EAPOL_START] = "EAPOL-Start", + [EAPOL_LOGOFF] = "EAPOL-Logoff", + [EAPOL_KEY] = "EAPOL-Key", + [EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert" +}; + +static inline const char *eap_get_type(int type) +{ + return (type >= ARRAY_SIZE(eap_types)) ? "Unknown" : eap_types[type]; +} + +struct eapol { + u8 snap[6]; + u16 ethertype; + u8 version; + u8 type; + u16 length; +} __attribute__ ((packed)); + +#define IEEE80211_3ADDR_LEN 24 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_FCS_LEN 4 + +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2346U + +/* Frame control field constants */ +#define IEEE80211_FCTL_VERS 0x0002 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_DSTODS 0x0300 //added by david +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_WEP 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#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_MANAGE_ACT 0x00D0 + +/* control */ +#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 //added for WMM 2006/8/2 +#define IEEE80211_STYPE_QOS_NULL 0x00C0 + + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + + +/* debug macros */ + +#ifdef CONFIG_IEEE80211_DEBUG +extern u32 ieee80211_debug_level; +#define IEEE80211_DEBUG(level, fmt, args...) \ +do { if (ieee80211_debug_level & (level)) \ + printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) +#else +#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) +#endif /* CONFIG_IEEE80211_DEBUG */ + +/* + * To use the debug system; + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of: + * + * #define IEEE80211_DL_xxxx VALUE + * + * shifting value to the left one bit from the previous entry. xxxx should be + * the name of the classification (for example, WEP) + * + * You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your + * classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * To add your debug level to the list of levels seen when you perform + * + * % cat /proc/net/ipw/debug_level + * + * you simply need to add your entry to the ipw_debug_levels array. + * + * If you do not see debug_level in /proc/net/ipw then you do not have + * CONFIG_IEEE80211_DEBUG defined in your kernel configuration + * + */ + +#define IEEE80211_DL_INFO (1<<0) +#define IEEE80211_DL_WX (1<<1) +#define IEEE80211_DL_SCAN (1<<2) +#define IEEE80211_DL_STATE (1<<3) +#define IEEE80211_DL_MGMT (1<<4) +#define IEEE80211_DL_FRAG (1<<5) +#define IEEE80211_DL_EAP (1<<6) +#define IEEE80211_DL_DROP (1<<7) + +#define IEEE80211_DL_TX (1<<8) +#define IEEE80211_DL_RX (1<<9) + +#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) +#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) +#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a) + +#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a) +#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a) +#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a) +#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a) +#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a) +#define IEEE80211_DEBUG_EAP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_EAP, f, ## a) +#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a) +#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a) +#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a) +#include +#include +#include /* ARPHRD_ETHER */ + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY // enable iwspy support +#endif +#include // new driver API + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +/* IEEE 802.11 defines */ + +#define P80211_OUI_LEN 3 + +struct ieee80211_snap_hdr { + + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ + +} __attribute__ ((packed)); + +#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) + +#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) +#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) +#define WLAN_GET_SEQ_SEQ(seq) ((seq) & IEEE80211_SCTL_SEQ) + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_BSS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#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) +#define WLAN_CAPABILITY_SHORT_SLOT (1<<10) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_CHALLENGE 16 +#define WLAN_EID_RSN 48 +#define WLAN_EID_GENERIC 221 + +#define IEEE80211_MGMT_HDR_LEN 24 +#define IEEE80211_DATA_HDR3_LEN 24 +#define IEEE80211_DATA_HDR4_LEN 30 + + +#define IEEE80211_STATMASK_SIGNAL (1<<0) +#define IEEE80211_STATMASK_RSSI (1<<1) +#define IEEE80211_STATMASK_NOISE (1<<2) +#define IEEE80211_STATMASK_RATE (1<<3) +#define IEEE80211_STATMASK_WEMASK 0x7 + + +#define IEEE80211_CCK_MODULATION (1<<0) +#define IEEE80211_OFDM_MODULATION (1<<1) + +#define IEEE80211_24GHZ_BAND (1<<0) +#define IEEE80211_52GHZ_BAND (1<<1) + +#define IEEE80211_CCK_RATE_LEN 4 +#define IEEE80211_CCK_RATE_1MB 0x02 +#define IEEE80211_CCK_RATE_2MB 0x04 +#define IEEE80211_CCK_RATE_5MB 0x0B +#define IEEE80211_CCK_RATE_11MB 0x16 +#define IEEE80211_OFDM_RATE_LEN 8 +#define IEEE80211_OFDM_RATE_6MB 0x0C +#define IEEE80211_OFDM_RATE_9MB 0x12 +#define IEEE80211_OFDM_RATE_12MB 0x18 +#define IEEE80211_OFDM_RATE_18MB 0x24 +#define IEEE80211_OFDM_RATE_24MB 0x30 +#define IEEE80211_OFDM_RATE_36MB 0x48 +#define IEEE80211_OFDM_RATE_48MB 0x60 +#define IEEE80211_OFDM_RATE_54MB 0x6C +#define IEEE80211_BASIC_RATE_MASK 0x80 + +#define IEEE80211_CCK_RATE_1MB_MASK (1<<0) +#define IEEE80211_CCK_RATE_2MB_MASK (1<<1) +#define IEEE80211_CCK_RATE_5MB_MASK (1<<2) +#define IEEE80211_CCK_RATE_11MB_MASK (1<<3) +#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) +#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) +#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) +#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) +#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) +#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) +#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) +#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) + +#define IEEE80211_CCK_RATES_MASK 0x0000000F +#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ + IEEE80211_CCK_RATE_2MB_MASK) +#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ + IEEE80211_CCK_RATE_5MB_MASK | \ + IEEE80211_CCK_RATE_11MB_MASK) + +#define IEEE80211_OFDM_RATES_MASK 0x00000FF0 +#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ + IEEE80211_OFDM_RATE_12MB_MASK | \ + IEEE80211_OFDM_RATE_24MB_MASK) +#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ + IEEE80211_OFDM_RATE_9MB_MASK | \ + IEEE80211_OFDM_RATE_18MB_MASK | \ + IEEE80211_OFDM_RATE_36MB_MASK | \ + IEEE80211_OFDM_RATE_48MB_MASK | \ + IEEE80211_OFDM_RATE_54MB_MASK) +#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ + IEEE80211_CCK_DEFAULT_RATES_MASK) + +#define IEEE80211_NUM_OFDM_RATES 8 +#define IEEE80211_NUM_CCK_RATES 4 +#define IEEE80211_OFDM_SHIFT_MASK_A 4 + + + + +/* NOTE: This data is for statistical purposes; not all hardware provides this + * information for frames received. Not setting these will not cause + * any adverse affects. */ +struct ieee80211_rx_stats { + u32 mac_time[2]; + u8 signalstrength; + s8 rssi; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ + u8 received_channel; + u8 control; + u8 mask; + u8 freq; + u16 len; + u8 nic_type; +}; + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define IEEE80211_FRAG_CACHE_LEN 4 + +struct ieee80211_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + +struct ieee80211_stats { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + +struct ieee80211_softmac_stats{ + unsigned int rx_ass_ok; + unsigned int rx_ass_err; + unsigned int rx_probe_rq; + unsigned int tx_probe_rs; + unsigned int tx_beacons; + unsigned int rx_auth_rq; + unsigned int rx_auth_rs_ok; + unsigned int rx_auth_rs_err; + unsigned int tx_auth_rq; + unsigned int no_auth_rs; + unsigned int no_ass_rs; + unsigned int tx_ass_rq; + unsigned int rx_ass_rq; + unsigned int tx_probe_rq; + unsigned int reassoc; + unsigned int swtxstop; + unsigned int swtxawake; +}; + +struct ieee80211_device; + +#include "ieee80211_crypt.h" + +#define SEC_KEY_1 (1<<0) +#define SEC_KEY_2 (1<<1) +#define SEC_KEY_3 (1<<2) +#define SEC_KEY_4 (1<<3) +#define SEC_ACTIVE_KEY (1<<4) +#define SEC_AUTH_MODE (1<<5) +#define SEC_UNICAST_GROUP (1<<6) +#define SEC_LEVEL (1<<7) +#define SEC_ENABLED (1<<8) + +#define SEC_LEVEL_0 0 /* None */ +#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ +#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ +#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ +#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 +#define ALG_KEY_LEN 32 + +#ifdef _RTL8187_EXT_PATCH_ +#define MAX_MP 16 +#endif +struct ieee80211_security { + u16 active_key:2, + enabled:1, + auth_mode:2, + auth_algo:4, + unicast_uses_group:1; + u8 key_sizes[WEP_KEYS]; + u8 keys[WEP_KEYS][ALG_KEY_LEN]; + u8 level; + u16 flags; +} __attribute__ ((packed)); + + +/* + + 802.11 data frame from AP + + ,-------------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `-------------------------------------------------------------------' + +Total: 28-2340 bytes + +*/ + +struct ieee80211_header_data { + u16 frame_ctl; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; +}; + +#define BEACON_PROBE_SSID_ID_POSITION 12 + +/* Management Frame Information Element Types */ +#define MFIE_TYPE_SSID 0 +#define MFIE_TYPE_RATES 1 +#define MFIE_TYPE_FH_SET 2 +#define MFIE_TYPE_DS_SET 3 +#define MFIE_TYPE_CF_SET 4 +#define MFIE_TYPE_TIM 5 +#define MFIE_TYPE_IBSS_SET 6 +#define MFIE_TYPE_COUNTRY 7 +#define MFIE_TYPE_CHALLENGE 16 +#define MFIE_TYPE_ERP 42 +#define MFIE_TYPE_RSN 48 +#define MFIE_TYPE_RATES_EX 50 +#define MFIE_TYPE_GENERIC 221 + +#ifdef ENABLE_DOT11D +typedef enum +{ + COUNTRY_CODE_FCC = 0, + COUNTRY_CODE_IC = 1, + COUNTRY_CODE_ETSI = 2, + COUNTRY_CODE_SPAIN = 3, + COUNTRY_CODE_FRANCE = 4, + COUNTRY_CODE_MKK = 5, + COUNTRY_CODE_MKK1 = 6, + COUNTRY_CODE_ISRAEL = 7, + COUNTRY_CODE_TELEC = 8, + COUNTRY_CODE_GLOBAL_DOMAIN = 9, + COUNTRY_CODE_WORLD_WIDE_13_INDEX = 10 +}country_code_type_t; +#endif + + +struct ieee80211_info_element_hdr { + u8 id; + u8 len; +} __attribute__ ((packed)); + +struct ieee80211_info_element { + u8 id; + u8 len; + u8 data[0]; +} __attribute__ ((packed)); + +/* + * These are the data types that can make up management packets + * + u16 auth_algorithm; + u16 auth_sequence; + u16 beacon_interval; + u16 capability; + u8 current_ap[ETH_ALEN]; + u16 listen_interval; + struct { + u16 association_id:14, reserved:2; + } __attribute__ ((packed)); + u32 time_stamp[2]; + u16 reason; + u16 status; +*/ + +#define IEEE80211_DEFAULT_TX_ESSID "Penguin" +#define IEEE80211_DEFAULT_BASIC_RATE 10 +#define IEEE80211_DEFAULT_MESHID "802.11s" +#define IEEE80211_DEFAULT_MESH_CHAN 1 + +struct ieee80211_authentication { + struct ieee80211_header_data header; + u16 algorithm; + u16 transaction; + u16 status; + //struct ieee80211_info_element_hdr info_element; +} __attribute__ ((packed)); + + +struct ieee80211_probe_response { + struct ieee80211_header_data header; + u32 time_stamp[2]; + u16 beacon_interval; + u16 capability; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + +struct ieee80211_probe_request { + struct ieee80211_header_data header; + /*struct ieee80211_info_element info_element;*/ +} __attribute__ ((packed)); + +struct ieee80211_assoc_request_frame { + struct ieee80211_hdr_3addr header; + u16 capability; + u16 listen_interval; + //u8 current_ap[ETH_ALEN]; + struct ieee80211_info_element_hdr info_element; +} __attribute__ ((packed)); + +struct ieee80211_assoc_response_frame { + struct ieee80211_hdr_3addr header; + u16 capability; + u16 status; + u16 aid; + struct ieee80211_info_element info_element; /* supported rates */ +} __attribute__ ((packed)); + + +struct ieee80211_txb { + u8 nr_frags; + u8 encrypted; + u16 reserved; + u16 frag_size; + u16 payload_size; + struct sk_buff *fragments[0]; +}; + +struct ieee80211_wmm_ac_param { + u8 ac_aci_acm_aifsn; + u8 ac_ecwmin_ecwmax; + u16 ac_txop_limit; +}; + +struct ieee80211_wmm_ts_info { + u8 ac_dir_tid; + u8 ac_up_psb; + u8 reserved; +} __attribute__ ((packed)); + +struct ieee80211_wmm_tspec_elem { + struct ieee80211_wmm_ts_info ts_info; + u16 norm_msdu_size; + u16 max_msdu_size; + u32 min_serv_inter; + u32 max_serv_inter; + u32 inact_inter; + u32 suspen_inter; + u32 serv_start_time; + u32 min_data_rate; + u32 mean_data_rate; + u32 peak_data_rate; + u32 max_burst_size; + u32 delay_bound; + u32 min_phy_rate; + u16 surp_band_allow; + u16 medium_time; +}__attribute__((packed)); + +enum {WMM_all_frame, WMM_two_frame, WMM_four_frame, WMM_six_frame}; +#define MAX_SP_Len (WMM_all_frame << 4) +#define IEEE80211_QOS_TID 0x0f +#define QOS_CTL_NOTCONTAIN_ACK (0x01 << 5) + +/* SWEEP TABLE ENTRIES NUMBER*/ +#define MAX_SWEEP_TAB_ENTRIES 42 +#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 +/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs + * only use 8, and then use extended rates for the remaining supported + * rates. Other APs, however, stick all of their supported rates on the + * main rates information element... */ +#define MAX_RATES_LENGTH ((u8)12) +#define MAX_RATES_EX_LENGTH ((u8)16) +#define MAX_NETWORK_COUNT 128 +#ifdef ENABLE_DOT11D +#define MAX_CHANNEL_NUMBER 165 //YJ,modified,080625 +#define MAX_IE_LEN 0xFF //+YJ,080625 +#else +#define MAX_CHANNEL_NUMBER 161 +#endif + +//#define IEEE80211_SOFTMAC_SCAN_TIME 400 +#define IEEE80211_SOFTMAC_SCAN_TIME 100//lzm mod 081209 +//(HZ / 2) +#define IEEE80211_SOFTMAC_ASSOC_RETRY_TIME (HZ * 2) + +#define CRC_LENGTH 4U + +#define MAX_WPA_IE_LEN 64 + +#define NETWORK_EMPTY_ESSID (1<<0) +#define NETWORK_HAS_OFDM (1<<1) +#define NETWORK_HAS_CCK (1<<2) + +#define IEEE80211_DTIM_MBCAST 4 +#define IEEE80211_DTIM_UCAST 2 +#define IEEE80211_DTIM_VALID 1 +#define IEEE80211_DTIM_INVALID 0 + +#define IEEE80211_PS_DISABLED 0 +#define IEEE80211_PS_UNICAST IEEE80211_DTIM_UCAST +#define IEEE80211_PS_MBCAST IEEE80211_DTIM_MBCAST + +//added by David for QoS 2006/6/30 +//#define WMM_Hang_8187 +#ifdef WMM_Hang_8187 +#undef WMM_Hang_8187 +#endif + +#define WME_AC_BE 0x00 +#define WME_AC_BK 0x01 +#define WME_AC_VI 0x02 +#define WME_AC_VO 0x03 +#define WME_ACI_MASK 0x03 +#define WME_AIFSN_MASK 0x03 +#define WME_AC_PRAM_LEN 16 + +//UP Mapping to AC, using in MgntQuery_SequenceNumber() and maybe for DSCP +//#define UP2AC(up) ((up<3) ? ((up==0)?1:0) : (up>>1)) +#define UP2AC(up) ( \ + ((up) < 1) ? WME_AC_BE : \ + ((up) < 3) ? WME_AC_BK : \ + ((up) < 4) ? WME_AC_BE : \ + ((up) < 6) ? WME_AC_VI : \ + WME_AC_VO) +//AC Mapping to UP, using in Tx part for selecting the corresponding TX queue +#define AC2UP(_ac) ( \ + ((_ac) == WME_AC_VO) ? 6 : \ + ((_ac) == WME_AC_VI) ? 5 : \ + ((_ac) == WME_AC_BK) ? 1 : \ + 0) + +#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */ +struct ether_header { + u8 ether_dhost[ETHER_ADDR_LEN]; + u8 ether_shost[ETHER_ADDR_LEN]; + u16 ether_type; +} __attribute__((packed)); + +#ifndef ETHERTYPE_PAE +#define ETHERTYPE_PAE 0x888e /* EAPOL PAE/802.1x */ +#endif +#ifndef ETHERTYPE_IP +#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#endif + +struct ieee80211_network { + /* These entries are used to identify a unique network */ + u8 bssid[ETH_ALEN]; + u8 channel; + /* Ensure null-terminated for any debug msgs */ + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u8 ssid_len; + + /* These are network statistics */ + struct ieee80211_rx_stats stats; + u16 capability; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; + u8 flags; + u32 last_associate; + u32 time_stamp[2]; + u16 beacon_interval; + u16 listen_interval; + u16 atim_window; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + u8 dtim_period; + u8 dtim_data; + u32 last_dtim_sta_time[2]; +#ifdef _RTL8187_EXT_PATCH_ + void *ext_entry; +#endif + struct list_head list; + //appeded for QoS + u8 wmm_info; + struct ieee80211_wmm_ac_param wmm_param[4]; + u8 QoS_Enable; + u8 SignalStrength; +#ifdef THOMAS_TURBO + u8 Turbo_Enable;//enable turbo mode, added by thomas +#endif + +#ifdef ENABLE_DOT11D + u16 CountryIeLen; + u8 CountryIeBuf[MAX_IE_LEN]; +#endif + +}; + +enum ieee80211_state { + + /* the card is not linked at all */ + IEEE80211_NOLINK = 0, + + /* IEEE80211_ASSOCIATING* are for BSS client mode + * the driver shall not perform RX filtering unless + * the state is LINKED. + * The driver shall just check for the state LINKED and + * defaults to NOLINK for ALL the other states (including + * LINKED_SCANNING) + */ + + /* the association procedure will start (wq scheduling)*/ + IEEE80211_ASSOCIATING, + IEEE80211_ASSOCIATING_RETRY, + + /* the association procedure is sending AUTH request*/ + IEEE80211_ASSOCIATING_AUTHENTICATING, + + /* the association procedure has successfully authentcated + * and is sending association request + */ + IEEE80211_ASSOCIATING_AUTHENTICATED, + + /* the link is ok. the card associated to a BSS or linked + * to a ibss cell or acting as an AP and creating the bss + */ + IEEE80211_LINKED, + + /* same as LINKED, but the driver shall apply RX filter + * rules as we are in NO_LINK mode. As the card is still + * logically linked, but it is doing a syncro site survey + * then it will be back to LINKED state. + */ + IEEE80211_LINKED_SCANNING, +//by amy for mesh + IEEE80211_MESH_SCANNING, + IEEE80211_MESH_LINKED, +//by amy for mesh + +}; + +#define DEFAULT_MAX_SCAN_AGE (15 * HZ) +#define DEFAULT_FTS 2346 +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5] + + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)) +extern inline int is_multicast_ether_addr(const u8 *addr) +{ + return ((addr[0] != 0xff) && (0x01 & addr[0])); +} +#endif + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)) +extern inline int is_broadcast_ether_addr(const u8 *addr) +{ + return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) && \ + (addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff)); +} +#endif + +#define CFG_IEEE80211_RESERVE_FCS (1<<0) +#define CFG_IEEE80211_COMPUTE_FCS (1<<1) + +typedef struct tx_pending_t{ + int frag; + struct ieee80211_txb *txb; +}tx_pending_t; + +#ifdef _RTL8187_EXT_PATCH_ +struct ieee80211_crypt_data_list{ + u8 used; + u8 mac_addr[ETH_ALEN]; //record mac_add + struct ieee80211_crypt_data *crypt[WEP_KEYS]; +}__attribute__((packed)); + +#endif + +struct ieee80211_device { + struct net_device *dev; + + /* Bookkeeping structures */ + struct net_device_stats stats; + struct ieee80211_stats ieee_stats; + struct ieee80211_softmac_stats softmac_stats; + + /* Probe / Beacon management */ + struct list_head network_free_list; + struct list_head network_list; + struct ieee80211_network *networks; + int scans; + int scan_age; + + int iw_mode; /* operating mode (IW_MODE_*) */ +#ifdef _RTL8187_EXT_PATCH_ + int iw_ext_mode; // if iw_mode == iw_ext_mode, do ext_patch_**(); +#endif + + spinlock_t lock; + spinlock_t wpax_suitlist_lock; + + int tx_headroom; /* Set to size of any additional room needed at front + * of allocated Tx SKBs */ + u32 config; + + /* WEP and other encryption related settings at the device level */ + int open_wep; /* Set to 1 to allow unencrypted frames */ + + int reset_on_keychange; /* Set to 1 if the HW needs to be reset on + * WEP key changes */ + + /* If the host performs {en,de}cryption, then set to 1 */ + int host_encrypt; + int host_decrypt; + int ieee802_1x; /* is IEEE 802.1X used */ + + /* WPA data */ + int wpa_enabled; + int drop_unencrypted; + int tkip_countermeasures; + int privacy_invoked; + size_t wpa_ie_len; + u8 *wpa_ie; + +//#ifdef JOHN_TKIP + u8 ap_mac_addr[6]; + u16 pairwise_key_type; + u16 broadcast_key_type; +//#endif + struct list_head crypt_deinit_list; +#ifdef _RTL8187_EXT_PATCH_ + struct ieee80211_crypt_data_list* cryptlist[MAX_MP]; +#else + struct ieee80211_crypt_data *crypt[WEP_KEYS]; +#endif + int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */ + struct timer_list crypt_deinit_timer; + + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + /* Fragmentation structures */ + // each streaming contain a entry + struct ieee80211_frag_entry frag_cache[17][IEEE80211_FRAG_CACHE_LEN]; + unsigned int frag_next_idx[17]; + u16 fts; /* Fragmentation Threshold */ + + /* This stores infos for the current network. + * Either the network we are associated in INFRASTRUCTURE + * or the network that we are creating in MASTER mode. + * ad-hoc is a mixture ;-). + * Note that in infrastructure mode, even when not associated, + * fields bssid and essid may be valid (if wpa_set and essid_set + * are true) as thy carry the value set by the user via iwconfig + */ + struct ieee80211_network current_network; + + + enum ieee80211_state state; + + int short_slot; + int mode; /* A, B, G */ + int modulation; /* CCK, OFDM */ + int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ + int abg_true; /* ABG flag */ + + /* used for forcing the ibss workqueue to terminate + * without wait for the syncro scan to terminate + */ + short sync_scan_hurryup; + +#ifdef ENABLE_DOT11D + void * pDot11dInfo; + bool bGlobalDomain; + bool bWorldWide13;//lzm add 20081205 + + // For Liteon Ch12~13 passive scan + u8 MinPassiveChnlNum; + u8 IbssStartChnl; +#else + /* map of allowed channels. 0 is dummy */ + // FIXME: remeber to default to a basic channel plan depending of the PHY type + int channel_map[MAX_CHANNEL_NUMBER+1]; +#endif + + int rate; /* current rate */ + int basic_rate; + //FIXME: pleace callback, see if redundant with softmac_features + short active_scan; + +#ifdef _RTL8187_EXT_PATCH_ +// short ch_lock; + short meshScanMode; +#endif + /* this contains flags for selectively enable softmac support */ + u16 softmac_features; + + /* if the sequence control field is not filled by HW */ + u16 seq_ctrl[5]; + + /* association procedure transaction sequence number */ + u16 associate_seq; + + /* AID for RTXed association responses */ + u16 assoc_id; + + /* power save mode related*/ + short ps; + short sta_sleep; + int ps_timeout; + struct tasklet_struct ps_task; + u32 ps_th; + u32 ps_tl; + + short raw_tx; + /* used if IEEE_SOFTMAC_TX_QUEUE is set */ + short queue_stop; + short scanning; + short scan_watchdog;//lzm add 081215 for roaming + short proto_started; + + struct semaphore wx_sem; + struct semaphore scan_sem; + struct semaphore ips_sem; + spinlock_t mgmt_tx_lock; + spinlock_t beacon_lock; + spinlock_t beaconflag_lock; + short beacon_txing; + + short wap_set; + short ssid_set; + + u8 wpax_type_set; //{added by David, 2006.9.28} + u32 wpax_type_notify; //{added by David, 2006.9.26} + + /* QoS related flag */ + char init_wmmparam_flag; + + /* for discarding duplicated packets in IBSS */ + struct list_head ibss_mac_hash[IEEE_IBSS_MAC_HASH_SIZE]; + + /* for discarding duplicated packets in Mesh */ //added by david 2008.2.28/ + struct list_head mesh_mac_hash[IEEE_MESH_MAC_HASH_SIZE]; + + /* for discarding duplicated packets in BSS */ + u16 last_rxseq_num[17]; /* rx seq previous per-tid */ + u16 last_rxfrag_num[17];/* tx frag previous per-tid */ + unsigned long last_packet_time[17]; + + /* for PS mode */ + unsigned long last_rx_ps_time; + + /* used if IEEE_SOFTMAC_SINGLE_QUEUE is set */ + struct sk_buff *mgmt_queue_ring[MGMT_QUEUE_NUM]; + int mgmt_queue_head; + int mgmt_queue_tail; +//by amy for ps + bool bInactivePs; + bool actscanning; + u16 ListenInterval; + u32 NumRxData; + unsigned long NumRxDataInPeriod; //YJ,add,080828 + unsigned long NumRxBcnInPeriod; //YJ,add,080828 +//by amy for ps + short meshid_set; + /* used if IEEE_SOFTMAC_TX_QUEUE is set */ + struct tx_pending_t tx_pending; + + /* used if IEEE_SOFTMAC_ASSOCIATE is set */ + struct timer_list associate_timer; + + /* used if IEEE_SOFTMAC_BEACONS is set */ + struct timer_list beacon_timer; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct work_struct associate_complete_wq; +// struct work_struct associate_retry_wq; +// struct work_struct start_ibss_wq; + struct work_struct associate_procedure_wq; + struct work_struct ips_leave_wq; //YJ,add,081230,for IPS + bool bHwRadioOff;//by lizhaoming +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + struct delayed_work softmac_scan_wq; + struct delayed_work start_ibss_wq; + struct delayed_work associate_retry_wq; +//by amy for rate adaptive + struct delayed_work rate_adapter_wq; +//by amy for rate adaptive + struct delayed_work watch_dog_wq; + struct delayed_work hw_dig_wq; + struct delayed_work tx_pw_wq; + +#ifdef SW_ANTE_DIVERSITY + struct delayed_work SwAntennaWorkItem; +#endif + +#else + struct work_struct softmac_scan_wq; + struct work_struct start_ibss_wq; + struct work_struct associate_retry_wq; +//by amy for rate adaptive + struct work_struct rate_adapter_wq; +//by amy for rate adaptive + struct work_struct watch_dog_wq; + struct work_struct hw_dig_wq; + struct work_struct tx_pw_wq; + +#ifdef SW_ANTE_DIVERSITY + struct work_struct SwAntennaWorkItem; +#endif + +#endif + +//struct work_struct softmac_scan_wq; + struct work_struct wx_sync_scan_wq; + struct work_struct wmm_param_update_wq; +#ifdef _RTL8187_EXT_PATCH_ + struct work_struct ext_stop_scan_wq; + struct work_struct ext_send_beacon_wq; +#endif + struct workqueue_struct *wq; +#else + /* used for periodly scan */ + struct timer_list scan_timer; + + struct tq_struct associate_complete_wq; + struct tq_struct associate_retry_wq; + struct tq_struct start_ibss_wq; + struct tq_struct associate_procedure_wq; + struct tq_struct ips_leave_wq; //YJ,add,081230,for IPS + struct tq_struct softmac_scan_wq; + struct tq_struct wx_sync_scan_wq; + struct tq_struct wmm_param_update_wq; +#ifdef _RTL8187_EXT_PATCH_ + struct tq_struct ext_stop_scan_wq; + struct tq_struct ext_send_beacon_wq; +#endif +#endif + + /* Callback functions */ + void (*set_security)(struct net_device *dev, + struct ieee80211_security *sec); + + /* Used to TX data frame by using txb structs. + * this is not used if in the softmac_features + * is set the flag IEEE_SOFTMAC_TX_QUEUE + */ + int (*hard_start_xmit)(struct ieee80211_txb *txb, + struct net_device *dev); + + int (*reset_port)(struct net_device *dev); + + /* Softmac-generated frames (mamagement) are TXed via this + * callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is + * not set. As some cards may have different HW queues that + * one might want to use for data and management frames + * the option to have two callbacks might be useful. + * This fucntion can't sleep. + */ + int (*softmac_hard_start_xmit)(struct sk_buff *skb, + struct net_device *dev); + + /* used instead of hard_start_xmit (not softmac_hard_start_xmit) + * if the IEEE_SOFTMAC_TX_QUEUE feature is used to TX data + * frames. I the option IEEE_SOFTMAC_SINGLE_QUEUE is also set + * then also management frames are sent via this callback. + * This function can't sleep. + */ + void (*softmac_data_hard_start_xmit)(struct sk_buff *skb, + struct net_device *dev,int rate); + + /* stops the HW queue for DATA frames. Useful to avoid + * waste time to TX data frame when we are reassociating + * This function can sleep. + */ + void (*data_hard_stop)(struct net_device *dev); + + /* OK this is complementar to data_poll_hard_stop */ + void (*data_hard_resume)(struct net_device *dev); + + /* ask to the driver to retune the radio . + * This function can sleep. the driver should ensure + * the radio has been swithced before return. + */ + void (*set_chan)(struct net_device *dev,short ch); + + /* These are not used if the ieee stack takes care of + * scanning (IEEE_SOFTMAC_SCAN feature set). + * In this case only the set_chan is used. + * + * The syncro version is similar to the start_scan but + * does not return until all channels has been scanned. + * this is called in user context and should sleep, + * it is called in a work_queue when swithcing to ad-hoc mode + * or in behalf of iwlist scan when the card is associated + * and root user ask for a scan. + * the fucntion stop_scan should stop both the syncro and + * background scanning and can sleep. + * The fucntion start_scan should initiate the background + * scanning and can't sleep. + */ + void (*scan_syncro)(struct net_device *dev); + void (*start_scan)(struct net_device *dev); + void (*stop_scan)(struct net_device *dev); + + /* indicate the driver that the link state is changed + * for example it may indicate the card is associated now. + * Driver might be interested in this to apply RX filter + * rules or simply light the LINK led + */ + void (*link_change)(struct net_device *dev); + + /* these two function indicates to the HW when to start + * and stop to send beacons. This is used when the + * IEEE_SOFTMAC_BEACONS is not set. For now the + * stop_send_bacons is NOT guaranteed to be called only + * after start_send_beacons. + */ + void (*start_send_beacons) (struct net_device *dev); + void (*stop_send_beacons) (struct net_device *dev); + + /* power save mode related */ + void (*sta_wake_up) (struct net_device *dev); + void (*ps_request_tx_ack) (struct net_device *dev); + void (*enter_sleep_state) (struct net_device *dev, u32 th, u32 tl); + short (*ps_is_queue_empty) (struct net_device *dev); + +//by lizhaoming for LED 2008.6.23 +#ifdef LED + void (*ieee80211_led_contorl) (struct net_device *dev, LED_CTL_MODE LedAction); +#endif +#ifdef CONFIG_IPS + void (*ieee80211_ips_leave) (struct net_device *dev); +#endif + /* QoS related */ + //void (*wmm_param_update) (struct net_device *dev, u8 *ac_param); + //void (*wmm_param_update) (struct ieee80211_device *ieee); + + +#ifdef _RTL8187_EXT_PATCH_ + + /// ieee80211_softmac.c + int (*ext_patch_ieee80211_start_protocol) (struct ieee80211_device *ieee); // start special mode + + short (*ext_patch_ieee80211_probe_req_1) (struct ieee80211_device *ieee); // return = 0: no more phases, >0: another phase + u8* (*ext_patch_ieee80211_probe_req_2) (struct ieee80211_device *ieee, struct sk_buff *skb, u8 *tag); // return tag + + void (*ext_patch_ieee80211_stop_protocol) (struct ieee80211_device *ieee); // stop timer + + void (*ext_patch_ieee80211_association_req_1) (struct ieee80211_assoc_request_frame *hdr); + u8* (*ext_patch_ieee80211_association_req_2) (struct ieee80211_device *ieee, struct ieee80211_network *pstat, struct sk_buff *skb); + + int (*ext_patch_ieee80211_rx_frame_softmac_on_assoc_req) (struct ieee80211_device *ieee, struct sk_buff *skb); + int (*ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp) (struct ieee80211_device *ieee, struct sk_buff *skb); + + void (*ext_patch_ieee80211_assoc_resp_by_net_1) (struct ieee80211_assoc_response_frame *assoc); + u8* (*ext_patch_ieee80211_assoc_resp_by_net_2) (struct ieee80211_device *ieee, struct ieee80211_network *pstat, int pkt_type, struct sk_buff *skb); + + int (*ext_patch_ieee80211_ext_stop_scan_wq_set_channel) (struct ieee80211_device *ieee); + + int (*ext_patch_ieee80211_softmac_xmit_get_rate) (struct ieee80211_device *ieee, struct sk_buff *skb); + + int (*ext_patch_ieee80211_rx_frame_softmac_on_auth)(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); + int (*ext_patch_ieee80211_rx_frame_softmac_on_deauth)(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); +//by amy for mesh + void (*ext_patch_ieee80211_start_mesh)(struct ieee80211_device *ieee); +//by amy for mesh + // ieee80211_rx.c + // rz + void (*ext_patch_ieee80211_rx_mgt_on_probe_req) ( struct ieee80211_device *ieee, struct ieee80211_probe_request *beacon, struct ieee80211_rx_stats *stats); + unsigned int(*ext_patch_ieee80211_process_probe_response_1)(struct ieee80211_device *ieee, struct ieee80211_probe_response *beacon, struct ieee80211_rx_stats *stats); + + void (*ext_patch_ieee80211_rx_mgt_update_expire) ( struct ieee80211_device *ieee, struct sk_buff *skb); + struct sk_buff* (*ext_patch_get_beacon_get_probersp)(struct ieee80211_device *ieee, u8 *dest, struct ieee80211_network *net); + + // success(return 0) is responsible to free skb + int (*ext_patch_ieee80211_rx_on_rx) (struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats, u16 type, u16 stype); + + int (*ext_patch_ieee80211_rx_frame_get_hdrlen) (struct ieee80211_device *ieee, struct sk_buff *skb); + + // Check whether or not accept the incoming frame. return 0: not accept, >0: accept + int (*ext_patch_ieee80211_rx_is_valid_framectl) (struct ieee80211_device *ieee, u16 fc, u16 type, u16 stype); + + // return > 0 is success. 0 when failed + // success(return >0) is responsible to free skb + int (*ext_patch_ieee80211_rx_process_dataframe) (struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); + + /* added by david for setting acl dynamically */ + u8 (*ext_patch_ieee80211_acl_query) (struct ieee80211_device *ieee, u8 *sa); + + // int (*ext_patch_is_duplicate_packet) (struct ieee80211_device *ieee, struct ieee80211_hdr *header, u16 type, u16 stype); + + // ieee80211_tx.c + + // locked by ieee->lock. Call ieee80211_softmac_xmit afterward + struct ieee80211_txb* (*ext_patch_ieee80211_xmit) (struct sk_buff *skb, struct net_device *dev); + + +#endif // _RTL8187_EXT_PATCH_ + + /* This must be the last item so that it points to the data + * allocated beyond this structure by alloc_ieee80211 */ + u8 priv[0]; +}; + +#define IEEE_A (1<<0) +#define IEEE_B (1<<1) +#define IEEE_G (1<<2) +#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) + +/* Generate a 802.11 header */ + +/* Uses the channel change callback directly + * instead of [start/stop] scan callbacks + */ +#define IEEE_SOFTMAC_SCAN (1<<2) + +/* Perform authentication and association handshake */ +#define IEEE_SOFTMAC_ASSOCIATE (1<<3) + +/* Generate probe requests */ +#define IEEE_SOFTMAC_PROBERQ (1<<4) + +/* Generate respones to probe requests */ +#define IEEE_SOFTMAC_PROBERS (1<<5) + +/* The ieee802.11 stack will manages the netif queue + * wake/stop for the driver, taking care of 802.11 + * fragmentation. See softmac.c for details. */ +#define IEEE_SOFTMAC_TX_QUEUE (1<<7) + +/* Uses only the softmac_data_hard_start_xmit + * even for TX management frames. + */ +#define IEEE_SOFTMAC_SINGLE_QUEUE (1<<8) + +/* Generate beacons. The stack will enqueue beacons + * to the card + */ +#define IEEE_SOFTMAC_BEACONS (1<<6) +#ifdef _RTL8187_EXT_PATCH_ +extern inline int ieee80211_find_MP(struct ieee80211_device* ieee, const u8* addr, u8 set) +{ + int i=0; + for (i=1; icryptlist[i]->used == 0)&&set) + {//entry is empty + memcpy(ieee->cryptlist[i]->mac_addr, addr, ETH_ALEN); + ieee->cryptlist[i]->used = 1; + return i; + } + else if (0 == memcmp(ieee->cryptlist[i]->mac_addr, addr, ETH_ALEN)) //find matched entry + { + return i; + } + } + return -1; +} +#endif + + + +static inline void *ieee80211_priv(struct net_device *dev) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + return ((struct ieee80211_device *)netdev_priv(dev))->priv; +#else + return ((struct ieee80211_device *)dev->priv)->priv; +#endif +} + +extern inline int ieee80211_is_empty_essid(const char *essid, int essid_len) +{ + /* Single white space is for Linksys APs */ + if (essid_len == 1 && essid[0] == ' ') + return 1; + + /* Otherwise, if the entire essid is 0, we assume it is hidden */ + while (essid_len) { + essid_len--; + if (essid[essid_len] != '\0') + return 0; + } + + return 1; +} + +extern inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, int mode) +{ + /* + * It is possible for both access points and our device to support + * combinations of modes, so as long as there is one valid combination + * of ap/device supported modes, then return success + * + */ + if ((mode & IEEE_A) && + (ieee->modulation & IEEE80211_OFDM_MODULATION) && + (ieee->freq_band & IEEE80211_52GHZ_BAND)) + return 1; + + if ((mode & IEEE_G) && + (ieee->modulation & IEEE80211_OFDM_MODULATION) && + (ieee->freq_band & IEEE80211_24GHZ_BAND)) + return 1; + + if ((mode & IEEE_B) && + (ieee->modulation & IEEE80211_CCK_MODULATION) && + (ieee->freq_band & IEEE80211_24GHZ_BAND)) + return 1; + + return 0; +} + +extern inline int ieee80211_get_hdrlen(u16 fc) +{ + int hdrlen = 24; + + switch (WLAN_FC_GET_TYPE(fc)) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) + hdrlen = 30; /* Addr4 */ + if(IEEE80211_QOS_HAS_SEQ(fc)) + hdrlen += 2; /* QOS ctrl*/ + break; + case IEEE80211_FTYPE_CTL: + switch (WLAN_FC_GET_STYPE(fc)) { + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + break; + } + + return hdrlen; +} + + + +/* ieee80211.c */ +extern void free_ieee80211(struct net_device *dev); +extern struct net_device *alloc_ieee80211(int sizeof_priv); + +extern int ieee80211_set_encryption(struct ieee80211_device *ieee); + +/* ieee80211_tx.c */ + +extern int ieee80211_encrypt_fragment( + struct ieee80211_device *ieee, + struct sk_buff *frag, + int hdr_len); + +extern int ieee80211_xmit(struct sk_buff *skb, + struct net_device *dev); +extern void ieee80211_txb_free(struct ieee80211_txb *); + + +/* ieee80211_rx.c */ +extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats); +extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr *header, + struct ieee80211_rx_stats *stats); + +/* ieee80211_wx.c */ +extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data* wrqu, char *extra); +int ieee80211_wx_set_auth(struct ieee80211_device *ieee, + struct iw_request_info *info, + struct iw_param *data, char *extra); +int ieee80211_wx_set_mlme(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len); +/* ieee80211_softmac.c */ +extern short ieee80211_is_54g(struct ieee80211_network net); +extern short ieee80211_is_shortslot(struct ieee80211_network net); +extern int ieee80211_rx_frame_softmac(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats, u16 type, + u16 stype); +extern void ieee80211_softmac_new_net(struct ieee80211_device *ieee, struct ieee80211_network *net); + +extern void ieee80211_softmac_xmit(struct ieee80211_txb *txb, struct ieee80211_device *ieee); +extern void ieee80211_softmac_check_all_nets(struct ieee80211_device *ieee); +extern void ieee80211_start_bss(struct ieee80211_device *ieee); +extern void ieee80211_start_master_bss(struct ieee80211_device *ieee); +extern void ieee80211_start_ibss(struct ieee80211_device *ieee); +extern void ieee80211_softmac_init(struct ieee80211_device *ieee); +extern void ieee80211_softmac_free(struct ieee80211_device *ieee); +extern void ieee80211_associate_abort(struct ieee80211_device *ieee); +extern void ieee80211_disassociate(struct ieee80211_device *ieee); +extern void ieee80211_stop_scan(struct ieee80211_device *ieee); +extern void ieee80211_start_scan_syncro(struct ieee80211_device *ieee); +extern void ieee80211_check_all_nets(struct ieee80211_device *ieee); +extern void ieee80211_start_protocol(struct ieee80211_device *ieee); +extern void ieee80211_stop_protocol(struct ieee80211_device *ieee); +extern void ieee80211_softmac_start_protocol(struct ieee80211_device *ieee); +extern void ieee80211_softmac_stop_protocol(struct ieee80211_device *ieee); +extern void ieee80211_reset_queue(struct ieee80211_device *ieee); +extern void ieee80211_wake_queue(struct ieee80211_device *ieee); +extern void ieee80211_stop_queue(struct ieee80211_device *ieee); +extern struct sk_buff *ieee80211_get_beacon(struct ieee80211_device *ieee); +extern void ieee80211_start_send_beacons(struct ieee80211_device *ieee); +extern void ieee80211_stop_send_beacons(struct ieee80211_device *ieee); +extern int ieee80211_wpa_supplicant_ioctl(struct ieee80211_device *ieee, struct iw_point *p); +extern void notify_wx_assoc_event(struct ieee80211_device *ieee); +extern void ieee80211_ps_tx_ack(struct ieee80211_device *ieee, short success); +extern void ieee80211_start_scan(struct ieee80211_device *ieee); + +#ifdef _RTL8187_EXT_PATCH_ +extern void ieee80211_rx_auth_rq(struct ieee80211_device *ieee, struct sk_buff *skb); +extern void ieee80211_ext_issue_assoc_req(struct ieee80211_device *ieee, struct ieee80211_network *pstat); +extern void ieee80211_associate_step1(struct ieee80211_device *ieee); +extern void ieee80211_ext_issue_disassoc(struct ieee80211_device *ieee, struct ieee80211_network *pstat, int reason, unsigned char extReason); +extern void ieee80211_ext_issue_assoc_rsp(struct ieee80211_device *ieee, u8 *dest, unsigned short status, struct ieee80211_network *pstat, int pkt_type); +extern void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee); +extern struct sk_buff* ieee80211_ext_probe_resp_by_net(struct ieee80211_device *ieee, u8 *dest, struct ieee80211_network *net); +extern int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee80211_probe_response *beacon, struct ieee80211_network *network, struct ieee80211_rx_stats *stats); +extern struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size, int gfp_mask); +extern void ieee80211_ext_send_11s_beacon(struct ieee80211_device *ieee); +extern struct ieee80211_txb *ieee80211_ext_alloc_txb(struct sk_buff *skb, struct net_device *dev, struct ieee80211_hdr_3addr *header, int hdr_len, u8 isQoS, u16 *pQOS_ctl, int isEncrypt, struct ieee80211_crypt_data* crypt); +extern struct ieee80211_txb *ieee80211_ext_reuse_txb(struct sk_buff *skb, struct net_device *dev, struct ieee80211_hdr_3addr *header, int hdr_len, u8 isQoS, u16 *pQOS_ctl, int isEncrypt, struct ieee80211_crypt_data* crypt); +extern int ieee_ext_skb_p80211_to_ether(struct sk_buff *skb, int hdrlen, u8 *dst, u8 *src); +#endif + +/* ieee80211_crypt_ccmp&tkip&wep.c */ +extern void ieee80211_tkip_null(void); +extern void ieee80211_wep_null(void); +extern void ieee80211_ccmp_null(void); +/* ieee80211_softmac_wx.c */ + +extern int ieee80211_wx_get_wap(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *ext); + +extern int ieee80211_wx_set_wap(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra); + +extern int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b); + +extern int ieee80211_wx_set_rate(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +extern int ieee80211_wx_get_rate(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +extern int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +extern int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +extern int ieee80211_wx_set_essid(struct ieee80211_device *ieee, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra); + +extern int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +extern int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +extern int ieee80211_wx_get_freq(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +//extern void ieee80211_wx_sync_scan_wq(struct ieee80211_device *ieee); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +extern void ieee80211_wx_sync_scan_wq(struct work_struct *work); +#else + extern void ieee80211_wx_sync_scan_wq(struct ieee80211_device *ieee); +#endif +extern int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +extern int ieee80211_wx_get_name(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +extern int ieee80211_wx_set_power(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +extern int ieee80211_wx_get_power(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +extern const long ieee80211_wlan_frequencies[]; + +extern inline void ieee80211_increment_scans(struct ieee80211_device *ieee) +{ + ieee->scans++; +} + +extern inline int ieee80211_get_scans(struct ieee80211_device *ieee) +{ + return ieee->scans; +} + +static inline const char *escape_essid(const char *essid, u8 essid_len) { + static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; + const char *s = essid; + char *d = escaped; + + if (ieee80211_is_empty_essid(essid, essid_len)) { + memcpy(escaped, "", sizeof("")); + return escaped; + } + + essid_len = min(essid_len, (u8)IW_ESSID_MAX_SIZE); + while (essid_len--) { + if (*s == '\0') { + *d++ = '\\'; + *d++ = '0'; + s++; + } else { + *d++ = *s++; + } + } + *d = '\0'; + return escaped; +} +#endif /* IEEE80211_H */ diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt.c new file mode 100644 index 0000000..57a6696 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt.c @@ -0,0 +1,275 @@ +/* + * Host AP crypto routines + * + * Copyright (c) 2002-2003, Jouni Malinen + * Portions Copyright (C) 2004, Intel 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. See README and COPYING for + * more details. + * + */ + +//#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("HostAP crypto"); +MODULE_LICENSE("GPL"); + +struct ieee80211_crypto_alg { + struct list_head list; + struct ieee80211_crypto_ops *ops; +}; + + +struct ieee80211_crypto { + struct list_head algs; + spinlock_t lock; +}; + +static struct ieee80211_crypto *hcrypt; + +void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, + int force) +{ + struct list_head *ptr, *n; + struct ieee80211_crypt_data *entry; + + for (ptr = ieee->crypt_deinit_list.next, n = ptr->next; + ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct ieee80211_crypt_data, list); + + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + + list_del(ptr); + + if (entry->ops) { + entry->ops->deinit(entry->priv); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + module_put(entry->ops->owner); +#else + __MOD_DEC_USE_COUNT(entry->ops->owner); +#endif + } + kfree(entry); + } +} + +void ieee80211_crypt_deinit_handler(unsigned long data) +{ + struct ieee80211_device *ieee = (struct ieee80211_device *)data; + unsigned long flags; + + spin_lock_irqsave(&ieee->lock, flags); + ieee80211_crypt_deinit_entries(ieee, 0); + if (!list_empty(&ieee->crypt_deinit_list)) { + printk(KERN_DEBUG "%s: entries remaining in delayed crypt " + "deletion list\n", ieee->dev->name); + ieee->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&ieee->crypt_deinit_timer); + } + spin_unlock_irqrestore(&ieee->lock, flags); + +} + +void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee, + struct ieee80211_crypt_data **crypt) +{ + struct ieee80211_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(&ieee->lock, flags); + list_add(&tmp->list, &ieee->crypt_deinit_list); + if (!timer_pending(&ieee->crypt_deinit_timer)) { + ieee->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&ieee->crypt_deinit_timer); + } + spin_unlock_irqrestore(&ieee->lock, flags); +} + +int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops) +{ + unsigned long flags; + struct ieee80211_crypto_alg *alg; + + if (hcrypt == NULL) + return -1; + + alg = kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + spin_lock_irqsave(&hcrypt->lock, flags); + list_add(&alg->list, &hcrypt->algs); + spin_unlock_irqrestore(&hcrypt->lock, flags); + + printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n", + ops->name); + + return 0; +} + +int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops) +{ + unsigned long flags; + struct list_head *ptr; + struct ieee80211_crypto_alg *del_alg = NULL; + + if (hcrypt == NULL) + return -1; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + if (alg->ops == ops) { + list_del(&alg->list); + del_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (del_alg) { + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " + "'%s'\n", ops->name); + kfree(del_alg); + } + + return del_alg ? 0 : -1; +} + + +struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name) +{ + unsigned long flags; + struct list_head *ptr; + struct ieee80211_crypto_alg *found_alg = NULL; + + if (hcrypt == NULL) + return NULL; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + if (strcmp(alg->ops->name, name) == 0) { + found_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (found_alg) + return found_alg->ops; + else + return NULL; +} + + +static void * ieee80211_crypt_null_init(int keyidx) { return (void *) 1; } +static void ieee80211_crypt_null_deinit(void *priv) {} + +static struct ieee80211_crypto_ops ieee80211_crypt_null = { + .name = "NULL", + .init = ieee80211_crypt_null_init, + .deinit = ieee80211_crypt_null_deinit, + .encrypt_mpdu = NULL, + .decrypt_mpdu = NULL, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = NULL, + .get_key = NULL, + .extra_prefix_len = 0, + .extra_postfix_len = 0, + .owner = THIS_MODULE, +}; + + +int __init ieee80211_crypto_init(void) +{ + int ret = -ENOMEM; + + hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL); + if (!hcrypt) + goto out; + + memset(hcrypt, 0, sizeof(*hcrypt)); + INIT_LIST_HEAD(&hcrypt->algs); + spin_lock_init(&hcrypt->lock); + + ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null); + if (ret < 0) { + kfree(hcrypt); + hcrypt = NULL; + } +out: + return ret; +} + + +void __exit ieee80211_crypto_deinit(void) +{ + struct list_head *ptr, *n; + + if (hcrypt == NULL) + return; + + for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs; + ptr = n, n = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + list_del(ptr); + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " + "'%s' (deinit)\n", alg->ops->name); + kfree(alg); + } + + kfree(hcrypt); +} + +#if 0 +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_crypt_deinit_entries); +EXPORT_SYMBOL(ieee80211_crypt_deinit_handler); +EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit); + +EXPORT_SYMBOL(ieee80211_register_crypto_ops); +EXPORT_SYMBOL(ieee80211_unregister_crypto_ops); +EXPORT_SYMBOL(ieee80211_get_crypto_ops); +#else +EXPORT_SYMBOL_NOVERS(ieee80211_crypt_deinit_entries); +EXPORT_SYMBOL_NOVERS(ieee80211_crypt_deinit_handler); +EXPORT_SYMBOL_NOVERS(ieee80211_crypt_delayed_deinit); + +EXPORT_SYMBOL_NOVERS(ieee80211_register_crypto_ops); +EXPORT_SYMBOL_NOVERS(ieee80211_unregister_crypto_ops); +EXPORT_SYMBOL_NOVERS(ieee80211_get_crypto_ops); +#endif + +module_init(ieee80211_crypto_init); +module_exit(ieee80211_crypto_deinit); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt.h b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt.h new file mode 100644 index 0000000..5fa8764 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt.h @@ -0,0 +1,91 @@ +/* + * 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 + * + * 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. + */ + +/* + * This file defines the interface to the ieee80211 crypto module. + */ +#ifndef IEEE80211_CRYPT_H +#define IEEE80211_CRYPT_H + +#include + +struct ieee80211_crypto_ops { + const char *name; + + /* 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); + + /* 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_prefix_len, extra_postfix_len; + + struct module *owner; +}; + +struct ieee80211_crypt_data { + struct list_head list; /* delayed deletion list */ + struct ieee80211_crypto_ops *ops; + void *priv; + atomic_t refcnt; +}; + +int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops); +int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops); +struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name); +void ieee80211_crypt_deinit_entries(struct ieee80211_device *, int); +void ieee80211_crypt_deinit_handler(unsigned long); +void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee, + struct ieee80211_crypt_data **crypt); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK) +#define crypto_alloc_tfm crypto_alloc_tfm_rtl +#define crypto_free_tfm crypto_free_tfm_rtl +#endif + +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_ccmp.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_ccmp.c new file mode 100644 index 0000000..37f4259 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_ccmp.c @@ -0,0 +1,524 @@ +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * 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 "ieee80211.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#include "rtl_crypto.h" +#else +#include +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + #include +#else + #include +#endif + +//#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 ieee80211_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_tfm *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]; +}; + +void ieee80211_ccmp_aes_encrypt(struct crypto_tfm *tfm, + const u8 pt[16], u8 ct[16]) +{ + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + struct scatterlist src, dst; + + src.page = virt_to_page(pt); + src.offset = offset_in_page(pt); + src.length = AES_BLOCK_LEN; + + dst.page = virt_to_page(ct); + dst.offset = offset_in_page(ct); + dst.length = AES_BLOCK_LEN; + + crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN); + #else + crypto_cipher_encrypt_one((void*)tfm, ct, pt); + #endif +} + +static void * ieee80211_ccmp_init(int key_idx) +{ + struct ieee80211_ccmp_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + priv->tfm = crypto_alloc_tfm("aes", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate " + "crypto API aes\n"); + goto fail; + } + #else + priv->tfm = (void*)crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tfm)) { + printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate " + "crypto API aes\n"); + priv->tfm = NULL; + goto fail; + } + #endif + return priv; + +fail: + if (priv) { + if (priv->tfm) + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + crypto_free_tfm(priv->tfm); + #else + crypto_free_cipher((void*)priv->tfm); + #endif + kfree(priv); + } + + return NULL; +} + + +static void ieee80211_ccmp_deinit(void *priv) +{ + struct ieee80211_ccmp_data *_priv = priv; + if (_priv && _priv->tfm) + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + crypto_free_tfm(_priv->tfm); + #else + crypto_free_cipher((void*)_priv->tfm); + #endif + 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]; +} + +#ifndef JOHN_CCMP +static void ccmp_init_blocks(struct crypto_tfm *tfm, + struct ieee80211_hdr *hdr, + u8 *pn, size_t dlen, u8 *b0, u8 *auth, + u8 *s0) +{ + u8 *pos, qc = 0; + size_t aad_len; + u16 fc; + int a4_included, qc_included; + u8 aad[2 * AES_BLOCK_LEN]; + + fc = le16_to_cpu(hdr->frame_ctl); + a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)); + /* + qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) && + (WLAN_FC_GET_STYPE(fc) & 0x08)); + */ + // fixed by David :2006.9.6 + qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) && + (WLAN_FC_GET_STYPE(fc) & 0x80)); + 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_ctl; + 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 */ + ieee80211_ccmp_aes_encrypt(tfm, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + ieee80211_ccmp_aes_encrypt(tfm, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + ieee80211_ccmp_aes_encrypt(tfm, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + ieee80211_ccmp_aes_encrypt(tfm, b0, s0); +} +#endif + +static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_ccmp_data *key = priv; + int data_len, i; + u8 *pos; + struct ieee80211_hdr *hdr; +#ifndef JOHN_CCMP + int blocks, last, len; + u8 *mic; + u8 *b0 = key->tx_b0; + u8 *b = key->tx_b; + u8 *e = key->tx_e; + u8 *s0 = key->tx_s0; +#endif + if (skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < CCMP_MIC_LEN || + skb->len < hdr_len) + return -1; + + data_len = skb->len - hdr_len; + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; +// mic = skb_put(skb, CCMP_MIC_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]; + + hdr = (struct ieee80211_hdr *) skb->data; +#ifndef JOHN_CCMP + //mic is moved to here by john + mic = skb_put(skb, CCMP_MIC_LEN); + + ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); + + blocks = (data_len + AES_BLOCK_LEN - 1) / 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); + ieee80211_ccmp_aes_encrypt(key->tfm, b, b); + /* Encryption, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + ieee80211_ccmp_aes_encrypt(key->tfm, b0, e); + xor_block(pos, e, len); + pos += len; + } + + for (i = 0; i < CCMP_MIC_LEN; i++) + mic[i] = b[i] ^ s0[i]; +#endif + return 0; +} + + +static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_ccmp_data *key = priv; + u8 keyidx, *pos; + struct ieee80211_hdr *hdr; + u8 pn[6]; +#ifndef JOHN_CCMP + size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; + u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; + u8 *b0 = key->rx_b0; + u8 *b = key->rx_b; + u8 *a = key->rx_a; + int i, blocks, last, len; +#endif + 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 " MAC_FMT "\n", MAC_ARG(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 " MAC_FMT + " with keyid=%d that does not have a configured" + " key\n", MAC_ARG(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 0 + if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT + " previous PN %02x%02x%02x%02x%02x%02x " + "received PN %02x%02x%02x%02x%02x%02x\n", + MAC_ARG(hdr->addr2), MAC_ARG(key->rx_pn), + MAC_ARG(pn)); + } + key->dot11RSNAStatsCCMPReplays++; + return -4; + } +#endif +#ifndef JOHN_CCMP + ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); + xor_block(mic, b, CCMP_MIC_LEN); + + blocks = (data_len + AES_BLOCK_LEN - 1) / 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; + ieee80211_ccmp_aes_encrypt(key->tfm, b0, b); + xor_block(pos, b, len); + /* Authentication */ + xor_block(a, pos, len); + ieee80211_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=" + MAC_FMT "\n", MAC_ARG(hdr->addr2)); + } + key->dot11RSNAStatsCCMPDecryptErrors++; + return -5; + } + + memcpy(key->rx_pn, pn, CCMP_PN_LEN); + +#endif + /* 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 ieee80211_ccmp_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_ccmp_data *data = priv; + int keyidx; + struct crypto_tfm *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((void*)data->tfm, data->key, CCMP_TK_LEN); + } else if (len == 0) + data->key_set = 0; + else + return -1; + + return 0; +} + + +static int ieee80211_ccmp_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_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 * ieee80211_ccmp_print_stats(char *p, void *priv) +{ + struct ieee80211_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, + MAC_ARG(ccmp->tx_pn), MAC_ARG(ccmp->rx_pn), + ccmp->dot11RSNAStatsCCMPFormatErrors, + ccmp->dot11RSNAStatsCCMPReplays, + ccmp->dot11RSNAStatsCCMPDecryptErrors); + + return p; +} + +void ieee80211_ccmp_null(void) +{ + // printk("============>%s()\n", __FUNCTION__); + return; +} +static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = { + .name = "CCMP", + .init = ieee80211_ccmp_init, + .deinit = ieee80211_ccmp_deinit, + .encrypt_mpdu = ieee80211_ccmp_encrypt, + .decrypt_mpdu = ieee80211_ccmp_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = ieee80211_ccmp_set_key, + .get_key = ieee80211_ccmp_get_key, + .print_stats = ieee80211_ccmp_print_stats, + .extra_prefix_len = CCMP_HDR_LEN, + .extra_postfix_len = CCMP_MIC_LEN, + .owner = THIS_MODULE, +}; + + +int __init ieee80211_crypto_ccmp_init(void) +{ + return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp); +} + + +void __exit ieee80211_crypto_ccmp_exit(void) +{ + ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp); +} +#if 0 +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_ccmp_null); +#else +EXPORT_SYMBOL_NOVERS(ieee80211_ccmp_null); +#endif + +module_init(ieee80211_crypto_ccmp_init); +module_exit(ieee80211_crypto_ccmp_exit); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_tkip.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_tkip.c new file mode 100644 index 0000000..92a6e93 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_tkip.c @@ -0,0 +1,996 @@ +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * 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 "ieee80211.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#include "rtl_crypto.h" +#else +#include +#endif +//#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + #include +#else + #include +#endif + +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: TKIP"); +MODULE_LICENSE("GPL"); + +struct ieee80211_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; + + #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)) + struct crypto_blkcipher *rx_tfm_arc4; + struct crypto_hash *rx_tfm_michael; + struct crypto_blkcipher *tx_tfm_arc4; + struct crypto_hash *tx_tfm_michael; + #endif + + struct crypto_tfm *tfm_arc4; + struct crypto_tfm *tfm_michael; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16], tx_hdr[16]; +}; + +static void * ieee80211_tkip_init(int key_idx) +{ + struct ieee80211_tkip_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->tfm_arc4 == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tfm_michael == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + goto fail; + } + + #else + priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tx_tfm_arc4)) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API arc4\n"); + 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)) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + 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)) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API arc4\n"); + 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)) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + priv->rx_tfm_michael = NULL; + goto fail; + } + #endif + return priv; + +fail: + if (priv) { + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + if (priv->tfm_michael) + crypto_free_tfm(priv->tfm_michael); + if (priv->tfm_arc4) + crypto_free_tfm(priv->tfm_arc4); + #else + 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); + #endif + kfree(priv); + } + + return NULL; +} + + +static void ieee80211_tkip_deinit(void *priv) +{ + struct ieee80211_tkip_data *_priv = priv; + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + if (_priv && _priv->tfm_michael) + crypto_free_tfm(_priv->tfm_michael); + if (_priv && _priv->tfm_arc4) + crypto_free_tfm(_priv->tfm_arc4); + #else + 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); + } + #endif + 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(u16 *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)); +} + +#ifndef JOHN_TKIP +#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((u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &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((u16 *) &TK[0])) >> 1); + +#ifdef __BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} +#endif +static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)) + struct blkcipher_desc desc = {.tfm = tkey->tx_tfm_arc4}; + #endif + int len; + u8 *pos; + struct ieee80211_hdr *hdr; +#ifndef JOHN_TKIP + u8 rc4key[16],*icv; + u32 crc; + struct scatterlist sg; +#endif + #if(LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,21)) + int ret; + #endif + + if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + hdr = (struct ieee80211_hdr *) skb->data; +#if 0 +printk("@@ tkey\n"); +printk("%x|", ((u32*)tkey->key)[0]); +printk("%x|", ((u32*)tkey->key)[1]); +printk("%x|", ((u32*)tkey->key)[2]); +printk("%x|", ((u32*)tkey->key)[3]); +printk("%x|", ((u32*)tkey->key)[4]); +printk("%x|", ((u32*)tkey->key)[5]); +printk("%x|", ((u32*)tkey->key)[6]); +printk("%x\n", ((u32*)tkey->key)[7]); +#endif + +#ifndef JOHN_TKIP + 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); + +#else + tkey->tx_phase1_done = 1; +#endif /*JOHN_TKIP*/ + + len = skb->len - hdr_len; + pos = skb_push(skb, 8); + memmove(pos, pos + 8, hdr_len); + pos += hdr_len; + +#ifdef JOHN_TKIP + *pos++ = Hi8(tkey->tx_iv16); + *pos++ = (Hi8(tkey->tx_iv16) | 0x20) & 0x7F; + *pos++ = Lo8(tkey->tx_iv16); +#else + *pos++ = rc4key[0]; + *pos++ = rc4key[1]; + *pos++ = rc4key[2]; +#endif + *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; +#ifndef JOHN_TKIP + icv = skb_put(skb, 4); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + crc = ~crc32_le(~0, pos, len); +#else + crc = ~ether_crc_le(len, pos); +#endif + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); + #else + crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + #else + sg_init_one(&sg, pos, len + 4); + #endif + ret= crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); + #endif +#endif + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } +#ifndef JOHN_TKIP + #if(LINUX_VERSION_CODE = KERNEL_VERSION(2,6,21)) + struct blkcipher_desc desc = {.tfm = tkey->rx_tfm_arc4}; + #endif + u8 keyidx, *pos; + u32 iv32; + u16 iv16; + struct ieee80211_hdr *hdr; +#ifndef JOHN_TKIP + u8 icv[4]; + u32 crc; + struct scatterlist sg; + u8 rc4key[16]; + int plen; +#endif + if (skb->len < hdr_len + 8 + 4) + 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 "TKIP: received packet without ExtIV" + " flag from " MAC_FMT "\n", MAC_ARG(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 " MAC_FMT + " with keyid=%d that does not have a configured" + " key\n", MAC_ARG(hdr->addr2), keyidx); + } + return -3; + } + iv16 = (pos[0] << 8) | pos[2]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += 8; +#ifndef JOHN_TKIP +#if 0 + if (iv32 < tkey->rx_iv32 || + (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT + " previous TSC %08x%04x received TSC " + "%08x%04x\n", MAC_ARG(hdr->addr2), + tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); + } + tkey->dot11RSNAStatsTKIPReplays++; + return -4; + } +#endif + 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; + #if(LINUX_VERSION_CODE tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4); + #else + crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + #else + sg_init_one(&sg, pos, plen + 4); + #endif + if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { + if (net_ratelimit()) { + printk(KERN_DEBUG ": TKIP: failed to decrypt " + "received packet from " MAC_FMT "\n", + MAC_ARG(hdr->addr2)); + } + return -7; + } + #endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + crc = ~crc32_le(~0, pos, plen); +#else + crc = ~ether_crc_le(plen, pos); +#endif + 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; + } + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: ICV error detected: STA=" + MAC_FMT "\n", MAC_ARG(hdr->addr2)); + } + tkey->dot11RSNAStatsTKIPICVErrors++; + return -5; + } + +#endif /* JOHN_TKIP */ + + /* 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 + 8, skb->data, hdr_len); + skb_pull(skb, 8); + skb_trim(skb, skb->len - 4); + +//john's test +#ifdef JOHN_DUMP +if( ((u16*)skb->data)[0] & 0x4000){ + printk("@@ rx decrypted skb->data"); + int i; + for(i=0;ilen;i++){ + if( (i%24)==0 ) printk("\n"); + printk("%2x ", ((u8*)skb->data)[i]); + } + printk("\n"); +} +#endif /*JOHN_DUMP*/ + return keyidx; +} + +#if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) +static int michael_mic(struct ieee80211_tkip_data *tkey, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + struct scatterlist sg[2]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + struct hash_desc desc; + int ret=0; +#endif + if (tkey->tfm_michael == NULL) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; + + //crypto_digest_init(tkey->tfm_michael); + //crypto_digest_setkey(tkey->tfm_michael, key, 8); + //crypto_digest_update(tkey->tfm_michael, sg, 2); + //crypto_digest_final(tkey->tfm_michael, mic); + + //return 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + crypto_digest_init(tkey->tfm_michael); + crypto_digest_setkey(tkey->tfm_michael, key, 8); + crypto_digest_update(tkey->tfm_michael, sg, 2); + crypto_digest_final(tkey->tfm_michael, mic); + + return 0; +#else +if (crypto_hash_setkey(tkey->tfm_michael, key, 8)) + return -1; + +// return 0; + desc.tfm = tkey->tfm_michael; + desc.flags = 0; + ret = crypto_hash_digest(&desc, sg, data_len + 16, mic); + return ret; +#endif +} +#else +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) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; +#else + sg_init_table(sg, 2); + sg_set_buf(&sg[0], hdr, 16); + sg_set_buf(&sg[1], data, data_len); +#endif + 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); +} +#endif + + + +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_ctl) & + (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; + } + + hdr[12] = 0; /* priority */ + + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + + +static int ieee80211_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + u8 *pos; + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *) skb->data; + + 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); + + // { david, 2006.9.1 + // fix the wpa process with wmm enabled. + if(IEEE80211_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl))) { + tkey->tx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07; + } + // } + pos = skb_put(skb, 8); + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + #else + if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + #endif + return -1; + + return 0; +} + + +#if WIRELESS_EXT >= 18 +static void ieee80211_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); +} +#elif WIRELESS_EXT >= 15 +static void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + char buf[128]; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=" + MAC_FMT ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC_ARG(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); +} +#else /* WIRELESS_EXT >= 15 */ +static inline void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ +} +#endif /* WIRELESS_EXT >= 15 */ + + +static int ieee80211_michael_mic_verify(struct sk_buff *skb, int keyidx, + int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + u8 mic[8]; + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *) skb->data; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + // { david, 2006.9.1 + // fix the wpa process with wmm enabled. + if(IEEE80211_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl))) { + tkey->rx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07; + } + // } + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + #else + if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + #endif + 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 " MAC_FMT " keyidx=%d\n", + skb->dev ? skb->dev->name : "N/A", MAC_ARG(hdr->addr2), + keyidx); + if (skb->dev) + ieee80211_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 ieee80211_tkip_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + int keyidx; + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + struct crypto_tfm *tfm = tkey->tfm_michael; + struct crypto_tfm *tfm2 = tkey->tfm_arc4; + #else + 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; + #endif + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; + + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + tkey->tfm_michael = tfm; + tkey->tfm_arc4 = tfm2; + #else + tkey->tx_tfm_michael = tfm; + tkey->tx_tfm_arc4 = tfm2; + tkey->rx_tfm_michael = tfm3; + tkey->rx_tfm_arc4 = tfm4; + #endif + + 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 ieee80211_tkip_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_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 * ieee80211_tkip_print_stats(char *p, void *priv) +{ + struct ieee80211_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 ieee80211_crypto_ops ieee80211_crypt_tkip = { + .name = "TKIP", + .init = ieee80211_tkip_init, + .deinit = ieee80211_tkip_deinit, + .encrypt_mpdu = ieee80211_tkip_encrypt, + .decrypt_mpdu = ieee80211_tkip_decrypt, + .encrypt_msdu = ieee80211_michael_mic_add, + .decrypt_msdu = ieee80211_michael_mic_verify, + .set_key = ieee80211_tkip_set_key, + .get_key = ieee80211_tkip_get_key, + .print_stats = ieee80211_tkip_print_stats, + .extra_prefix_len = 4 + 4, /* IV + ExtIV */ + .extra_postfix_len = 8 + 4, /* MIC + ICV */ + .owner = THIS_MODULE, +}; + + +int __init ieee80211_crypto_tkip_init(void) +{ + return ieee80211_register_crypto_ops(&ieee80211_crypt_tkip); +} + + +void __exit ieee80211_crypto_tkip_exit(void) +{ + ieee80211_unregister_crypto_ops(&ieee80211_crypt_tkip); +} + + +void ieee80211_tkip_null(void) +{ +// printk("============>%s()\n", __FUNCTION__); + return; +} + +#if 0 +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_tkip_null); +#else +EXPORT_SYMBOL_NOVERS(ieee80211_tkip_null); +#endif + + +module_init(ieee80211_crypto_tkip_init); +module_exit(ieee80211_crypto_tkip_exit); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_wep.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_wep.c new file mode 100644 index 0000000..d97b637 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_crypt_wep.c @@ -0,0 +1,383 @@ +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * 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 "ieee80211.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#include "rtl_crypto.h" +#else +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + #include +#else + #include +#endif +//#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: WEP"); +MODULE_LICENSE("GPL"); + + +struct prism2_wep_data { + u32 iv; +#define WEP_KEY_LEN 13 + u8 key[WEP_KEY_LEN + 1]; + u8 key_len; + u8 key_idx; + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + struct crypto_tfm *tfm; + #else + struct crypto_blkcipher *tx_tfm; + struct crypto_blkcipher *rx_tfm; + #endif +}; + + +static void * prism2_wep_init(int keyidx) +{ + struct prism2_wep_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = keyidx; + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + priv->tfm = crypto_alloc_tfm("arc4", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " + "crypto API arc4\n"); + goto fail; + } + #else + priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tx_tfm)) { + printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " + "crypto API arc4\n"); + priv->tx_tfm = NULL; + goto fail; + } + priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->rx_tfm)) { + printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " + "crypto API arc4\n"); + priv->rx_tfm = NULL; + goto fail; + } + #endif + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; + +fail: + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + if (priv) { + if (priv->tfm) + crypto_free_tfm(priv->tfm); + kfree(priv); + } + #else + if (priv) { + if (priv->tx_tfm) + crypto_free_blkcipher(priv->tx_tfm); + if (priv->rx_tfm) + crypto_free_blkcipher(priv->rx_tfm); + kfree(priv); + } + #endif + return NULL; +} + + +static void prism2_wep_deinit(void *priv) +{ + struct prism2_wep_data *_priv = priv; + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); + #else + if (_priv) { + if (_priv->tx_tfm) + crypto_free_blkcipher(_priv->tx_tfm); + if (_priv->rx_tfm) + crypto_free_blkcipher(_priv->rx_tfm); + } + #endif + kfree(priv); +} + + +/* 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 prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; +#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)) + struct blkcipher_desc desc = {.tfm = wep->tx_tfm}; +#endif + u32 klen, len; + u8 key[WEP_KEY_LEN + 3]; + u8 *pos; +#ifndef JOHN_HWSEC + u32 crc; + u8 *icv; + struct scatterlist sg; +#endif + if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + len = skb->len - hdr_len; + 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++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + +#ifndef JOHN_HWSEC + /* Append little-endian CRC32 and encrypt it to produce ICV */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + crc = ~crc32_le(~0, pos, len); +#else + crc = ~ether_crc_le(len, pos); +#endif + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + #if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4); + + return 0; + #else + crypto_blkcipher_setkey(wep->tx_tfm, key, klen); + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + #else + sg_init_one(&sg, pos, len + 4); + #endif + return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); + #endif +#endif /* JOHN_HWSEC */ + return 0; +} + + +/* 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 prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)) + struct blkcipher_desc desc = {.tfm = wep->rx_tfm}; + #endif + u32 klen, plen; + u8 key[WEP_KEY_LEN + 3]; + u8 keyidx, *pos; +#ifndef JOHN_HWSEC + u32 crc; + u8 icv[4]; + struct scatterlist sg; +#endif + 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; +#ifndef JOHN_HWSEC +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4); +#else + crypto_blkcipher_setkey(wep->rx_tfm, key, klen); + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + #else + sg_init_one(&sg, pos, plen + 4); + #endif + if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) + return -7; +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + crc = ~crc32_le(~0, pos, plen); +#else + crc = ~ether_crc_le(plen, pos); +#endif + 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; + } +#endif /* JOHN_HWSEC */ + + /* 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 prism2_wep_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_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 prism2_wep_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < wep->key_len) + return -1; + + memcpy(key, wep->key, wep->key_len); + + return wep->key_len; +} + + +static char * prism2_wep_print_stats(char *p, void *priv) +{ + struct prism2_wep_data *wep = priv; + p += sprintf(p, "key[%d] alg=WEP len=%d\n", + wep->key_idx, wep->key_len); + return p; +} + + +static struct ieee80211_crypto_ops ieee80211_crypt_wep = { + .name = "WEP", + .init = prism2_wep_init, + .deinit = prism2_wep_deinit, + .encrypt_mpdu = prism2_wep_encrypt, + .decrypt_mpdu = prism2_wep_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = prism2_wep_set_key, + .get_key = prism2_wep_get_key, + .print_stats = prism2_wep_print_stats, + .extra_prefix_len = 4, /* IV */ + .extra_postfix_len = 4, /* ICV */ + .owner = THIS_MODULE, +}; + + +int __init ieee80211_crypto_wep_init(void) +{ + return ieee80211_register_crypto_ops(&ieee80211_crypt_wep); +} + + +void __exit ieee80211_crypto_wep_exit(void) +{ + ieee80211_unregister_crypto_ops(&ieee80211_crypt_wep); +} + + +void ieee80211_wep_null(void) +{ +// printk("============>%s()\n", __FUNCTION__); + return; +} +#if 0 +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_wep_null); +#else +EXPORT_SYMBOL_NOVERS(ieee80211_wep_null); +#endif + +module_init(ieee80211_crypto_wep_init); +module_exit(ieee80211_crypto_wep_exit); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_module.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_module.c new file mode 100644 index 0000000..b85f6dd --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_module.c @@ -0,0 +1,385 @@ +/******************************************************************************* + + Copyright(c) 2004 Intel Corporation. All rights reserved. + + Portions of this file are based on the WEP enablement code provided by the + Host AP project hostap-drivers v0.1.3 + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + James P. Ketrenos + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +MODULE_DESCRIPTION("802.11 data/management/control stack"); +MODULE_AUTHOR("Copyright (C) 2004 Intel Corporation "); +MODULE_LICENSE("GPL"); + +#define DRV_NAME "ieee80211" + +static inline int ieee80211_networks_allocate(struct ieee80211_device *ieee) +{ + if (ieee->networks) + return 0; + + ieee->networks = kmalloc( + MAX_NETWORK_COUNT * sizeof(struct ieee80211_network), + GFP_KERNEL); + if (!ieee->networks) { + printk(KERN_WARNING "%s: Out of memory allocating beacons\n", + ieee->dev->name); + return -ENOMEM; + } + + memset(ieee->networks, 0, + MAX_NETWORK_COUNT * sizeof(struct ieee80211_network)); + + return 0; +} + +static inline void ieee80211_networks_free(struct ieee80211_device *ieee) +{ + if (!ieee->networks) + return; + kfree(ieee->networks); + ieee->networks = NULL; +} + +static inline void ieee80211_networks_initialize(struct ieee80211_device *ieee) +{ + int i; + + INIT_LIST_HEAD(&ieee->network_free_list); + INIT_LIST_HEAD(&ieee->network_list); + for (i = 0; i < MAX_NETWORK_COUNT; i++) + list_add_tail(&ieee->networks[i].list, &ieee->network_free_list); +} + + +struct net_device *alloc_ieee80211(int sizeof_priv) +{ + struct ieee80211_device *ieee; + struct net_device *dev; + int i,err; + + IEEE80211_DEBUG_INFO("Initializing...\n"); + + dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv); + if (!dev) { + IEEE80211_ERROR("Unable to network device.\n"); + goto failed; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + ieee = netdev_priv(dev); +#else + ieee = (struct ieee80211_device *)dev->priv; +#endif + + ieee->dev = dev; + + err = ieee80211_networks_allocate(ieee); + if (err) { + IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", + err); + goto failed; + } + ieee80211_networks_initialize(ieee); + + /* Default fragmentation threshold is maximum payload size */ + ieee->fts = DEFAULT_FTS; + ieee->scan_age = DEFAULT_MAX_SCAN_AGE; + ieee->open_wep = 1; + + /* Default to enabling full open WEP with host based encrypt/decrypt */ + ieee->host_encrypt = 1; + ieee->host_decrypt = 1; + ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ + + INIT_LIST_HEAD(&ieee->crypt_deinit_list); + init_timer(&ieee->crypt_deinit_timer); + ieee->crypt_deinit_timer.data = (unsigned long)ieee; + ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler; + + spin_lock_init(&ieee->lock); + spin_lock_init(&ieee->wpax_suitlist_lock); + + ieee->wpax_type_set = 0; + ieee->wpa_enabled = 0; + ieee->tkip_countermeasures = 0; + ieee->drop_unencrypted = 0; + ieee->privacy_invoked = 0; + ieee->ieee802_1x = 1; + ieee->raw_tx = 0; +#ifdef _RTL8187_EXT_PATCH_ + for (i=0; icryptlist[i] = (struct ieee80211_crypt_data_list*) kmalloc(sizeof(struct ieee80211_crypt_data_list), GFP_KERNEL); + if (NULL == ieee->cryptlist[i]) + { + printk("error kmalloc cryptlist\n"); + goto failed; + } + memset(ieee->cryptlist[i], 0, sizeof(struct ieee80211_crypt_data_list)); + + } +#endif + ieee80211_softmac_init(ieee); + + for (i = 0; i < IEEE_IBSS_MAC_HASH_SIZE; i++) + INIT_LIST_HEAD(&ieee->ibss_mac_hash[i]); + + for (i = 0; i < IEEE_MESH_MAC_HASH_SIZE; i++) + INIT_LIST_HEAD(&ieee->mesh_mac_hash[i]); + + for (i = 0; i < 17; i++) { + ieee->last_rxseq_num[i] = -1; + ieee->last_rxfrag_num[i] = -1; + ieee->last_packet_time[i] = 0; + } +#if 1 //added these to autoload encryption module. WB + ieee80211_tkip_null(); + ieee80211_wep_null(); + ieee80211_ccmp_null(); +#endif + return dev; + + failed: +#ifdef _RTL8187_EXT_PATCH_ + for (i=0; icryptlist[i]==NULL){ + continue; + } + kfree(ieee->cryptlist[i]); + ieee->cryptlist[i] = NULL; + + } +#endif + if (dev) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + free_netdev(dev); +#else + kfree(dev); +#endif + return NULL; +} + + +void free_ieee80211(struct net_device *dev) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + struct ieee80211_device *ieee = netdev_priv(dev); +#else + struct ieee80211_device *ieee = (struct ieee80211_device *)dev->priv; +#endif + int i;//,j; + struct list_head *p, *q; + + + ieee80211_softmac_free(ieee); + del_timer_sync(&ieee->crypt_deinit_timer); + ieee80211_crypt_deinit_entries(ieee, 1); +#if 1 + ieee80211_tkip_null(); + ieee80211_wep_null(); + ieee80211_ccmp_null(); +#endif + for (i = 0; i < WEP_KEYS; i++) { +#ifdef _RTL8187_EXT_PATCH_ +{ + // int j; + for (j=0;jcryptlist[j] == NULL) + continue; + struct ieee80211_crypt_data *crypt = ieee->cryptlist[j]->crypt[i]; +#else + struct ieee80211_crypt_data *crypt = ieee->crypt[i]; +#endif + if (crypt) { + if (crypt->ops) { + crypt->ops->deinit(crypt->priv); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + module_put(crypt->ops->owner); +#else + __MOD_DEC_USE_COUNT(crypt->ops->owner); +#endif + } + kfree(crypt); +#ifdef _RTL8187_EXT_PATCH_ + ieee->cryptlist[j]->crypt[i] = NULL; + //kfree(ieee->cryptlist[j]); + //ieee->cryptlist[j] = NULL; +#else + ieee->crypt[i] = NULL; +#endif + } +#ifdef _RTL8187_EXT_PATCH_ + } + } +#endif +} +#ifdef _RTL8187_EXT_PATCH_ +for(j=0;jcryptlist[j]) + { + kfree(ieee->cryptlist[j]); + ieee->cryptlist[j] = NULL; + } + } + + for (i = 0; i < IEEE_MESH_MAC_HASH_SIZE; i++) { + list_for_each_safe(p, q, &ieee->mesh_mac_hash[i]) { + kfree(list_entry(p, struct ieee_mesh_seq, list)); + list_del(p); + } + } +#endif + ieee80211_networks_free(ieee); + + for (i = 0; i < IEEE_IBSS_MAC_HASH_SIZE; i++) { + list_for_each_safe(p, q, &ieee->ibss_mac_hash[i]) { + kfree(list_entry(p, struct ieee_ibss_seq, list)); + list_del(p); + } + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + free_netdev(dev); +#else + kfree(dev); +#endif +} + +#ifdef CONFIG_IEEE80211_DEBUG + +static int debug = 0; +u32 ieee80211_debug_level = 0; +struct proc_dir_entry *ieee80211_proc = NULL; + +static int show_debug_level(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + return snprintf(page, count, "0x%08X\n", ieee80211_debug_level); +} + +static int store_debug_level(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char buf[] = "0x00000000"; + unsigned long len = min(sizeof(buf) - 1, (u32)count); + char *p = (char *)buf; + unsigned long val; + + if (copy_from_user(buf, buffer, len)) + return count; + buf[len] = 0; + if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { + p++; + if (p[0] == 'x' || p[0] == 'X') + p++; + val = simple_strtoul(p, &p, 16); + } else + val = simple_strtoul(p, &p, 10); + if (p == buf) + printk(KERN_INFO DRV_NAME + ": %s is not in hex or decimal form.\n", buf); + else + ieee80211_debug_level = val; + + return strnlen(buf, count); +} + +static int __init ieee80211_init(void) +{ + struct proc_dir_entry *e; + + ieee80211_debug_level = debug; + ieee80211_proc = create_proc_entry(DRV_NAME, S_IFDIR, proc_net); + if (ieee80211_proc == NULL) { + IEEE80211_ERROR("Unable to create " DRV_NAME + " proc directory\n"); + return -EIO; + } + e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR, + ieee80211_proc); + if (!e) { + remove_proc_entry(DRV_NAME, proc_net); + ieee80211_proc = NULL; + return -EIO; + } + e->read_proc = show_debug_level; + e->write_proc = store_debug_level; + e->data = NULL; + + return 0; +} + +static void __exit ieee80211_exit(void) +{ + if (ieee80211_proc) { + remove_proc_entry("debug_level", ieee80211_proc); + remove_proc_entry(DRV_NAME, proc_net); + ieee80211_proc = NULL; + } +} + +#include +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); + + +module_exit(ieee80211_exit); +module_init(ieee80211_init); +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(alloc_ieee80211); +EXPORT_SYMBOL(free_ieee80211); +#else +EXPORT_SYMBOL_NOVERS(alloc_ieee80211); +EXPORT_SYMBOL_NOVERS(free_ieee80211); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_rx.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_rx.c new file mode 100644 index 0000000..769203c --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_rx.c @@ -0,0 +1,2074 @@ +/* + * Original code based Host AP (software wireless LAN access point) driver + * for Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2004, Intel 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. See README and COPYING for + * more details. + ****************************************************************************** + + Few modifications for Realtek's Wi-Fi drivers by + Andrea Merello + + A special thanks goes to Realtek for their support ! + +******************************************************************************/ + + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + +static inline void ieee80211_monitor_rx(struct ieee80211_device *ieee, + struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_ctl); + + skb->dev = ieee->dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + skb_reset_mac_header(skb); +#else + skb->mac.raw = skb->data; +#endif + + //skb->mac.raw = skb->data; + skb_pull(skb, ieee80211_get_hdrlen(fc)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_80211_RAW); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static struct ieee80211_frag_entry * +ieee80211_frag_cache_find(struct ieee80211_device *ieee, unsigned int seq, + unsigned int frag, u8 tid,u8 *src, u8 *dst) +{ + struct ieee80211_frag_entry *entry; + int i; + + for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) { + entry = &ieee->frag_cache[tid][i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + IEEE80211_DEBUG_FRAG( + "expiring fragment cache entry " + "seq=%u last_frag=%u\n", + entry->seq, entry->last_frag); + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + return entry; + } + + return NULL; +} + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff * +ieee80211_frag_cache_get(struct ieee80211_device *ieee, + struct ieee80211_hdr *hdr) +{ + struct sk_buff *skb = NULL; + u16 fc = le16_to_cpu(hdr->frame_ctl); + u16 sc = le16_to_cpu(hdr->seq_ctl); + unsigned int frag = WLAN_GET_SEQ_FRAG(sc); + unsigned int seq = WLAN_GET_SEQ_SEQ(sc); + struct ieee80211_frag_entry *entry; + struct ieee80211_hdr_3addr_QOS *hdr_3addr_QoS; + struct ieee80211_hdr_QOS *hdr_4addr_QoS; + u8 tid; + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + { + tid = (hdr->addr2[ETH_ALEN-2] ^ hdr->addr2[ETH_ALEN-1]) & IEEE80211_QOS_TID; + } + else +#endif + if (((fc & IEEE80211_FCTL_DSTODS) == IEEE80211_FCTL_DSTODS)&&IEEE80211_QOS_HAS_SEQ(fc)) { + hdr_4addr_QoS = (struct ieee80211_hdr_QOS *)hdr; + tid = le16_to_cpu(hdr_4addr_QoS->QOS_ctl) & IEEE80211_QOS_TID; + tid = UP2AC(tid); + tid ++; + } else if (IEEE80211_QOS_HAS_SEQ(fc)) { + hdr_3addr_QoS = (struct ieee80211_hdr_3addr_QOS *)hdr; + tid = le16_to_cpu(hdr_3addr_QoS->QOS_ctl) & IEEE80211_QOS_TID; + tid = UP2AC(tid); + tid ++; + } else { + tid = 0; + } + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(ieee->dev->mtu + + sizeof(struct ieee80211_hdr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + + ETH_ALEN /* WDS */ + + (IEEE80211_QOS_HAS_SEQ(fc)?2:0) /* QOS Control */); + if (skb == NULL) + return NULL; + + entry = &ieee->frag_cache[tid][ieee->frag_next_idx[tid]]; + ieee->frag_next_idx[tid]++; + if (ieee->frag_next_idx[tid] >= IEEE80211_FRAG_CACHE_LEN) + ieee->frag_next_idx[tid] = 0; + + if (entry->skb != NULL) + dev_kfree_skb_any(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); + memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received */ + entry = ieee80211_frag_cache_find(ieee, seq, frag, tid,hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + + +/* Called only as a tasklet (software IRQ) */ +static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee, + struct ieee80211_hdr *hdr) +{ + u16 fc = le16_to_cpu(hdr->frame_ctl); + u16 sc = le16_to_cpu(hdr->seq_ctl); + unsigned int seq = WLAN_GET_SEQ_SEQ(sc); + struct ieee80211_frag_entry *entry; + struct ieee80211_hdr_3addr_QOS *hdr_3addr_QoS; + struct ieee80211_hdr_QOS *hdr_4addr_QoS; + u8 tid; + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + { + tid = (hdr->addr2[ETH_ALEN-2] ^ hdr->addr2[ETH_ALEN-1]) & IEEE80211_QOS_TID; + } + else +#endif + if(((fc & IEEE80211_FCTL_DSTODS) == IEEE80211_FCTL_DSTODS)&&IEEE80211_QOS_HAS_SEQ(fc)) { + hdr_4addr_QoS = (struct ieee80211_hdr_QOS *)hdr; + tid = le16_to_cpu(hdr_4addr_QoS->QOS_ctl) & IEEE80211_QOS_TID; + tid = UP2AC(tid); + tid ++; + } else if (IEEE80211_QOS_HAS_SEQ(fc)) { + hdr_3addr_QoS = (struct ieee80211_hdr_3addr_QOS *)hdr; + tid = le16_to_cpu(hdr_3addr_QoS->QOS_ctl) & IEEE80211_QOS_TID; + tid = UP2AC(tid); + tid ++; + } else { + tid = 0; + } + + entry = ieee80211_frag_cache_find(ieee, seq, -1, tid,hdr->addr2, + hdr->addr1); + + if (entry == NULL) { + IEEE80211_DEBUG_FRAG( + "could not invalidate fragment cache " + "entry (seq=%u)\n", seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + + + +/* ieee80211_rx_frame_mgtmt + * + * Responsible for handling management control frames + * + * Called by ieee80211_rx */ +static inline int +ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats, u16 type, + u16 stype) +{ + /* On the struct stats definition there is written that + * this is not mandatory.... but seems that the probe + * response parser uses it + */ + struct ieee80211_hdr * hdr = (struct ieee80211_hdr*)skb->data; + rx_stats->len = skb->len; + ieee80211_rx_mgt(ieee,(struct ieee80211_hdr *)skb->data,rx_stats); + + if ((ieee->state == IEEE80211_LINKED) && (memcmp(hdr->addr3, ieee->current_network.bssid, ETH_ALEN))) + { + dev_kfree_skb_any(skb); + return 0; + } + + ieee80211_rx_frame_softmac(ieee, skb, rx_stats, type, stype); + + dev_kfree_skb_any(skb); + + return 0; + + #ifdef NOT_YET + if (ieee->iw_mode == IW_MODE_MASTER) { + printk(KERN_DEBUG "%s: Master mode not yet suppported.\n", + ieee->dev->name); + return 0; +/* + hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr *) + skb->data);*/ + } + + if (ieee->hostapd && type == IEEE80211_TYPE_MGMT) { + if (stype == WLAN_FC_STYPE_BEACON && + ieee->iw_mode == IW_MODE_MASTER) { + struct sk_buff *skb2; + /* Process beacon frames also in kernel driver to + * update STA(AP) table statistics */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + hostap_rx(skb2->dev, skb2, rx_stats); + } + + /* send management frames to the user space daemon for + * processing */ + ieee->apdevstats.rx_packets++; + ieee->apdevstats.rx_bytes += skb->len; + prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); + return 0; + } + + if (ieee->iw_mode == IW_MODE_MASTER) { + if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type, stype); + return -1; + } + + hostap_rx(skb->dev, skb, rx_stats); + return 0; + } + + printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " + "received in non-Host AP mode\n", skb->dev->name); + return -1; + #endif +} + + + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char rfc1042_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char bridge_tunnel_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + +/* Called by ieee80211_rx_frame_decrypt */ +static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee, + struct sk_buff *skb, size_t hdrlen) +{ + struct net_device *dev = ieee->dev; + u16 fc, ethertype; + struct ieee80211_hdr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + + /* check that the frame is unicast frame to us */ + if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ +// pos = skb->data + 24; + pos = skb->data + hdrlen; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + +/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +static inline int +ieee80211_rx_frame_decrypt(struct ieee80211_device* ieee, struct sk_buff *skb, + struct ieee80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && (ieee->ext_patch_ieee80211_rx_frame_get_hdrlen)) + { + hdrlen = ieee->ext_patch_ieee80211_rx_frame_get_hdrlen(ieee, skb); + } + else +#endif + hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + +#ifdef CONFIG_IEEE80211_CRYPT_TKIP + if (ieee->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "received packet from " MAC_FMT "\n", + ieee->dev->name, MAC_ARG(hdr->addr2)); + } + return -1; + } +#endif + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + IEEE80211_DEBUG_DROP( + "decryption failed (SA=" MAC_FMT + ") res=%d\n", MAC_ARG(hdr->addr2), res); + if (res == -2) + IEEE80211_DEBUG_DROP("Decryption failed ICV " + "mismatch (key %d)\n", + skb->data[hdrlen + 3] >> 6); + ieee->ieee_stats.rx_discards_undecryptable++; + return -1; + } + + return res; +} + + +/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +static inline int +ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device* ieee, struct sk_buff *skb, + int keyidx, struct ieee80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && (ieee->ext_patch_ieee80211_rx_frame_get_hdrlen)) + { + hdrlen = ieee->ext_patch_ieee80211_rx_frame_get_hdrlen(ieee, skb); + } + else +#endif + hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" + " (SA=" MAC_FMT " keyidx=%d)\n", + ieee->dev->name, MAC_ARG(hdr->addr2), keyidx); + return -1; + } + + return 0; +} + + +/* this function is stolen from ipw2200 driver*/ +#define IEEE_PACKET_RETRY_TIME (5*HZ) +static int is_duplicate_packet(struct ieee80211_device *ieee, + struct ieee80211_hdr *header) +{ + u16 fc = le16_to_cpu(header->frame_ctl); + u16 sc = le16_to_cpu(header->seq_ctl); + u16 seq = WLAN_GET_SEQ_SEQ(sc); + u16 frag = WLAN_GET_SEQ_FRAG(sc); + u16 *last_seq, *last_frag; + unsigned long *last_time; + struct ieee80211_hdr_3addr_QOS *hdr_3addr_QoS; + struct ieee80211_hdr_QOS *hdr_4addr_QoS; + u8 tid; + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + { + tid = (header->addr2[ETH_ALEN-2] ^ header->addr2[ETH_ALEN-1]) & IEEE80211_QOS_TID; + } + else +#endif + //TO2DS and QoS + if(((fc & IEEE80211_FCTL_DSTODS) == IEEE80211_FCTL_DSTODS)&&IEEE80211_QOS_HAS_SEQ(fc)) { + hdr_4addr_QoS = (struct ieee80211_hdr_QOS *)header; + tid = le16_to_cpu(hdr_4addr_QoS->QOS_ctl) & IEEE80211_QOS_TID; + tid = UP2AC(tid); + tid ++; + } else if(IEEE80211_QOS_HAS_SEQ(fc)) { //QoS + hdr_3addr_QoS = (struct ieee80211_hdr_3addr_QOS*)header; + tid = le16_to_cpu(hdr_3addr_QoS->QOS_ctl) & IEEE80211_QOS_TID; + tid = UP2AC(tid); + tid ++; + } else { // no QoS + tid = 0; + } + + switch (ieee->iw_mode) { + case IW_MODE_ADHOC: + { + struct list_head *p; + struct ieee_ibss_seq *entry = NULL; + u8 *mac = header->addr2; + int index = mac[5] % IEEE_IBSS_MAC_HASH_SIZE; + //for (pos = (head)->next; pos != (head); pos = pos->next) + __list_for_each(p, &ieee->ibss_mac_hash[index]) { + entry = list_entry(p, struct ieee_ibss_seq, list); + if (!memcmp(entry->mac, mac, ETH_ALEN)) + break; + } + // if (memcmp(entry->mac, mac, ETH_ALEN)){ + if (p == &ieee->ibss_mac_hash[index]) { + entry = kmalloc(sizeof(struct ieee_ibss_seq), GFP_ATOMIC); + if (!entry) { + printk(KERN_WARNING "Cannot malloc new mac entry\n"); + return 0; + } + memcpy(entry->mac, mac, ETH_ALEN); + entry->seq_num[tid] = seq; + entry->frag_num[tid] = frag; + entry->packet_time[tid] = jiffies; + list_add(&entry->list, &ieee->ibss_mac_hash[index]); + return 0; + } + last_seq = &entry->seq_num[tid]; + last_frag = &entry->frag_num[tid]; + last_time = &entry->packet_time[tid]; + break; + } + + case IW_MODE_INFRA: + last_seq = &ieee->last_rxseq_num[tid]; + last_frag = &ieee->last_rxfrag_num[tid]; + last_time = &ieee->last_packet_time[tid]; + + break; + default: +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + { +#if 0 + printk("==============> tid = %d\n", tid); + last_seq = &ieee->last_rxseq_num[tid]; + last_frag = &ieee->last_rxfrag_num[tid]; + last_time = &ieee->last_packet_time[tid]; +#else + struct list_head *p; + struct ieee_mesh_seq *entry = NULL; + u8 *mac = header->addr2; + int index = mac[5] % IEEE_IBSS_MAC_HASH_SIZE; + + __list_for_each(p, &ieee->mesh_mac_hash[index]) { + entry = list_entry(p, struct ieee_mesh_seq, list); + if (!memcmp(entry->mac, mac, ETH_ALEN)) + break; + } + if (p == &ieee->mesh_mac_hash[index]) { + entry = kmalloc(sizeof(struct ieee_mesh_seq), GFP_ATOMIC); + if (!entry) { + printk(KERN_WARNING "Cannot malloc new mac entry for mesh\n"); + return 0; + } + memcpy(entry->mac, mac, ETH_ALEN); + entry->seq_num = seq; + entry->frag_num = frag; + entry->packet_time = jiffies; + list_add(&entry->list, &ieee->mesh_mac_hash[index]); + return 0; + } + last_seq = &entry->seq_num; + last_frag = &entry->frag_num; + last_time = &entry->packet_time; +#endif + break; + } + else +#endif + return 0; + } + +// if(tid != 0) { +// printk(KERN_WARNING ":)))))))))))%x %x %x, fc(%x)\n", tid, *last_seq, seq, header->frame_ctl); +// } + if ((*last_seq == seq) && + time_after(*last_time + IEEE_PACKET_RETRY_TIME, jiffies)) { + if (*last_frag == frag){ + //printk(KERN_WARNING "[1] go drop!\n"); + goto drop; + + } + if (*last_frag + 1 != frag) + /* out-of-order fragment */ + //printk(KERN_WARNING "[2] go drop!\n"); + goto drop; + } else + *last_seq = seq; + + *last_frag = frag; + *last_time = jiffies; + return 0; + +drop: +// BUG_ON(!(fc & IEEE80211_FCTL_RETRY)); +// printk("DUP\n"); + + return 1; +} +#ifdef JUST_FOR_87SEMESH +#define ActionHeadLen 30 +#define WIFI_MESH_TYPE IEEE80211_FTYPE_DATA +#define WIFI_11S_MESH_ACTION 0x00A0 +#endif + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). */ +int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats) +{ + struct net_device *dev = ieee->dev; + struct ieee80211_hdr *hdr; + //struct ieee80211_hdr_3addr_QOS *hdr; + + size_t hdrlen; + u16 fc, type, stype, sc; + struct net_device_stats *stats; + unsigned int frag; + u8 *payload; + u16 ethertype; +#ifdef NOT_YET + struct net_device *wds = NULL; + struct sk_buff *skb2 = NULL; + struct net_device *wds = NULL; + int frame_authorized = 0; + int from_assoc_ap = 0; + void *sta = NULL; +#endif +// u16 QOS_ctl = 0; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + struct ieee80211_crypt_data *crypt = NULL; + int keyidx = 0; + + //Added for mesh by Lawrence. + //u8 status; + //u32 flags; + + // cheat the the hdr type + hdr = (struct ieee80211_hdr *)skb->data; + stats = &ieee->stats; + + if (skb->len < 10) { + printk(KERN_INFO "%s: SKB length < 10\n", + dev->name); + goto rx_dropped; + } +#if 0 +//{added by david for filter the packet listed in the filter table +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && (ieee->ext_patch_ieee80211_acl_query)) + { + if(!ieee->ext_patch_ieee80211_acl_query(ieee, hdr->addr2)) + goto rx_dropped; + } +#endif +//} +#endif + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + //Because 87se's bad feature,do more handle. +#ifdef JUST_FOR_87SEMESH + +u8 tmphead[ActionHeadLen]; + if(type ==WIFI_MESH_TYPE && stype== WIFI_11S_MESH_ACTION ) + //head=sizeof(struct ieee80211_hdr)=30 + { + memset(tmphead,0,ActionHeadLen); + memcpy(tmphead,skb->data,ActionHeadLen); + + skb_pull(skb,ActionHeadLen+2); + memcpy(skb_push(skb,ActionHeadLen),tmphead,ActionHeadLen); + hdr = (struct ieee80211_hdr *)skb->data; + } + +#endif + sc = le16_to_cpu(hdr->seq_ctl); + + frag = WLAN_GET_SEQ_FRAG(sc); +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && (ieee->ext_patch_ieee80211_rx_frame_get_hdrlen)) + { + hdrlen = ieee->ext_patch_ieee80211_rx_frame_get_hdrlen(ieee, skb); + if(skb->len < hdrlen) + goto rx_dropped; + } + else +#endif + hdrlen = ieee80211_get_hdrlen(fc); + +#ifdef NOT_YET +#if WIRELESS_EXT > 15 + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (iface->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = rx_stats->signal; + wstats.noise = rx_stats->noise; + wstats.updated = 6; /* No qual value */ + /* Update spy records */ + wireless_spy_update(dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_EXT > 15 */ + hostap_update_rx_stats(local->ap, hdr, rx_stats); +#endif + +#if WIRELESS_EXT > 15 + if (ieee->iw_mode == IW_MODE_MONITOR) { + ieee80211_monitor_rx(ieee, skb, rx_stats); + stats->rx_packets++; + stats->rx_bytes += skb->len; + return 1; + } +#endif + if (ieee->host_decrypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; +#ifdef _RTL8187_EXT_PATCH_ + + crypt = ieee->cryptlist[0]->crypt[idx]; +#if 0 + { + int i = ieee80211_find_MP(ieee, ((struct ieee80211_hdr*)skb->data)->addr2); + if (i == -1) + { + printk("error find entry in entry list\n"); + goto rx_dropped; + } + //printk("%s():"MAC_FMT", find in index:%d", __FUNCTION__, MAC_ARG(((struct ieee80211_hdr*)skb->data)->addr2), i); + crypt = ieee->cryptlist[i]->crypt[idx]; + } +#endif +#else + crypt = ieee->crypt[idx]; +#endif + +#ifdef NOT_YET + sta = NULL; + + /* Use station specific key to override default keys if the + * receiver address is a unicast address ("individual RA"). If + * bcrx_sta_key parameter is set, station specific key is used + * even with broad/multicast targets (this is against IEEE + * 802.11, but makes it easier to use different keys with + * stations that do not support WEP key mapping). */ + + if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) + (void) hostap_handle_sta_crypto(local, hdr, &crypt, + &sta); +#endif + + /* allow NULL decrypt to indicate an station specific override + * for default encryption */ + if (crypt && (crypt->ops == NULL || + crypt->ops->decrypt_mpdu == NULL)) + crypt = NULL; + + if (!crypt && (fc & IEEE80211_FCTL_WEP)) { + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. */ + IEEE80211_DEBUG_DROP("Decryption failed (not set)" + " (SA=" MAC_FMT ")\n", + MAC_ARG(hdr->addr2)); + ieee->ieee_stats.rx_discards_undecryptable++; + goto rx_dropped; + } + } + + if (skb->len < IEEE80211_DATA_HDR3_LEN) + goto rx_dropped; + + // if QoS enabled, should check the sequence for each of the AC + if (is_duplicate_packet(ieee, hdr)) + goto rx_dropped; + +#ifdef _RTL8187_EXT_PATCH_ + if( ieee->iw_mode == ieee->iw_ext_mode && ieee->ext_patch_ieee80211_rx_mgt_update_expire ) + ieee->ext_patch_ieee80211_rx_mgt_update_expire( ieee, skb ); +#endif + + if (type == IEEE80211_FTYPE_MGMT) { + + #if 0 + if ( stype == IEEE80211_STYPE_AUTH && + fc & IEEE80211_FCTL_WEP && ieee->host_decrypt && + (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) + { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from " MAC_FMT "\n", dev->name, + MAC_ARG(hdr->addr2)); + /* TODO: could inform hostapd about this so that it + * could send auth failure report */ + goto rx_dropped; + } + #endif + + + if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } + +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_on_rx) + { + if(ieee->ext_patch_ieee80211_rx_on_rx(ieee, skb, rx_stats, type, stype)==0) + { + goto rx_exit; + } + } +#endif + + /* Data frame - extract src/dst addresses */ + switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case IEEE80211_FCTL_FROMDS: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + memcpy(bssid, hdr->addr2, ETH_ALEN); + break; + case IEEE80211_FCTL_TODS: + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + memcpy(bssid, hdr->addr1, ETH_ALEN); + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + if (skb->len < IEEE80211_DATA_HDR4_LEN) + goto rx_dropped; + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + memcpy(bssid, ieee->current_network.bssid, ETH_ALEN); + break; + case 0: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + memcpy(bssid, hdr->addr3, ETH_ALEN); + break; + } + +#ifdef NOT_YET + if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) + goto rx_dropped; + if (wds) { + skb->dev = dev = wds; + stats = hostap_get_stats(dev); + } + + if (ieee->iw_mode == IW_MODE_MASTER && !wds && + (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS && + ieee->stadev && + memcmp(hdr->addr2, ieee->assoc_ap_addr, ETH_ALEN) == 0) { + /* Frame from BSSID of the AP for which we are a client */ + skb->dev = dev = ieee->stadev; + stats = hostap_get_stats(dev); + from_assoc_ap = 1; + } +#endif + + dev->last_rx = jiffies; + +#ifdef NOT_YET + if ((ieee->iw_mode == IW_MODE_MASTER || + ieee->iw_mode == IW_MODE_REPEAT) && + !from_assoc_ap) { + switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, + wds != NULL)) { + case AP_RX_CONTINUE_NOT_AUTHORIZED: + frame_authorized = 0; + break; + case AP_RX_CONTINUE: + frame_authorized = 1; + break; + case AP_RX_DROP: + goto rx_dropped; + case AP_RX_EXIT: + goto rx_exit; + } + } +#endif + +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_is_valid_framectl) + { + if(ieee->ext_patch_ieee80211_rx_is_valid_framectl(ieee, fc, type, stype)==0) + goto rx_dropped; + } + else +#endif + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + if (stype != IEEE80211_STYPE_DATA && + stype != IEEE80211_STYPE_DATA_CFACK && + stype != IEEE80211_STYPE_DATA_CFPOLL && + stype != IEEE80211_STYPE_DATA_CFACKPOLL&& + stype != IEEE80211_STYPE_QOS_DATA//add by David,2006.8.4 + ) { + if (stype != IEEE80211_STYPE_NULLFUNC) + IEEE80211_DEBUG_DROP( + "RX: dropped data frame " + "with no data (type=0x%02x, " + "subtype=0x%02x, len=%d)\n", + type, stype, skb->len); + goto rx_dropped; + } + + if (memcmp(bssid, ieee->current_network.bssid, ETH_ALEN)) + goto rx_dropped; + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ +#ifdef _RTL8187_EXT_PATCH_ + if (ieee->host_decrypt && crypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + if (ieee->iw_ext_mode == ieee->iw_mode) //if in mesh mode + { + int i = ieee80211_find_MP(ieee, ((struct ieee80211_hdr*)skb->data)->addr2, 0); + if (i == -1) + { + printk("error find entry in entry list\n"); + goto rx_dropped; + } + // printk("%s():"MAC_FMT", find in index:%d\n", __FUNCTION__, MAC_ARG(((struct ieee80211_hdr*)skb->data)->addr2), i); + if (ieee->cryptlist[i]&&ieee->cryptlist[i]->crypt[idx]) + crypt = ieee->cryptlist[i]->crypt[idx]; + + else + crypt = NULL; + } + else + crypt = ieee->cryptlist[0]->crypt[idx]; + } +#endif + + if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0) + goto rx_dropped; + + hdr = (struct ieee80211_hdr *) skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + // PR: FIXME: hostap has additional conditions in the "if" below: + // ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + if ((frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) { + int flen; + struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr); + IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); + + if (!frag_skb) { + IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG, + "Rx cannot get skb from fragment " + "cache (morefrag=%d seq=%u frag=%u)\n", + (fc & IEEE80211_FCTL_MOREFRAGS) != 0, + WLAN_GET_SEQ_SEQ(sc), frag); + goto rx_dropped; + } + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + printk(KERN_WARNING "%s: host decrypted and " + "reassembled frame did not fit skb\n", + dev->name); + ieee80211_frag_cache_invalidate(ieee, hdr); + goto rx_dropped; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data, flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, + flen); + } + dev_kfree_skb_any(skb); + skb = NULL; + + if (fc & IEEE80211_FCTL_MOREFRAGS) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received */ + goto rx_exit; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache */ + skb = frag_skb; + hdr = (struct ieee80211_hdr *) skb->data; + ieee80211_frag_cache_invalidate(ieee, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ + if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct ieee80211_hdr *) skb->data; + if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep) { + if (/*ieee->ieee802_1x &&*/ + ieee80211_is_eapol_frame(ieee, skb, hdrlen)) { + +#ifdef CONFIG_IEEE80211_DEBUG + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + struct eapol *eap = (struct eapol *)(skb->data + + 24); + IEEE80211_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n", + eap_get_type(eap->type)); +#endif + } else { + IEEE80211_DEBUG_DROP( + "encryption configured, but RX " + "frame not encrypted (SA=" MAC_FMT ")\n", + MAC_ARG(hdr->addr2)); + goto rx_dropped; + } + } + +#ifdef CONFIG_IEEE80211_DEBUG + if (crypt && !(fc & IEEE80211_FCTL_WEP) && + ieee80211_is_eapol_frame(ieee, skb)) { + struct eapol *eap = (struct eapol *)(skb->data + + 24); + IEEE80211_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n", + eap_get_type(eap->type)); + } +#endif + + if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep && + !ieee80211_is_eapol_frame(ieee, skb, hdrlen)) { + IEEE80211_DEBUG_DROP( + "dropped unencrypted RX data " + "frame from " MAC_FMT + " (drop_unencrypted=1)\n", + MAC_ARG(hdr->addr2)); + goto rx_dropped; + } +/* + if(ieee80211_is_eapol_frame(ieee, skb, hdrlen)) { + printk(KERN_WARNING "RX: IEEE802.1X EPAOL frame!\n"); + } +*/ + /* skb: hdr + (possible reassembled) full plaintext payload */ + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + +#ifdef NOT_YET + /* If IEEE 802.1X is used, check whether the port is authorized to send + * the received frame. */ + if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { + if (ethertype == ETH_P_PAE) { + printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n", + dev->name); + if (ieee->hostapd && ieee->apdev) { + /* Send IEEE 802.1X frames to the user + * space daemon for processing */ + prism2_rx_80211(ieee->apdev, skb, rx_stats, + PRISM2_RX_MGMT); + ieee->apdevstats.rx_packets++; + ieee->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", + dev->name, ethertype); + goto rx_dropped; + } + } +#endif + +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_process_dataframe) + { + if(ieee->ext_patch_ieee80211_rx_process_dataframe(ieee, skb, rx_stats)) + { + stats->rx_packets++; + stats->rx_bytes += skb->len; + goto rx_exit; + } + else + goto rx_dropped; + } +#endif + ieee->NumRxDataInPeriod++; +// ieee->NumRxOkTotal++; + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + SNAP_SIZE); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + u16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + +#ifdef NOT_YET + if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS) && + skb->len >= ETH_HLEN + ETH_ALEN) { + /* Non-standard frame: get addr4 from its bogus location after + * the payload */ + memcpy(skb->data + ETH_ALEN, + skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_trim(skb, skb->len - ETH_ALEN); + } +#endif + + stats->rx_packets++; + stats->rx_bytes += skb->len; + +#ifdef NOT_YET + if (ieee->iw_mode == IW_MODE_MASTER && !wds && + ieee->ap->bridge_packets) { + if (dst[0] & 0x01) { + /* copy multicast frame both to the higher layers and + * to the wireless media */ + ieee->ap->bridged_multicast++; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + printk(KERN_DEBUG "%s: skb_clone failed for " + "multicast frame\n", dev->name); + } else if (hostap_is_sta_assoc(ieee->ap, dst)) { + /* send frame directly to the associated STA using + * wireless media and not passing to higher layers */ + ieee->ap->bridged_unicast++; + skb2 = skb; + skb = NULL; + } + } + + if (skb2 != NULL) { + /* send to wireless media */ + skb2->protocol = __constant_htons(ETH_P_802_3); + skb2->mac.raw = skb2->nh.raw = skb2->data; + /* skb2->nh.raw = skb2->data + ETH_HLEN; */ + skb2->dev = dev; + dev_queue_xmit(skb2); + } + +#endif + if (skb) { + //printk("0skb_len(%d)\n", skb->len); + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ + //skb->ip_summed = CHECKSUM_UNNECESSARY; /* 802.11 crc not sufficient */ + ieee->last_rx_ps_time = jiffies; + //printk("1skb_len(%d)\n", skb->len); + netif_rx(skb); + } + +//by lizhaoming for LED_RX 2008.6.23 +#ifdef LED_SHIN +// printk("==================>data rcvd\n"); + ieee->ieee80211_led_contorl(dev,LED_CTL_RX); +#endif + + rx_exit: +#ifdef NOT_YET + if (sta) + hostap_handle_sta_release(sta); +#endif + return 1; + + rx_dropped: + stats->rx_dropped++; +#if 0 + int i; + printk("======>dropped: %s():addr2:"MAC_FMT",addr1:"MAC_FMT",skb->len:%d, hdrlen:%d\n", __FUNCTION__, MAC_ARG(((struct ieee80211_hdr*)skb->data)->addr2), MAC_ARG(((struct ieee80211_hdr*)skb->data)->addr1), skb->len, hdrlen); + for (i = 0; i < skb->len; i++) { + if (i % 16 == 0) printk("\n\t"); + printk("%2x ", *(skb->data+i)); + } + + printk("\n"); +#endif + /* Returning 0 indicates to caller that we have not handled the SKB-- + * so it is still allocated and can be used again by underlying + * hardware as a DMA target */ + return 0; +} + +#ifdef _RTL8187_EXT_PATCH_ +int ieee_ext_skb_p80211_to_ether(struct sk_buff *skb, int hdrlen, u8 *dst, u8 *src) +{ + u8 *payload; + u16 ethertype; + + /* skb: hdr + (possible reassembled) full plaintext payload */ + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + SNAP_SIZE); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + u16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + + return 1; +} +#endif // _RTL8187_EXT_PATCH_ + + +#define MGMT_FRAME_FIXED_PART_LENGTH 0x24 + +static inline int ieee80211_is_ofdm_rate(u8 rate) +{ + switch (rate & ~IEEE80211_BASIC_RATE_MASK) { + case IEEE80211_OFDM_RATE_6MB: + case IEEE80211_OFDM_RATE_9MB: + case IEEE80211_OFDM_RATE_12MB: + case IEEE80211_OFDM_RATE_18MB: + case IEEE80211_OFDM_RATE_24MB: + case IEEE80211_OFDM_RATE_36MB: + case IEEE80211_OFDM_RATE_48MB: + case IEEE80211_OFDM_RATE_54MB: + return 1; + } + return 0; +} + + +// +// Description: +// Translate 0-100 signal strength index into dBm. +// +int +TranslateToDbm8187( + unsigned char SignalStrengthIndex // 0-100 index. + ) +{ + unsigned char SignalPower; // in dBm. + + // Translate to dBm (x=0.5y-95). + //SignalPower = (int)((SignalStrengthIndex + 1) >> 1); + SignalPower = (int)SignalStrengthIndex * 7 / 10; + SignalPower -= 95; +// printk("==>SignalPower:%d\n", SignalPower); + return SignalPower; +} + +static inline int ieee80211_SignalStrengthTranslate( + int CurrSS + ) +{ + int RetSS; + + // Step 1. Scale mapping. + if(CurrSS >= 71 && CurrSS <= 100) + { + RetSS = 95 + (((CurrSS - 70) / 6 == 5) ? 5 : ((CurrSS - 70) / 6 + 1)); + } + else if(CurrSS >= 41 && CurrSS <= 70) + { + RetSS = 83 + ((CurrSS - 40) / 3); + } + else if(CurrSS >= 31 && CurrSS <= 40) + { + RetSS = 71 + (CurrSS - 30); + } + else if(CurrSS >= 21 && CurrSS <= 30) + { + RetSS = 59 + (CurrSS - 20); + } + else if(CurrSS >= 5 && CurrSS <= 20) + { + RetSS = 47 + (((CurrSS - 5) * 2) / 3); + } + else if(CurrSS == 4) + { + RetSS = 37; + } + else if(CurrSS == 3) + { + RetSS = 27; + } + else if(CurrSS == 2) + { + RetSS = 18; + } + else if(CurrSS == 1) + { + RetSS = 9; + } + else + { + RetSS = CurrSS; + } + //RT_TRACE(COMP_DBG, DBG_LOUD, ("##### After Mapping: LastSS: %d, CurrSS: %d, RetSS: %d\n", LastSS, CurrSS, RetSS)); + + // Step 2. Smoothing. + + //RT_TRACE(COMP_DBG, DBG_LOUD, ("$$$$$ After Smoothing: LastSS: %d, CurrSS: %d, RetSS: %d\n", LastSS, CurrSS, RetSS)); + + return RetSS; +} + +#ifdef ENABLE_DOT11D +static inline void ieee80211_extract_country_ie( + struct ieee80211_device *ieee, + struct ieee80211_info_element *info_element, + struct ieee80211_network *network, + u8 * addr2 +) +{ +#if 0 + u32 i = 0; + u8 * p = (u8*)info_element->data; + printk("-----------------------\n"); + printk("%s Country IE:", network->ssid); + for(i=0; ilen; i++) + printk("\t%2.2x", *(p+i)); + printk("\n-----------------------\n"); +#endif + if(IS_DOT11D_ENABLE(ieee)) + { + if(info_element->len!= 0) + { + memcpy(network->CountryIeBuf, info_element->data, info_element->len); + network->CountryIeLen = info_element->len; + + if(!IS_COUNTRY_IE_VALID(ieee)) + { + Dot11d_UpdateCountryIe(ieee, addr2, info_element->len, info_element->data); + } + } + + // + // 070305, rcnjko: I update country IE watch dog here because + // some AP (e.g. Cisco 1242) don't include country IE in their + // probe response frame. + // + if(IS_EQUAL_CIE_SRC(ieee, addr2) ) + { + UPDATE_CIE_WATCHDOG(ieee); + } + } + +} +#endif + + + inline int ieee80211_network_init( + struct ieee80211_device *ieee, + struct ieee80211_probe_response *beacon, + struct ieee80211_network *network, + struct ieee80211_rx_stats *stats) +{ +#ifdef CONFIG_IEEE80211_DEBUG + char rates_str[64]; + char *p; +#endif + struct ieee80211_info_element *info_element; + u16 left; + u8 i; + short offset; + + /* Pull out fixed field data */ + memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); + network->capability = beacon->capability; + network->last_scanned = jiffies; + network->time_stamp[0] = beacon->time_stamp[0]; + network->time_stamp[1] = beacon->time_stamp[1]; + network->beacon_interval = beacon->beacon_interval; + /* Where to pull this? beacon->listen_interval;*/ + network->listen_interval = 0x0A; + network->rates_len = network->rates_ex_len = 0; + network->last_associate = 0; + network->ssid_len = 0; + network->flags = 0; + network->atim_window = 0; + network->QoS_Enable = 0; +#ifdef THOMAS_TURBO + network->Turbo_Enable = 0; +#endif +#ifdef ENABLE_DOT11D + network->CountryIeLen = 0; + memset(network->CountryIeBuf, 0, MAX_IE_LEN); +#endif + + if (stats->freq == IEEE80211_52GHZ_BAND) { + /* for A band (No DS info) */ + network->channel = stats->received_channel; + } else + network->flags |= NETWORK_HAS_CCK; + + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; + + info_element = &beacon->info_element; + left = stats->len - ((void *)info_element - (void *)beacon); + while (left >= sizeof(struct ieee80211_info_element_hdr)) { + if (sizeof(struct ieee80211_info_element_hdr) + info_element->len > left) { + IEEE80211_DEBUG_SCAN("SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%d left=%d.\n", + info_element->len + sizeof(struct ieee80211_info_element), + left); + return 1; + } + + switch (info_element->id) { + case MFIE_TYPE_SSID: + if (ieee80211_is_empty_essid(info_element->data, + info_element->len)) { + network->flags |= NETWORK_EMPTY_ESSID; + break; + } + + network->ssid_len = min(info_element->len, + (u8)IW_ESSID_MAX_SIZE); + memcpy(network->ssid, info_element->data, network->ssid_len); + if (network->ssid_len < IW_ESSID_MAX_SIZE) + memset(network->ssid + network->ssid_len, 0, + IW_ESSID_MAX_SIZE - network->ssid_len); + + IEEE80211_DEBUG_SCAN("MFIE_TYPE_SSID: '%s' len=%d.\n", + network->ssid, network->ssid_len); + break; + + case MFIE_TYPE_RATES: +#ifdef CONFIG_IEEE80211_DEBUG + p = rates_str; +#endif + network->rates_len = min(info_element->len, MAX_RATES_LENGTH); + for (i = 0; i < network->rates_len; i++) { + network->rates[i] = info_element->data[i]; +#ifdef CONFIG_IEEE80211_DEBUG + p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); +#endif + if (ieee80211_is_ofdm_rate(info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + IEEE80211_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + IEEE80211_DEBUG_SCAN("MFIE_TYPE_RATES: '%s' (%d)\n", + rates_str, network->rates_len); + break; + + case MFIE_TYPE_RATES_EX: +#ifdef CONFIG_IEEE80211_DEBUG + p = rates_str; +#endif + network->rates_ex_len = min(info_element->len, MAX_RATES_EX_LENGTH); + for (i = 0; i < network->rates_ex_len; i++) { + network->rates_ex[i] = info_element->data[i]; +#ifdef CONFIG_IEEE80211_DEBUG + p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); +#endif + if (ieee80211_is_ofdm_rate(info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + IEEE80211_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + IEEE80211_DEBUG_SCAN("MFIE_TYPE_RATES_EX: '%s' (%d)\n", + rates_str, network->rates_ex_len); + break; + + case MFIE_TYPE_DS_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_DS_SET: %d\n", + info_element->data[0]); + if (stats->freq == IEEE80211_24GHZ_BAND) + network->channel = info_element->data[0]; + break; + + case MFIE_TYPE_FH_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_FH_SET: ignored\n"); + break; + + case MFIE_TYPE_CF_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_CF_SET: ignored\n"); + break; + + case MFIE_TYPE_TIM: + + if(info_element->len < 4) + break; + + network->dtim_period = info_element->data[1]; + + if(ieee->state != IEEE80211_LINKED) + break; + + network->last_dtim_sta_time[0] = stats->mac_time[0]; + network->last_dtim_sta_time[1] = stats->mac_time[1]; + + network->dtim_data = IEEE80211_DTIM_VALID; + + if(info_element->data[0] != 0) + break; + + if(info_element->data[2] & 1) + network->dtim_data |= IEEE80211_DTIM_MBCAST; + + offset = (info_element->data[2] >> 1)*2; + + //printk("offset1:%x aid:%x\n",offset, ieee->assoc_id); + + if(ieee->assoc_id < offset || + ieee->assoc_id > 8*(offset + info_element->len -3)) + + break; + + + offset = offset + ieee->assoc_id / 8;// + ((aid % 8)? 0 : 1) ; + + // printk("offset:%x data:%x, ucast:%d\n", offset, + // info_element->data[3+offset] , + // info_element->data[3+offset] & (1<<(ieee->assoc_id%8))); + + if(info_element->data[3+offset] & (1<<(ieee->assoc_id%8))) + network->dtim_data |= IEEE80211_DTIM_UCAST; + + break; + + case MFIE_TYPE_IBSS_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_IBSS_SET: ignored\n"); + break; + + case MFIE_TYPE_CHALLENGE: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_CHALLENGE: ignored\n"); + break; + + case MFIE_TYPE_GENERIC: + //nic is 87B + IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n", + info_element->len); + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x01) { + network->wpa_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->wpa_ie, info_element, + network->wpa_ie_len); + } + +#ifdef THOMAS_TURBO + if (info_element->len == 7 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0xe0 && + info_element->data[2] == 0x4c && + info_element->data[3] == 0x01 && + info_element->data[4] == 0x02) { + network->Turbo_Enable = 1; + } +#endif + if (1 == stats->nic_type) {//nic 87 + break; + } + + if (info_element->len >= 5 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x02 && + info_element->data[4] == 0x00) { + //printk(KERN_WARNING "wmm info updated: %x\n", info_element->data[6]); + //WMM Information Element + network->wmm_info = info_element->data[6]; + network->QoS_Enable = 1; + } + + if (info_element->len >= 8 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x02 && + info_element->data[4] == 0x01) { + // Not care about version at present. + //WMM Information Element + //printk(KERN_WARNING "wmm info¶m updated: %x\n", info_element->data[6]); + network->wmm_info = info_element->data[6]; + //WMM Parameter Element + memcpy(network->wmm_param, (u8 *)(info_element->data + 8),(info_element->len - 8)); + network->QoS_Enable = 1; + } + break; + + case MFIE_TYPE_RSN: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_RSN: %d bytes\n", + info_element->len); + network->rsn_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->rsn_ie, info_element, + network->rsn_ie_len); + break; + +#ifdef ENABLE_DOT11D + case MFIE_TYPE_COUNTRY: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_COUNTRY: %d bytes\n", + info_element->len); +// printk("=====>Receive <%s> Country IE\n",network->ssid); + ieee80211_extract_country_ie(ieee, info_element, network, beacon->header.addr2); + break; +#endif + + default: + IEEE80211_DEBUG_SCAN("unsupported IE %d\n", + info_element->id); + break; + } + + left -= sizeof(struct ieee80211_info_element_hdr) + + info_element->len; + info_element = (struct ieee80211_info_element *) + &info_element->data[info_element->len]; + } + + network->mode = 0; + if (stats->freq == IEEE80211_52GHZ_BAND) + network->mode = IEEE_A; + else { + if (network->flags & NETWORK_HAS_OFDM) + network->mode |= IEEE_G; + if (network->flags & NETWORK_HAS_CCK) + network->mode |= IEEE_B; + } + + if (network->mode == 0) { + IEEE80211_DEBUG_SCAN("Filtered out '%s (" MAC_FMT ")' " + "network.\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid)); + return 1; + } + + if (ieee80211_is_empty_essid(network->ssid, network->ssid_len)) + network->flags |= NETWORK_EMPTY_ESSID; + +#if 1 + //if(strcmp(network->ssid, "linksys_lzm000") == 0) + // printk("----signalstrength = %d ", stats->signalstrength); + stats->signal = TranslateToDbm8187(stats->signalstrength); + //stats->noise = stats->signal - stats->noise; + stats->noise = TranslateToDbm8187(100 - stats->signalstrength) - 25; +#endif + memcpy(&network->stats, stats, sizeof(network->stats)); + + //YJ,test,080611 + //if(strcmp(network->ssid, "ZyXEL") == 0) + // IEEE_NET_DUMP(network); + + return 0; +} + +static inline int is_same_network(struct ieee80211_network *src, + struct ieee80211_network *dst, + struct ieee80211_device * ieee) +{ + /* A network is only a duplicate if the channel, BSSID, ESSID + * and the capability field (in particular IBSS and BSS) all match. + * We treat all with the same BSSID and channel + * as one network */ + return (((src->ssid_len == dst->ssid_len) || (ieee->iw_mode == IW_MODE_INFRA)) && //YJ,mod,080819,for hidden ap + //((src->ssid_len == dst->ssid_len) && + (src->channel == dst->channel) && + !memcmp(src->bssid, dst->bssid, ETH_ALEN) && + (!memcmp(src->ssid, dst->ssid, src->ssid_len) || (ieee->iw_mode == IW_MODE_INFRA)) && //YJ,mod,080819,for hidden ap + //!memcmp(src->ssid, dst->ssid, src->ssid_len) && + ((src->capability & WLAN_CAPABILITY_IBSS) == + (dst->capability & WLAN_CAPABILITY_IBSS)) && + ((src->capability & WLAN_CAPABILITY_BSS) == + (dst->capability & WLAN_CAPABILITY_BSS))); +} + +inline void update_network(struct ieee80211_network *dst, + struct ieee80211_network *src) +{ + unsigned char quality = src->stats.signalstrength; + unsigned char signal = 0; + unsigned char noise = 0; + if(dst->stats.signalstrength > 0) { + quality = (dst->stats.signalstrength * 5 + src->stats.signalstrength + 5)/6; + } + signal = TranslateToDbm8187(quality); + //noise = signal - src->stats.noise; + if(dst->stats.noise > 0) + noise = (dst->stats.noise * 5 + src->stats.noise)/6; + //if(strcmp(dst->ssid, "linksys_lzm000") == 0) +// printk("ssid:%s, quality:%d, signal:%d\n", dst->ssid, quality, signal); + memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats)); + dst->stats.signalstrength = quality; + dst->stats.signal = signal; + dst->stats.noise = noise; + dst->capability = src->capability; + memcpy(dst->rates, src->rates, src->rates_len); + dst->rates_len = src->rates_len; + memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); + dst->rates_ex_len = src->rates_ex_len; + + //YJ,add,080819,for hidden ap + if(src->ssid_len > 0) + { + //if(src->ssid_len == 13) + // printk("=====================>>>>>>>> Dst ssid: %s Src ssid: %s\n", dst->ssid, src->ssid); + memset(dst->ssid, 0, dst->ssid_len); + dst->ssid_len = src->ssid_len; + memcpy(dst->ssid, src->ssid, src->ssid_len); + } + //YJ,add,080819,for hidden ap,end + dst->channel = src->channel; + dst->mode = src->mode; + dst->flags = src->flags; + dst->time_stamp[0] = src->time_stamp[0]; + dst->time_stamp[1] = src->time_stamp[1]; + + dst->beacon_interval = src->beacon_interval; + dst->listen_interval = src->listen_interval; + dst->atim_window = src->atim_window; + dst->dtim_period = src->dtim_period; + dst->dtim_data = src->dtim_data; + dst->last_dtim_sta_time[0] = src->last_dtim_sta_time[0]; + dst->last_dtim_sta_time[1] = src->last_dtim_sta_time[1]; + + memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); + dst->wpa_ie_len = src->wpa_ie_len; + memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); + dst->rsn_ie_len = src->rsn_ie_len; + + dst->last_scanned = jiffies; + /* dst->last_associate is not overwritten */ +#if 1 + dst->wmm_info = src->wmm_info; //sure to exist in beacon or probe response frame. +/* + if((dst->wmm_info^src->wmm_info)&0x0f) {//Param Set Count change, update Parameter + memcpy(dst->wmm_param, src->wmm_param, IEEE80211_AC_PRAM_LEN); + } +*/ + if(src->wmm_param[0].ac_aci_acm_aifsn|| \ + src->wmm_param[1].ac_aci_acm_aifsn|| \ + src->wmm_param[2].ac_aci_acm_aifsn|| \ + src->wmm_param[1].ac_aci_acm_aifsn) { + memcpy(dst->wmm_param, src->wmm_param, WME_AC_PRAM_LEN); + } + dst->QoS_Enable = src->QoS_Enable; +#else + dst->QoS_Enable = 1;//for Rtl8187 simulation +#endif + dst->SignalStrength = src->SignalStrength; +#ifdef THOMAS_TURBO + dst->Turbo_Enable = src->Turbo_Enable; +#endif +#ifdef ENABLE_DOT11D + dst->CountryIeLen = src->CountryIeLen; + memcpy(dst->CountryIeBuf, src->CountryIeBuf, src->CountryIeLen); +#endif + +} + +inline void ieee80211_process_probe_response( + struct ieee80211_device *ieee, + struct ieee80211_probe_response *beacon, + struct ieee80211_rx_stats *stats) +{ + struct ieee80211_network network; + struct ieee80211_network *target; + struct ieee80211_network *oldest = NULL; +#ifdef CONFIG_IEEE80211_DEBUG + struct ieee80211_info_element *info_element = &beacon->info_element; +#endif + unsigned long flags; + short renew; + u8 wmm_info; + u8 is_beacon = (WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == IEEE80211_STYPE_BEACON)? 1:0; + + memset(&network, 0, sizeof(struct ieee80211_network)); +//rz +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_process_probe_response_1) { + ieee->ext_patch_ieee80211_process_probe_response_1(ieee, beacon, stats); + return; + } +#endif + IEEE80211_DEBUG_SCAN( + "'%s' (" MAC_FMT "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", + escape_essid(info_element->data, info_element->len), + MAC_ARG(beacon->header.addr3), + (beacon->capability & (1<<0xf)) ? '1' : '0', + (beacon->capability & (1<<0xe)) ? '1' : '0', + (beacon->capability & (1<<0xd)) ? '1' : '0', + (beacon->capability & (1<<0xc)) ? '1' : '0', + (beacon->capability & (1<<0xb)) ? '1' : '0', + (beacon->capability & (1<<0xa)) ? '1' : '0', + (beacon->capability & (1<<0x9)) ? '1' : '0', + (beacon->capability & (1<<0x8)) ? '1' : '0', + (beacon->capability & (1<<0x7)) ? '1' : '0', + (beacon->capability & (1<<0x6)) ? '1' : '0', + (beacon->capability & (1<<0x5)) ? '1' : '0', + (beacon->capability & (1<<0x4)) ? '1' : '0', + (beacon->capability & (1<<0x3)) ? '1' : '0', + (beacon->capability & (1<<0x2)) ? '1' : '0', + (beacon->capability & (1<<0x1)) ? '1' : '0', + (beacon->capability & (1<<0x0)) ? '1' : '0'); + + if (ieee80211_network_init(ieee, beacon, &network, stats)) { + IEEE80211_DEBUG_SCAN("Dropped '%s' (" MAC_FMT ") via %s.\n", + escape_essid(info_element->data, + info_element->len), + MAC_ARG(beacon->header.addr3), + WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP ? + "PROBE RESPONSE" : "BEACON"); + return; + } + +#ifdef ENABLE_DOT11D + // For Asus EeePc request, + // (1) if wireless adapter receive get any 802.11d country code in AP beacon, + // wireless adapter should follow the country code. + // (2) If there is no any country code in beacon, + // then wireless adapter should do active scan from ch1~11 and + // passive scan from ch12~14 + if(ieee->bGlobalDomain) + { + if (WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == IEEE80211_STYPE_PROBE_RESP) + { + // Case 1: Country code + if(IS_COUNTRY_IE_VALID(ieee) ) + { + if( !IsLegalChannel(ieee, network.channel) ) + { + printk("GetScanInfo(): For Country code, filter probe response at channel(%d).\n", network.channel); + return; + } + } + // Case 2: No any country code. + else + { + // Filter over channel ch12~14 + if(network.channel > 11) + { + printk("GetScanInfo(): For Global Domain, filter probe response at channel(%d).\n", network.channel); + return; + } + } + } + else + { + // Case 1: Country code + if(IS_COUNTRY_IE_VALID(ieee) ) + { + if( !IsLegalChannel(ieee, network.channel) ) + { + printk("GetScanInfo(): For Country code, filter beacon at channel(%d).\n",network.channel); + return; + } + } + // Case 2: No any country code. + else + { + // Filter over channel ch12~14 + if(network.channel > 14) + { + printk("GetScanInfo(): For Global Domain, filter beacon at channel(%d).\n",network.channel); + return; + } + } + } + } + + //lzm add 081205 + // for Toshiba request, we use channel_plan COUNTRY_CODE_WORLD_WIDE_13_INDEX, + // For Liteon "World Wide 13" Domain name:ch1~11 active scan & ch12~13 passive scan + // So we shoud only rcv beacon in 12-13, and filter probe resp in 12-13. + if(ieee->MinPassiveChnlNum != MAX_CHANNEL_NUMBER+1) + { + if (WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == IEEE80211_STYPE_PROBE_RESP) + { + // Filter over channel ch12~13 + if(network.channel >= ieee->MinPassiveChnlNum) + { + printk("GetScanInfo(): passive scan, filter probe resp at channel(%d).\n", network.channel); + return; + } + } + } +#endif + + + /* The network parsed correctly -- so now we scan our known networks + * to see if we can find it in our list. + * + * NOTE: This search is definitely not optimized. Once its doing + * the "right thing" we'll optimize it for efficiency if + * necessary */ + + /* Search for this entry in the list and update it if it is + * already there. */ + + spin_lock_irqsave(&ieee->lock, flags); + + if(is_same_network(&ieee->current_network, &network, ieee)) { + //YJ,add,080819,for hidden ap + if(is_beacon == 0) + network.flags = (~NETWORK_EMPTY_ESSID & network.flags)|(NETWORK_EMPTY_ESSID & ieee->current_network.flags); + if ((ieee->state == IEEE80211_LINKED) && is_beacon) + ieee->NumRxBcnInPeriod++; + wmm_info = ieee->current_network.wmm_info; + update_network(&ieee->current_network, &network); + } + + list_for_each_entry(target, &ieee->network_list, list) { + if (is_same_network(target, &network, ieee)) + break; + if ((oldest == NULL) || + (target->last_scanned < oldest->last_scanned)) + oldest = target; + } + + /* If we didn't find a match, then get a new network slot to initialize + * with this beacon's information */ + if (&target->list == &ieee->network_list) { + if (list_empty(&ieee->network_free_list)) { + /* If there are no more slots, expire the oldest */ + list_del(&oldest->list); + target = oldest; + IEEE80211_DEBUG_SCAN("Expired '%s' (" MAC_FMT ") from " + "network list.\n", + escape_essid(target->ssid, + target->ssid_len), + MAC_ARG(target->bssid)); + } else { + /* Otherwise just pull from the free list */ + target = list_entry(ieee->network_free_list.next, + struct ieee80211_network, list); + list_del(ieee->network_free_list.next); + } + + +#ifdef CONFIG_IEEE80211_DEBUG + IEEE80211_DEBUG_SCAN("Adding '%s' (" MAC_FMT ") via %s.\n", + escape_essid(network.ssid, + network.ssid_len), + MAC_ARG(network.bssid), + WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP ? + "PROBE RESPONSE" : "BEACON"); +#endif + +#ifdef _RTL8187_EXT_PATCH_ + network.ext_entry = target->ext_entry; +#endif + memcpy(target, &network, sizeof(*target)); + list_add_tail(&target->list, &ieee->network_list); + if(ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) + ieee80211_softmac_new_net(ieee,&network); + } else { + IEEE80211_DEBUG_SCAN("Updating '%s' (" MAC_FMT ") via %s.\n", + escape_essid(target->ssid, + target->ssid_len), + MAC_ARG(target->bssid), + WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP ? + "PROBE RESPONSE" : "BEACON"); + + /* we have an entry and we are going to update it. But this entry may + * be already expired. In this case we do the same as we found a new + * net and call the new_net handler + */ + renew = !time_after(target->last_scanned + ieee->scan_age, jiffies); + //YJ,add,080819,for hidden ap + if(is_beacon == 0) + network.flags = (~NETWORK_EMPTY_ESSID & network.flags)|(NETWORK_EMPTY_ESSID & target->flags); + //if(strncmp(network.ssid, "linksys-c",9) == 0) + // printk("====>2 network.ssid=%s FLAG=%d target.ssid=%s FLAG=%d\n", network.ssid, network.flags, target->ssid, target->flags); + if(((network.flags & NETWORK_EMPTY_ESSID) == NETWORK_EMPTY_ESSID) \ + && (((network.ssid_len > 0) && (strncmp(target->ssid, network.ssid, network.ssid_len)))\ + ||((ieee->current_network.ssid_len == network.ssid_len)&&(strncmp(ieee->current_network.ssid, network.ssid, network.ssid_len) == 0)&&(ieee->state == IEEE80211_NOLINK)))) + renew = 1; + //YJ,add,080819,for hidden ap,end + update_network(target, &network); + if(renew && (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE)) + ieee80211_softmac_new_net(ieee,&network); + } + + spin_unlock_irqrestore(&ieee->lock, flags); +} + +void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr *header, + struct ieee80211_rx_stats *stats) +{ + switch (WLAN_FC_GET_STYPE(header->frame_ctl)) { + + case IEEE80211_STYPE_BEACON: + IEEE80211_DEBUG_MGMT("received BEACON (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_DEBUG_SCAN("Beacon\n"); + ieee80211_process_probe_response( + ieee, (struct ieee80211_probe_response *)header, stats); + break; + + case IEEE80211_STYPE_PROBE_RESP: + IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_DEBUG_SCAN("Probe response\n"); + ieee80211_process_probe_response( + ieee, (struct ieee80211_probe_response *)header, stats); + break; +//rz +#ifdef _RTL8187_EXT_PATCH_ + case IEEE80211_STYPE_PROBE_REQ: + IEEE80211_DEBUG_MGMT("received PROBE REQUEST (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_DEBUG_SCAN("Probe request\n"); + /// + //printk("Probe request\n"); + if( ieee->iw_mode == ieee->iw_ext_mode && ieee->ext_patch_ieee80211_rx_mgt_on_probe_req ) + ieee->ext_patch_ieee80211_rx_mgt_on_probe_req( ieee, (struct ieee80211_probe_request *)header, stats); + break; +#endif // _RTL8187_EXT_PATCH_ + + } +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_rx_mgt); +EXPORT_SYMBOL(ieee80211_rx); +EXPORT_SYMBOL(ieee80211_network_init); +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL(ieee_ext_skb_p80211_to_ether); +#endif +#else +EXPORT_SYMBOL_NOVERS(ieee80211_rx_mgt); +EXPORT_SYMBOL_NOVERS(ieee80211_rx); +EXPORT_SYMBOL_NOVERS(ieee80211_network_init); +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL_NOVERS(ieee_ext_skb_p80211_to_ether); +#endif +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_softmac.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_softmac.c new file mode 100644 index 0000000..c4b8629 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_softmac.c @@ -0,0 +1,4083 @@ +/* IEEE 802.11 SoftMAC layer + * Copyright (c) 2005 Andrea Merello + * + * Mostly extracted from the rtl8180-sa2400 driver for the + * in-kernel generic ieee802.11 stack. + * + * Few lines might be stolen from other part of the ieee80211 + * stack. Copyright who own it's copyright + * + * WPA code stolen from the ipw2200 driver. + * Copyright who own it's copyright. + * + * released under the GPL + */ + + +#include "ieee80211.h" + +#include +#include +#include +#include + +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + + +u8 rsn_authen_cipher_suite[16][4] = { + {0x00,0x0F,0xAC,0x00}, //Use group key, //Reserved + {0x00,0x0F,0xAC,0x01}, //WEP-40 //RSNA default + {0x00,0x0F,0xAC,0x02}, //TKIP //NONE //{used just as default} + {0x00,0x0F,0xAC,0x03}, //WRAP-historical + {0x00,0x0F,0xAC,0x04}, //CCMP + {0x00,0x0F,0xAC,0x05}, //WEP-104 +}; + +short ieee80211_is_54g(struct ieee80211_network net) +{ + return ((net.rates_ex_len > 0) || (net.rates_len > 4)); +} + +short ieee80211_is_shortslot(struct ieee80211_network net) +{ + return (net.capability & WLAN_CAPABILITY_SHORT_SLOT); +} + +/* returns the total length needed for pleacing the RATE MFIE + * tag and the EXTENDED RATE MFIE tag if needed. + * It encludes two bytes per tag for the tag itself and its len + */ +unsigned int ieee80211_MFIE_rate_len(struct ieee80211_device *ieee) +{ + unsigned int rate_len = 0; + + if (ieee->modulation & IEEE80211_CCK_MODULATION) + rate_len = IEEE80211_CCK_RATE_LEN + 2; + + if (ieee->modulation & IEEE80211_OFDM_MODULATION) + + rate_len += IEEE80211_OFDM_RATE_LEN + 2; + + return rate_len; +} + +/* pleace the MFIE rate, tag to the memory (double) poined. + * Then it updates the pointer so that + * it points after the new MFIE tag added. + */ +void ieee80211_MFIE_Brate(struct ieee80211_device *ieee, u8 **tag_p) +{ + u8 *tag = *tag_p; + + if (ieee->modulation & IEEE80211_CCK_MODULATION){ + *tag++ = MFIE_TYPE_RATES; + *tag++ = 7; + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + //added for basic rate set + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB; + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB; + *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; + } + + /* We may add an option for custom rates that specific HW might support */ + *tag_p = tag; +} + +void ieee80211_MFIE_Grate(struct ieee80211_device *ieee, u8 **tag_p) +{ + u8 *tag = *tag_p; + + if (ieee->modulation & IEEE80211_OFDM_MODULATION){ + + *tag++ = MFIE_TYPE_RATES_EX; + *tag++ = 5; + *tag++ = IEEE80211_OFDM_RATE_9MB; + *tag++ = IEEE80211_OFDM_RATE_18MB; + *tag++ = IEEE80211_OFDM_RATE_36MB; + *tag++ = IEEE80211_OFDM_RATE_48MB; + *tag++ = IEEE80211_OFDM_RATE_54MB; + + } + + /* We may add an option for custom rates that specific HW might support */ + *tag_p = tag; +} + + +void ieee80211_WMM_Info(struct ieee80211_device *ieee, u8 **tag_p) { + u8 *tag = *tag_p; + + *tag++ = MFIE_TYPE_GENERIC; //0 + *tag++ = 7; + *tag++ = 0x00; + *tag++ = 0x50; + *tag++ = 0xf2; + *tag++ = 0x02;//5 + *tag++ = 0x00; + *tag++ = 0x01; +#ifdef SUPPORT_USPD + if(ieee->current_network.wmm_info & 0x80) { + *tag++ = 0x0f|MAX_SP_Len; + } else { + *tag++ = MAX_SP_Len; + } +#else + *tag++ = MAX_SP_Len; +#endif + *tag_p = tag; +} + +#ifdef THOMAS_TURBO +void ieee80211_TURBO_Info(struct ieee80211_device *ieee, u8 **tag_p) { + u8 *tag = *tag_p; + + *tag++ = MFIE_TYPE_GENERIC; //0 + *tag++ = 7; + *tag++ = 0x00; + *tag++ = 0xe0; + *tag++ = 0x4c; + *tag++ = 0x01;//5 + *tag++ = 0x02; + *tag++ = 0x11; + *tag++ = 0x00; + + *tag_p = tag; + printk(KERN_ALERT "This is enable turbo mode IE process\n"); +} +#endif + +void enqueue_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb) +{ + int nh; + nh = (ieee->mgmt_queue_head +1) % MGMT_QUEUE_NUM; + +/* + * if the queue is full but we have newer frames then + * just overwrites the oldest. + * + * if (nh == ieee->mgmt_queue_tail) + * return -1; + */ + ieee->mgmt_queue_head = nh; + ieee->mgmt_queue_ring[nh] = skb; + + //return 0; +} + +struct sk_buff *dequeue_mgmt(struct ieee80211_device *ieee) +{ + struct sk_buff *ret; + + if(ieee->mgmt_queue_tail == ieee->mgmt_queue_head) + return NULL; + + ret = ieee->mgmt_queue_ring[ieee->mgmt_queue_tail]; + + ieee->mgmt_queue_tail = + (ieee->mgmt_queue_tail+1) % MGMT_QUEUE_NUM; + + return ret; +} + +void init_mgmt_queue(struct ieee80211_device *ieee) +{ + ieee->mgmt_queue_tail = ieee->mgmt_queue_head = 0; +} + + +void ieee80211_sta_wakeup(struct ieee80211_device *ieee, short nl); + +inline void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee) +{ + unsigned long flags; + short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE; + struct ieee80211_hdr_3addr *header= + (struct ieee80211_hdr_3addr *) skb->data; + + + spin_lock_irqsave(&ieee->lock, flags); + + /* called with 2nd param 0, no mgmt lock required */ + ieee80211_sta_wakeup(ieee,0); + + if(single){ + if(ieee->queue_stop){ + + enqueue_mgmt(ieee,skb); + }else{ + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + /* avoid watchdog triggers */ + ieee->dev->trans_start = jiffies; + ieee->softmac_data_hard_start_xmit(skb,ieee->dev,ieee->basic_rate); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) +// dev_kfree_skb_any(skb);//edit by thomas //'cause this function will cause Oops called in interrupt context in old version 101907 +#endif + } + + spin_unlock_irqrestore(&ieee->lock, flags); + }else{ + spin_unlock_irqrestore(&ieee->lock, flags); + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags); + + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + ieee->softmac_hard_start_xmit(skb,ieee->dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) +// dev_kfree_skb_any(skb);//edit by thomas +#endif + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags); + } +} + + +inline void softmac_ps_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee) +{ + + short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE; + struct ieee80211_hdr_3addr *header = + (struct ieee80211_hdr_3addr *) skb->data; + + + if(single){ + + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + /* avoid watchdog triggers */ + ieee->dev->trans_start = jiffies; + ieee->softmac_data_hard_start_xmit(skb,ieee->dev,ieee->basic_rate); + + }else{ + + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + ieee->softmac_hard_start_xmit(skb,ieee->dev); + + } + dev_kfree_skb_any(skb);//edit by thomas +} + +inline struct sk_buff *ieee80211_probe_req(struct ieee80211_device *ieee) +{ + unsigned int len,rate_len; + u8 *tag; + struct sk_buff *skb; + struct ieee80211_probe_request *req; + +#ifdef _RTL8187_EXT_PATCH_ + short extMore = 0; + if(ieee->ext_patch_ieee80211_probe_req_1) + extMore = ieee->ext_patch_ieee80211_probe_req_1(ieee); +#endif + + len = ieee->current_network.ssid_len; + + rate_len = ieee80211_MFIE_rate_len(ieee); + +#ifdef _RTL8187_EXT_PATCH_ + if(!extMore) +#endif + skb = dev_alloc_skb(sizeof(struct ieee80211_probe_request) + + 2 + len + rate_len); +#ifdef _RTL8187_EXT_PATCH_ + else + skb = dev_alloc_skb(sizeof(struct ieee80211_probe_request) + + 2 + len + rate_len+128); // MESHID + CAP +#endif + + if (!skb) + return NULL; + + req = (struct ieee80211_probe_request *) skb_put(skb,sizeof(struct ieee80211_probe_request)); + req->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + req->header.duration_id = 0; //FIXME: is this OK ? + + memset(req->header.addr1, 0xff, ETH_ALEN); + memcpy(req->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memset(req->header.addr3, 0xff, ETH_ALEN); + + tag = (u8 *) skb_put(skb,len+2+rate_len); + + *tag++ = MFIE_TYPE_SSID; + *tag++ = len; + memcpy(tag, ieee->current_network.ssid, len); + tag += len; + + ieee80211_MFIE_Brate(ieee,&tag); + ieee80211_MFIE_Grate(ieee,&tag); + +#ifdef _RTL8187_EXT_PATCH_ + if(extMore) + ieee->ext_patch_ieee80211_probe_req_2(ieee, skb, tag); +#endif + return skb; +} + +struct sk_buff *ieee80211_get_beacon_(struct ieee80211_device *ieee); + +#ifdef _RTL8187_EXT_PATCH_ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void ext_ieee80211_send_beacon_wq(struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, ext_send_beacon_wq); +#else +void ext_ieee80211_send_beacon_wq(struct ieee80211_device *ieee) +{ +#endif + + struct sk_buff *skb; + + //unsigned long flags; +// printk("=========>%s()\n", __FUNCTION__); + skb = ieee80211_get_beacon_(ieee); + + if (skb){ + softmac_mgmt_xmit(skb, ieee); + ieee->softmac_stats.tx_beacons++; + dev_kfree_skb_any(skb);//edit by thomas + } + + + //printk(KERN_WARNING "[1] beacon sending!\n"); +// ieee->beacon_timer.expires = jiffies + +// (MSECS( ieee->current_network.beacon_interval -5)); + + //spin_lock_irqsave(&ieee->beacon_lock,flags); +// if(ieee->beacon_txing) +// add_timer(&ieee->beacon_timer); + //spin_unlock_irqrestore(&ieee->beacon_lock,flags); +} +#endif + +void ieee80211_send_beacon(struct ieee80211_device *ieee) +{ + struct sk_buff *skb; + + //unsigned long flags; +// printk("=========>%s()\n", __FUNCTION__); + skb = ieee80211_get_beacon_(ieee); + + if (skb){ + softmac_mgmt_xmit(skb, ieee); + ieee->softmac_stats.tx_beacons++; + dev_kfree_skb_any(skb);//edit by thomas + } + + + //printk(KERN_WARNING "[1] beacon sending!\n"); + ieee->beacon_timer.expires = jiffies + + (MSECS( ieee->current_network.beacon_interval -5)); + + //spin_lock_irqsave(&ieee->beacon_lock,flags); + if(ieee->beacon_txing) + add_timer(&ieee->beacon_timer); + //spin_unlock_irqrestore(&ieee->beacon_lock,flags); +} + + +void ieee80211_send_beacon_cb(unsigned long _ieee) +{ + struct ieee80211_device *ieee = + (struct ieee80211_device *) _ieee; + unsigned long flags; + + spin_lock_irqsave(&ieee->beacon_lock, flags); + ieee80211_send_beacon(ieee); + spin_unlock_irqrestore(&ieee->beacon_lock, flags); +} + +#ifdef _RTL8187_EXT_PATCH_ + +inline struct sk_buff *ieee80211_probe_req_with_SSID(struct ieee80211_device *ieee, char *ssid, int len_ssid) +{ + unsigned int len,rate_len; + u8 *tag; + struct sk_buff *skb; + struct ieee80211_probe_request *req; + +#ifdef _RTL8187_EXT_PATCH_ + short extMore = 0; + if(ieee->ext_patch_ieee80211_probe_req_1) + extMore = ieee->ext_patch_ieee80211_probe_req_1(ieee); +#endif + + len = len_ssid; + + rate_len = ieee80211_MFIE_rate_len(ieee); + +#ifdef _RTL8187_EXT_PATCH_ + if(!extMore) +#endif + skb = dev_alloc_skb(sizeof(struct ieee80211_probe_request) + + 2 + len + rate_len); +#ifdef _RTL8187_EXT_PATCH_ + else + skb = dev_alloc_skb(sizeof(struct ieee80211_probe_request) + + 2 + len + rate_len+128); // MESHID + CAP +#endif + + if (!skb) + return NULL; + + req = (struct ieee80211_probe_request *) skb_put(skb,sizeof(struct ieee80211_probe_request)); + req->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + req->header.duration_id = 0; //FIXME: is this OK ? + + memset(req->header.addr1, 0xff, ETH_ALEN); + memcpy(req->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memset(req->header.addr3, 0xff, ETH_ALEN); + + tag = (u8 *) skb_put(skb,len+2+rate_len); + + *tag++ = MFIE_TYPE_SSID; + *tag++ = len; + if(len) + { + memcpy(tag, ssid, len); + tag += len; + } + + ieee80211_MFIE_Brate(ieee,&tag); + ieee80211_MFIE_Grate(ieee,&tag); + +#ifdef _RTL8187_EXT_PATCH_ + if(extMore) + ieee->ext_patch_ieee80211_probe_req_2(ieee, skb, tag); +#endif + return skb; +} + +#endif // _RTL8187_EXT_PATCH_ + + +void ieee80211_send_probe(struct ieee80211_device *ieee) +{ + struct sk_buff *skb; + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + skb = ieee80211_probe_req_with_SSID(ieee, NULL, 0); + else +#endif + skb = ieee80211_probe_req(ieee); + if (skb){ + softmac_mgmt_xmit(skb, ieee); + ieee->softmac_stats.tx_probe_rq++; + dev_kfree_skb_any(skb);//edit by thomas + } +} + +void ieee80211_send_probe_requests(struct ieee80211_device *ieee) +{ + if (ieee->active_scan && (ieee->softmac_features & IEEE_SOFTMAC_PROBERQ)){ + ieee80211_send_probe(ieee); + ieee80211_send_probe(ieee); + } +} + +/* this performs syncro scan blocking the caller until all channels + * in the allowed channel map has been checked. + */ +void ieee80211_softmac_scan_syncro(struct ieee80211_device *ieee) +{ + short ch = 0; +#ifdef ENABLE_DOT11D + u8 channel_map[MAX_CHANNEL_NUMBER+1]; + memcpy(channel_map, GET_DOT11D_INFO(ieee)->channel_map, MAX_CHANNEL_NUMBER+1); +#endif + + + down(&ieee->scan_sem); + + while(1) + { + + do{ + ch++; + if (ch > MAX_CHANNEL_NUMBER) + goto out; /* scan completed */ + +#ifdef ENABLE_DOT11D + }while(!channel_map[ch]); +#else + }while(!ieee->channel_map[ch]); +#endif + + //printk("=>current channel is %d\n",ch); + + /* this fuction can be called in two situations + * 1- We have switched to ad-hoc mode and we are + * performing a complete syncro scan before conclude + * there are no interesting cell and to create a + * new one. In this case the link state is + * IEEE80211_NOLINK until we found an interesting cell. + * If so the ieee8021_new_net, called by the RX path + * will set the state to IEEE80211_LINKED, so we stop + * scanning + * 2- We are linked and the root uses run iwlist scan. + * So we switch to IEEE80211_LINKED_SCANNING to remember + * that we are still logically linked (not interested in + * new network events, despite for updating the net list, + * but we are temporarly 'unlinked' as the driver shall + * not filter RX frames and the channel is changing. + * So the only situation in witch are interested is to check + * if the state become LINKED because of the #1 situation + */ + + if (ieee->state == IEEE80211_LINKED) + goto out; + + //printk("---->%s: chan %d\n", __func__, ch); + ieee->set_chan(ieee->dev, ch); +#ifdef ENABLE_DOT11D + if(channel_map[ch] == 1) +#endif + { + ieee80211_send_probe_requests(ieee); + } + + /* this prevent excessive time wait when we + * need to wait for a syncro scan to end.. + */ + if (ieee->sync_scan_hurryup) + goto out; + + + msleep_interruptible_rtl(IEEE80211_SOFTMAC_SCAN_TIME); + + } +out: + ieee->sync_scan_hurryup = 0; + up(&ieee->scan_sem); +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(ieee)) + DOT11D_ScanComplete(ieee); +#endif + +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +/* called both by wq with ieee->lock held */ +void ieee80211_softmac_scan(struct ieee80211_device *ieee) +{ +#if 0 + short watchdog = 0; + do{ + ieee->current_network.channel = + (ieee->current_network.channel + 1) % MAX_CHANNEL_NUMBER; + if (watchdog++ > MAX_CHANNEL_NUMBER) + return; /* no good chans */ + + }while(!ieee->channel_map[ieee->current_network.channel]); +#endif + + schedule_task(&ieee->softmac_scan_wq); +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void ieee80211_softmac_scan_wq(struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work, struct delayed_work, work); + struct ieee80211_device *ieee = container_of(dwork, struct ieee80211_device, softmac_scan_wq); +#else +void ieee80211_softmac_scan_wq(struct ieee80211_device *ieee) +{ +#endif + //static short watchdog = 0; + //short watchdog = 0;//lzm move into ieee->scan_watchdog 081215 for roaming + u8 channel_bak = ieee->current_network.channel;//lzm for channel+1 +#ifdef ENABLE_DOT11D + u8 channel_map[MAX_CHANNEL_NUMBER+1]; + memcpy(channel_map, GET_DOT11D_INFO(ieee)->channel_map, MAX_CHANNEL_NUMBER+1); +#endif + down(&ieee->scan_sem); + + do{ + ieee->current_network.channel = + (ieee->current_network.channel + 1) % MAX_CHANNEL_NUMBER; + if (ieee->scan_watchdog++ > MAX_CHANNEL_NUMBER) + goto out; /* no good chans */ +#ifdef ENABLE_DOT11D + }while(!channel_map[ieee->current_network.channel]); +#else + }while(!ieee->channel_map[ieee->current_network.channel]); +#endif + + if (ieee->scanning == 0 ) + goto out; + + //printk("current channel is %d\n",ieee->current_network.channel); + ieee->set_chan(ieee->dev, ieee->current_network.channel); +#ifdef ENABLE_DOT11D + if(channel_map[ieee->current_network.channel] == 1) +#endif + { + ieee80211_send_probe_requests(ieee); + } + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_delayed_work(ieee->wq, &ieee->softmac_scan_wq, IEEE80211_SOFTMAC_SCAN_TIME); +#else + ieee->scan_timer.expires = jiffies + (IEEE80211_SOFTMAC_SCAN_TIME); + if (ieee->scanning == 1) + add_timer(&ieee->scan_timer); +#endif + + up(&ieee->scan_sem); + return; +out: + //printk("%s():Stop scan now\n",__FUNCTION__); + ieee->actscanning = false; + ieee->scan_watchdog = 0; + ieee->scanning = 0; + ieee->current_network.channel = channel_bak; + up(&ieee->scan_sem); +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(ieee)) + DOT11D_ScanComplete(ieee); +#endif + + return; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +void ieee80211_softmac_scan_cb(unsigned long _dev) +{ + unsigned long flags; + struct ieee80211_device *ieee = (struct ieee80211_device *)_dev; + + spin_lock_irqsave(&ieee->lock, flags); + ieee80211_softmac_scan(ieee); + spin_unlock_irqrestore(&ieee->lock, flags); +} +#endif + + +void ieee80211_beacons_start(struct ieee80211_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->beacon_lock,flags); + + ieee->beacon_txing = 1; + ieee80211_send_beacon(ieee); + + spin_unlock_irqrestore(&ieee->beacon_lock,flags); +} + +void ieee80211_beacons_stop(struct ieee80211_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->beacon_lock,flags); + + ieee->beacon_txing = 0; + del_timer_sync(&ieee->beacon_timer); + + spin_unlock_irqrestore(&ieee->beacon_lock,flags); + +} + + +void ieee80211_stop_send_beacons(struct ieee80211_device *ieee) +{ + if(ieee->stop_send_beacons) + ieee->stop_send_beacons(ieee->dev); + if (ieee->softmac_features & IEEE_SOFTMAC_BEACONS) + ieee80211_beacons_stop(ieee); +} + + +void ieee80211_start_send_beacons(struct ieee80211_device *ieee) +{ + if(ieee->start_send_beacons) + ieee->start_send_beacons(ieee->dev); + if(ieee->softmac_features & IEEE_SOFTMAC_BEACONS) + ieee80211_beacons_start(ieee); +} + + +void ieee80211_softmac_stop_scan(struct ieee80211_device *ieee) +{ +// unsigned long flags; + + ieee->sync_scan_hurryup = 1; + + down(&ieee->scan_sem); +// spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->scanning == 1){ + //printk("%s():Stop scan now\n",__FUNCTION__); + ieee->scanning = 0; + //lzm add for softmac_scan_wq can't return from out + //example: rcv probe_response + ieee->scan_watchdog = 0;//lzm add 081215 for roaming + ieee->actscanning = false; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + cancel_delayed_work(&ieee->softmac_scan_wq); +#else + del_timer_sync(&ieee->scan_timer); +#endif + } + +// spin_unlock_irqrestore(&ieee->lock, flags); + up(&ieee->scan_sem); +} + +void ieee80211_stop_scan(struct ieee80211_device *ieee) +{ + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) + ieee80211_softmac_stop_scan(ieee); + else + ieee->stop_scan(ieee->dev); +} + +/* called with ieee->lock held */ +void ieee80211_start_scan(struct ieee80211_device *ieee) +{ + ieee->actscanning = true; +#ifdef CONFIG_IPS + ieee->ieee80211_ips_leave(ieee->dev); +#endif + +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(ieee) ) + { + if(IS_COUNTRY_IE_VALID(ieee)) + { + RESET_CIE_WATCHDOG(ieee); + } + } +#endif + + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN){ + if (ieee->scanning == 0){ + ieee->scanning = 1; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_delayed_work(ieee->wq, &ieee->softmac_scan_wq,0); +#else + ieee80211_softmac_scan(ieee); +#endif + } + }else + ieee->start_scan(ieee->dev); + +} + +/* called with wx_sem held */ +void ieee80211_start_scan_syncro(struct ieee80211_device *ieee) +{ + //printk("====>%s()\n", __func__); +#ifdef CONFIG_IPS + ieee->ieee80211_ips_leave(ieee->dev); +#endif + ieee->actscanning = true; + +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(ieee) ) + { + if(IS_COUNTRY_IE_VALID(ieee)) + { + RESET_CIE_WATCHDOG(ieee); + } + } +#endif + + ieee->sync_scan_hurryup = 0; + + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) + ieee80211_softmac_scan_syncro(ieee); + else + ieee->scan_syncro(ieee->dev); + + ieee->actscanning = false; + //printk("<====%s()\n", __func__); +} + +inline struct sk_buff *ieee80211_authentication_req(struct ieee80211_network *beacon, + struct ieee80211_device *ieee, int challengelen) +{ + struct sk_buff *skb; + struct ieee80211_authentication *auth; + + skb = dev_alloc_skb(sizeof(struct ieee80211_authentication) + challengelen); + + if (!skb) return NULL; + + auth = (struct ieee80211_authentication *) + skb_put(skb, sizeof(struct ieee80211_authentication)); + + auth->header.frame_ctl = IEEE80211_STYPE_AUTH; + if (challengelen) auth->header.frame_ctl |= IEEE80211_FCTL_WEP; + + auth->header.duration_id = 0x013a; //FIXME + + memcpy(auth->header.addr1, beacon->bssid, ETH_ALEN); + memcpy(auth->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memcpy(auth->header.addr3, beacon->bssid, ETH_ALEN); + + auth->algorithm = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; + + auth->transaction = cpu_to_le16(ieee->associate_seq); + ieee->associate_seq++; + + auth->status = cpu_to_le16(WLAN_STATUS_SUCCESS); + + return skb; + +} + +u8 WPA_OUI[3] = {0x00, 0x50, 0xf2}; + +static struct sk_buff* ieee80211_probe_resp(struct ieee80211_device *ieee, u8 *dest) +{ + u8 *tag; + int beacon_size; + struct ieee80211_probe_response *beacon_buf; + struct sk_buff *skb; + int encrypt; + int atim_len,erp_len; + struct ieee80211_crypt_data* crypt; + + char *ssid = ieee->current_network.ssid; + int ssid_len = ieee->current_network.ssid_len; + int rate_len = ieee->current_network.rates_len+2; + int rate_ex_len = ieee->current_network.rates_ex_len; + + int wpa_ie_len = 0, wpa_type=0; + if(rate_ex_len > 0) rate_ex_len+=2; + + if(ieee->current_network.capability & WLAN_CAPABILITY_IBSS) + atim_len = 4; + else + atim_len = 0; + + if(ieee80211_is_54g(ieee->current_network)) + erp_len = 3; + else + erp_len = 0; + if (ieee->wpa_enabled) + { + // printk("hoho wpa_enalbe\n"); + wpa_ie_len = ieee->wpa_ie_len; //24-2 + } + beacon_size = sizeof(struct ieee80211_probe_response)+ + ssid_len + +3 //channel + +rate_len + +rate_ex_len + +atim_len + +erp_len + +wpa_ie_len; + + skb = dev_alloc_skb(beacon_size); + + if (!skb) + return NULL; + + beacon_buf = (struct ieee80211_probe_response*) skb_put(skb, beacon_size); + + memcpy (beacon_buf->header.addr1, dest,ETH_ALEN); + memcpy (beacon_buf->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memcpy (beacon_buf->header.addr3, ieee->current_network.bssid, ETH_ALEN); + + beacon_buf->header.duration_id = 0; //FIXME + beacon_buf->beacon_interval = + cpu_to_le16(ieee->current_network.beacon_interval); + beacon_buf->capability = + cpu_to_le16(ieee->current_network.capability & WLAN_CAPABILITY_IBSS); + + if(ieee->short_slot && (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_SLOT)) + cpu_to_le16((beacon_buf->capability |= WLAN_CAPABILITY_SHORT_SLOT)); +#ifdef _RTL8187_EXT_PATCH_ +{ +/* struct ieee80211_crypt_data_list* cryptlist = ieee->cryptlist[1]; + u8 i = cryptlist->used; + crypt = cryptlist ->crypt[ieee->tx_keyidx]; +*/ + crypt = ieee->cryptlist[0]->crypt[ieee->tx_keyidx]; +} +#else + + crypt = ieee->crypt[ieee->tx_keyidx]; +#endif + if (crypt) + wpa_type = strcmp(crypt->ops->name, "TKIP"); + + + encrypt = ieee->host_encrypt && crypt && crypt->ops && + ((0 == strcmp(crypt->ops->name, "WEP")||wpa_ie_len)); + + if (encrypt) + beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + + beacon_buf->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_RESP); + + beacon_buf->info_element.id = MFIE_TYPE_SSID; + beacon_buf->info_element.len = ssid_len; + + tag = (u8*) beacon_buf->info_element.data; + + memcpy(tag, ssid, ssid_len); + + tag += ssid_len; + + *(tag++) = MFIE_TYPE_RATES; + *(tag++) = rate_len-2; + memcpy(tag,ieee->current_network.rates,rate_len-2); + tag+=rate_len-2; + + *(tag++) = MFIE_TYPE_DS_SET; + *(tag++) = 1; + *(tag++) = ieee->current_network.channel; + + if(atim_len){ + u16 val16; + *(tag++) = MFIE_TYPE_IBSS_SET; + *(tag++) = 2; + val16 = cpu_to_le16(ieee->current_network.atim_window); + //*((u16*)(tag)) = cpu_to_le16(ieee->current_network.atim_window); + memcpy((u8 *)tag,(u8 *)&val16,2); + tag+=2; + } + + if(erp_len){ + *(tag++) = MFIE_TYPE_ERP; + *(tag++) = 1; + *(tag++) = 0; + } + + if(rate_ex_len){ + *(tag++) = MFIE_TYPE_RATES_EX; + *(tag++) = rate_ex_len-2; + memcpy(tag,ieee->current_network.rates_ex,rate_ex_len-2); + tag+=rate_ex_len-2; + } + if (wpa_ie_len) + { +#if 0 + *(tag++) = 0xdd; + *(tag++) = wpa_ie_len-2; + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = 1; + *(tag++) = 1; + *(tag++) = 0; + + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = wpa_type ? 4:2; + *(tag++) = 1; + *(tag++) = 0; + + + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = wpa_type ? 4:0; + *(tag++) = 1; + *(tag++) = 0; + + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = 0; +#else + if (ieee->iw_mode == IW_MODE_ADHOC) + {//as Windows will set pairwise key same as the group key which is not allowed in Linux, so set this for IOT issue. WB 2008.07.07 + memcpy(&ieee->wpa_ie[14], &ieee->wpa_ie[8], 4); + } + + memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len); + +#endif + } + + + skb->dev = ieee->dev; + return skb; +} + + +#ifdef _RTL8187_EXT_PATCH_ +struct sk_buff* ieee80211_ext_probe_resp_by_net(struct ieee80211_device *ieee, u8 *dest, struct ieee80211_network *net) +{ + u8 *tag; + int beacon_size; + struct ieee80211_probe_response *beacon_buf; + struct sk_buff *skb; + int encrypt; + int atim_len,erp_len; + struct ieee80211_crypt_data* crypt; + u8 broadcast_addr[] = {0xff,0xff,0xff,0xff,0xff,0xff}; + + char *ssid = net->ssid; + int ssid_len = net->ssid_len; + + int rate_len = ieee->current_network.rates_len+2; + int rate_ex_len = ieee->current_network.rates_ex_len; + int wpa_ie_len = 0, wpa_type=0; + if(rate_ex_len > 0) rate_ex_len+=2; + + if( ieee->meshScanMode&4) + ieee->current_network.channel = ieee->ext_patch_ieee80211_ext_stop_scan_wq_set_channel(ieee); + if( ieee->meshScanMode&6) + { + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->ext_stop_scan_wq); +#else + schedule_task(&ieee->ext_stop_scan_wq); +#endif + } + if(ieee->current_network.capability & WLAN_CAPABILITY_IBSS) // use current_network here + atim_len = 4; + else + atim_len = 0; + + if(ieee80211_is_54g(*net)) + erp_len = 3; + else + erp_len = 0; + + if (ieee->wpa_enabled &&(ieee->iw_ext_mode==ieee->iw_mode)) + { +// printk("hoho wpa_enalbe\n"); + wpa_ie_len = ieee->wpa_ie_len; //24-2 + } + + beacon_size = sizeof(struct ieee80211_probe_response)+ + ssid_len + +3 //channel + +rate_len + +rate_ex_len + +atim_len + +erp_len + +wpa_ie_len; +//b + skb = dev_alloc_skb(beacon_size+196); + + if (!skb) + return NULL; + + beacon_buf = (struct ieee80211_probe_response*) skb_put(skb, beacon_size); + + memcpy (beacon_buf->header.addr1, dest,ETH_ALEN); + memcpy (beacon_buf->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memcpy (beacon_buf->header.addr3, ieee->current_network.bssid, ETH_ALEN); + + beacon_buf->header.duration_id = 0; //FIXME + + beacon_buf->beacon_interval = + cpu_to_le16(ieee->current_network.beacon_interval); // use current_network here + beacon_buf->capability = + cpu_to_le16(ieee->current_network.capability & WLAN_CAPABILITY_IBSS); + + if(ieee->short_slot && (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_SLOT)) + cpu_to_le16((beacon_buf->capability |= WLAN_CAPABILITY_SHORT_SLOT)); +#ifdef _RTL8187_EXT_PATCH_ + + crypt = ieee->cryptlist[0]->crypt[ieee->tx_keyidx]; +#else + + crypt = ieee->crypt[ieee->tx_keyidx]; +#endif + +// crypt = ieee->crypt[ieee->tx_keyidx]; + if (crypt) + wpa_type = strcmp(crypt->ops->name, "TKIP"); + + encrypt = ieee->host_encrypt && crypt && crypt->ops && + ((0 == strcmp(crypt->ops->name, "WEP")||wpa_ie_len)); + + if (encrypt) + beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + + beacon_buf->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_RESP); + + beacon_buf->info_element.id = MFIE_TYPE_SSID; + beacon_buf->info_element.len = ssid_len; + + tag = (u8*) beacon_buf->info_element.data; + + // brocad cast / probe rsp + if(memcmp(dest, broadcast_addr, ETH_ALEN )) + memcpy(tag, ssid, ssid_len); + else + ssid_len=0; + + tag += ssid_len; + +//get_bssrate_set(priv, _SUPPORTEDRATES_IE_, &pbssrate, &bssrate_len); +//pbuf = set_ie(pbuf, _SUPPORTEDRATES_IE_, bssrate_len, pbssrate, &frlen); + + *(tag++) = MFIE_TYPE_RATES; + *(tag++) = rate_len-2; + memcpy(tag,ieee->current_network.rates,rate_len-2); + tag+=rate_len-2; + + *(tag++) = MFIE_TYPE_DS_SET; + *(tag++) = 1; + *(tag++) = ieee->current_network.channel; // use current_network here + + + if(atim_len){ + *(tag++) = MFIE_TYPE_IBSS_SET; + *(tag++) = 2; + *((u16*)(tag)) = cpu_to_le16(ieee->current_network.atim_window); // use current_network here + tag+=2; + } + + if(erp_len){ + *(tag++) = MFIE_TYPE_ERP; + *(tag++) = 1; + *(tag++) = 0; + } + + if(rate_ex_len){ + *(tag++) = MFIE_TYPE_RATES_EX; + *(tag++) = rate_ex_len-2; + memcpy(tag,ieee->current_network.rates_ex,rate_ex_len-2); + tag+=rate_ex_len-2; + } + + if (wpa_ie_len) + { +#if 0 + *(tag++) = 0xdd; + *(tag++) = wpa_ie_len-2; + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = 1; + *(tag++) = 1; + *(tag++) = 0; + + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = wpa_type ? 4:2; + *(tag++) = 1; + *(tag++) = 0; + + + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = wpa_type ? 4:0; + *(tag++) = 1; + *(tag++) = 0; + + memcpy(tag, WPA_OUI, 3); + tag += 3; + *(tag++) = 0; +#else + memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len); +#endif + } + + + skb->dev = ieee->dev; + return skb; +} +#endif // _RTL8187_EXT_PATCH_ + + +struct sk_buff* ieee80211_assoc_resp(struct ieee80211_device *ieee, u8 *dest) +{ + struct sk_buff *skb; + u8* tag; + + struct ieee80211_crypt_data* crypt; + struct ieee80211_assoc_response_frame *assoc; + short encrypt; + + unsigned int rate_len = ieee80211_MFIE_rate_len(ieee); + int len = sizeof(struct ieee80211_assoc_response_frame) + rate_len; + + skb = dev_alloc_skb(len); + + if (!skb) + return NULL; + + assoc = (struct ieee80211_assoc_response_frame *) + skb_put(skb,sizeof(struct ieee80211_assoc_response_frame)); + + assoc->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP); + memcpy(assoc->header.addr1, dest,ETH_ALEN); + memcpy(assoc->header.addr3, ieee->dev->dev_addr, ETH_ALEN); + memcpy(assoc->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + assoc->capability = cpu_to_le16(ieee->iw_mode == IW_MODE_MASTER ? + WLAN_CAPABILITY_BSS : WLAN_CAPABILITY_IBSS); + + + if(ieee->short_slot) + assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT); + + if (ieee->host_encrypt){ +#ifdef _RTL8187_EXT_PATCH_ + crypt = ieee->cryptlist[0]->crypt[ieee->tx_keyidx]; +#else + crypt = ieee->crypt[ieee->tx_keyidx]; +#endif + } + else crypt = NULL; + + encrypt = ( crypt && crypt->ops); + + if (encrypt) + assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + assoc->status = 0; + assoc->aid = cpu_to_le16(ieee->assoc_id); + if (ieee->assoc_id == 0x2007) ieee->assoc_id=0; + else ieee->assoc_id++; + + tag = (u8*) skb_put(skb, rate_len); + + ieee80211_MFIE_Brate(ieee, &tag); + ieee80211_MFIE_Grate(ieee, &tag); + + return skb; +} + +struct sk_buff* ieee80211_auth_resp(struct ieee80211_device *ieee,int status, u8 *dest) +{ + struct sk_buff *skb; + struct ieee80211_authentication *auth; + + skb = dev_alloc_skb(sizeof(struct ieee80211_authentication)+1); + + if (!skb) + return NULL; + + skb->len = sizeof(struct ieee80211_authentication); + + auth = (struct ieee80211_authentication *)skb->data; + + auth->status = cpu_to_le16(status); + auth->transaction = cpu_to_le16(2); + auth->algorithm = cpu_to_le16(WLAN_AUTH_OPEN); + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + memcpy(auth->header.addr3, dest, ETH_ALEN); +#else + memcpy(auth->header.addr3, ieee->dev->dev_addr, ETH_ALEN); +#endif + memcpy(auth->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memcpy(auth->header.addr1, dest, ETH_ALEN); + auth->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_AUTH); + return skb; + + +} + +struct sk_buff* ieee80211_null_func(struct ieee80211_device *ieee,short pwr) +{ + struct sk_buff *skb; + struct ieee80211_hdr_3addr* hdr; + + skb = dev_alloc_skb(sizeof(struct ieee80211_hdr_3addr)); + + if (!skb) + return NULL; + + hdr = (struct ieee80211_hdr_3addr*)skb_put(skb,sizeof(struct ieee80211_hdr_3addr)); + + memcpy(hdr->addr1, ieee->current_network.bssid, ETH_ALEN); + memcpy(hdr->addr2, ieee->dev->dev_addr, ETH_ALEN); + memcpy(hdr->addr3, ieee->current_network.bssid, ETH_ALEN); + + hdr->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS | + (pwr ? IEEE80211_FCTL_PM:0)); + + return skb; + + +} + + +void ieee80211_resp_to_assoc_rq(struct ieee80211_device *ieee, u8* dest) +{ + struct sk_buff *buf = ieee80211_assoc_resp(ieee, dest); + + if (buf){ + softmac_mgmt_xmit(buf, ieee); + dev_kfree_skb_any(buf);//edit by thomas + } +} + + +void ieee80211_resp_to_auth(struct ieee80211_device *ieee, int s, u8* dest) +{ + struct sk_buff *buf = ieee80211_auth_resp(ieee, s, dest); + + if (buf){ + softmac_mgmt_xmit(buf, ieee); + dev_kfree_skb_any(buf);//edit by thomas + } +} + + +void ieee80211_resp_to_probe(struct ieee80211_device *ieee, u8 *dest) +{ + + struct sk_buff *buf = ieee80211_probe_resp(ieee, dest); + + if (buf) { + softmac_mgmt_xmit(buf, ieee); + dev_kfree_skb_any(buf);//edit by thomas + } +} + + +inline struct sk_buff *ieee80211_association_req(struct ieee80211_network *beacon,struct ieee80211_device *ieee) +{ + struct sk_buff *skb; + + struct ieee80211_assoc_request_frame *hdr; + u8 *tag; + //int i; + unsigned int wpa_len = beacon->wpa_ie_len; +#if 1 + // for testing purpose + unsigned int rsn_len = beacon->rsn_ie_len; +#else + unsigned int rsn_len = beacon->rsn_ie_len - 4; +#endif + unsigned int rate_len = ieee80211_MFIE_rate_len(ieee); + unsigned int wmm_info_len = beacon->QoS_Enable?9:0; +#ifdef THOMAS_TURBO + unsigned int turbo_info_len = beacon->Turbo_Enable?9:0; +#endif + + u8 encry_proto = ieee->wpax_type_notify & 0xff; + + + int len = 0; + + //[0] Notify type of encryption: WPA/WPA2 + //[1] pair wise type + //[2] authen type + if(ieee->wpax_type_set) { + if (IEEE_PROTO_WPA == encry_proto) { + rsn_len = 0; + } else if (IEEE_PROTO_RSN == encry_proto) { + wpa_len = 0; + } + } +#ifdef THOMAS_TURBO + len = sizeof(struct ieee80211_assoc_request_frame)+ + + beacon->ssid_len//essid tagged val + + rate_len//rates tagged val + + wpa_len + + rsn_len + + wmm_info_len + + turbo_info_len; +#else + len = sizeof(struct ieee80211_assoc_request_frame)+ + + beacon->ssid_len//essid tagged val + + rate_len//rates tagged val + + wpa_len + + rsn_len + + wmm_info_len; +#endif + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) + skb = dev_alloc_skb(len+256); // stanley + else +#endif + skb = dev_alloc_skb(len); + + if (!skb) + return NULL; + + hdr = (struct ieee80211_assoc_request_frame *) + skb_put(skb, sizeof(struct ieee80211_assoc_request_frame)); + + + hdr->header.frame_ctl = IEEE80211_STYPE_ASSOC_REQ; + hdr->header.duration_id= 37; //FIXME + memcpy(hdr->header.addr1, beacon->bssid, ETH_ALEN); + memcpy(hdr->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + memcpy(hdr->header.addr3, beacon->bssid, ETH_ALEN); + memcpy(ieee->ap_mac_addr, beacon->bssid, ETH_ALEN);//for HW security, John + + hdr->capability = cpu_to_le16(WLAN_CAPABILITY_BSS); + if (beacon->capability & WLAN_CAPABILITY_PRIVACY ) + hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + if (beacon->capability & WLAN_CAPABILITY_SHORT_PREAMBLE ) + hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); + + if(ieee->short_slot) + hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT); + +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_association_req_1) + ieee->ext_patch_ieee80211_association_req_1(hdr); +#endif + + hdr->listen_interval = 0xa; //FIXME + + hdr->info_element.id = MFIE_TYPE_SSID; + + hdr->info_element.len = beacon->ssid_len; + tag = skb_put(skb, beacon->ssid_len); + memcpy(tag, beacon->ssid, beacon->ssid_len); + + tag = skb_put(skb, rate_len); + + ieee80211_MFIE_Brate(ieee, &tag); + ieee80211_MFIE_Grate(ieee, &tag); + + //add rsn==0 condition for ap's mix security mode(wpa+wpa2), john2007.8.9 + //choose AES encryption as default algorithm while using mixed mode +#if 0 + if(rsn_len == 0){ + + tag = skb_put(skb,wpa_len); + + if(wpa_len) { + + + //{add by david. 2006.8.31 + //fix linksys compatibility bug + //} + if(wpa_len > 24) {//22+2, mean include the capability + beacon->wpa_ie[wpa_len - 2] = 0; + } + //multicast cipher OUI + if( beacon->wpa_ie[11]==0x2 ){ //0x0050f202 is the oui of tkip + ieee->broadcast_key_type = KEY_TYPE_TKIP; + } + else if( beacon->wpa_ie[11]==0x4 ){//0x0050f204 is the oui of ccmp + ieee->broadcast_key_type = KEY_TYPE_CCMP; + } + //unicast cipher OUI + if( beacon->wpa_ie[14]==0 + && beacon->wpa_ie[15]==0x50 + && beacon->wpa_ie[16]==0xf2 + && beacon->wpa_ie[17]==0x2 ){ //0x0050f202 is the oui of tkip + ieee->pairwise_key_type = KEY_TYPE_TKIP; + } + + else if( beacon->wpa_ie[14]==0 + && beacon->wpa_ie[15]==0x50 + && beacon->wpa_ie[16]==0xf2 + && beacon->wpa_ie[17]==0x4 ){//0x0050f204 is the oui of ccmp + ieee->pairwise_key_type = KEY_TYPE_CCMP; + } + //indicate the wpa_ie content to WPA_SUPPLICANT + buff = kmalloc(IW_CUSTOM_MAX, GFP_ATOMIC); + memset(buff, 0, IW_CUSTOM_MAX); + p=buff; + p += sprintf(p, "ASSOCINFO(ReqIEs="); + for(i=0;iwpa_ie[i]); + } + p += sprintf(p, ")"); + memset(&wrqu, 0, sizeof(wrqu) ); + wrqu.data.length = p - buff; + + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buff); + memcpy(tag,beacon->wpa_ie,wpa_len); + } + + } + + if(rsn_len > 22) { + + if( beacon->rsn_ie[4]==0x0 && + beacon->rsn_ie[5]==0xf && + beacon->rsn_ie[6]==0xac){ + + switch(beacon->rsn_ie[7]){ + case 0x1: + ieee->broadcast_key_type = KEY_TYPE_WEP40; + break; + case 0x2: + ieee->broadcast_key_type = KEY_TYPE_TKIP; + break; + case 0x4: + ieee->broadcast_key_type = KEY_TYPE_CCMP; + break; + case 0x5: + ieee->broadcast_key_type = KEY_TYPE_WEP104; + break; + default: + printk("fault suite type in RSN broadcast key\n"); + break; + } + } + + if( beacon->rsn_ie[10]==0x0 && + beacon->rsn_ie[11]==0xf && + beacon->rsn_ie[12]==0xac){ + if(beacon->rsn_ie[8]==1){//not mixed mode + switch(beacon->rsn_ie[13]){ + case 0x2: + ieee->pairwise_key_type = KEY_TYPE_TKIP; + break; + case 0x4: + ieee->pairwise_key_type = KEY_TYPE_CCMP; + break; + default: + printk("fault suite type in RSN pairwise key\n"); + break; + } + } + else if(beacon->rsn_ie[8]==2){//mixed mode + ieee->pairwise_key_type = KEY_TYPE_CCMP; + } + } + + + + tag = skb_put(skb,22); + memcpy(tag,(beacon->rsn_ie + info_addr),8); + tag[1] = 20; + tag += 8; + info_addr += 8; + + spin_lock_irqsave(&ieee->wpax_suitlist_lock,flags); + for (i = 0; i < 2; i++) { + tag[0] = 1; + tag[1] = 0; + tag += 2; + suite_count = beacon->rsn_ie[info_addr] + \ + (beacon->rsn_ie[info_addr + 1] << 8); + info_addr += 2; + if(1 == suite_count) { + memcpy(tag,(beacon->rsn_ie + info_addr),4); + info_addr += 4; + } else { + // if the wpax_type_notify has been set by the application, + // just use it, otherwise just use the default one. + if(ieee->wpax_type_set) { + suit_select = ((0 == i) ? pairwise_type:authen_type)&0x0f ; + memcpy(tag,rsn_authen_cipher_suite[suit_select],4); + } else { + //default set as ccmp, or none authentication + if(i == 0) { + memcpy(tag,rsn_authen_cipher_suite[4],4); + } else { + memcpy(tag,rsn_authen_cipher_suite[2],4); + } + + } + + info_addr += (suite_count * 4); + } + tag += 4; + } + spin_unlock_irqrestore(&ieee->wpax_suitlist_lock,flags); + + tag[0] = 0; + tag[1] = beacon->rsn_ie[info_addr+1]; + + + + } else { + tag = skb_put(skb,rsn_len); + if(rsn_len) { + + + if( beacon->rsn_ie[4]==0x0 && + beacon->rsn_ie[5]==0xf && + beacon->rsn_ie[6]==0xac){ + switch(beacon->rsn_ie[7]){ + case 0x1: + ieee->broadcast_key_type = KEY_TYPE_WEP40; + break; + case 0x2: + ieee->broadcast_key_type = KEY_TYPE_TKIP; + break; + case 0x4: + ieee->broadcast_key_type = KEY_TYPE_CCMP; + break; + case 0x5: + ieee->broadcast_key_type = KEY_TYPE_WEP104; + break; + default: + printk("fault suite type in RSN broadcast key\n"); + break; + } + } + if( beacon->rsn_ie[10]==0x0 && + beacon->rsn_ie[11]==0xf && + beacon->rsn_ie[12]==0xac){ + if(beacon->rsn_ie[8]==1){//not mixed mode + switch(beacon->rsn_ie[13]){ + case 0x2: + ieee->pairwise_key_type = KEY_TYPE_TKIP; + break; + case 0x4: + ieee->pairwise_key_type = KEY_TYPE_CCMP; + break; + default: + printk("fault suite type in RSN pairwise key\n"); + break; + } + + } + else if(beacon->rsn_ie[8]==2){//mixed mode + ieee->pairwise_key_type = KEY_TYPE_CCMP; + } + } + + + beacon->rsn_ie[rsn_len - 2] = 0; + memcpy(tag,beacon->rsn_ie,rsn_len); + } + } +#else + if (ieee->wpa_ie){ + tag = skb_put(skb,ieee->wpa_ie_len); + memcpy(tag,ieee->wpa_ie,ieee->wpa_ie_len); + } +#endif + tag = skb_put(skb,wmm_info_len); + if(wmm_info_len) { + ieee80211_WMM_Info(ieee, &tag); + } +#ifdef THOMAS_TURBO + tag = skb_put(skb,turbo_info_len); + if(turbo_info_len) { + ieee80211_TURBO_Info(ieee, &tag); + } +#endif + +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_association_req_2) + ieee->ext_patch_ieee80211_association_req_2(ieee, beacon, skb); +#endif + + return skb; +} + +void ieee80211_associate_abort(struct ieee80211_device *ieee) +{ + + unsigned long flags; + spin_lock_irqsave(&ieee->lock, flags); + + ieee->associate_seq++; + + /* don't scan, and avoid to have the RX path possibily + * try again to associate. Even do not react to AUTH or + * ASSOC response. Just wait for the retry wq to be scheduled. + * Here we will check if there are good nets to associate + * with, so we retry or just get back to NO_LINK and scanning + */ + if (ieee->state == IEEE80211_ASSOCIATING_AUTHENTICATING){ + IEEE80211_DEBUG_MGMT("Authentication failed\n"); + ieee->softmac_stats.no_auth_rs++; + }else{ + IEEE80211_DEBUG_MGMT("Association failed\n"); + ieee->softmac_stats.no_ass_rs++; + } + + ieee->state = IEEE80211_ASSOCIATING_RETRY; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_delayed_work(ieee->wq, &ieee->associate_retry_wq, \ + IEEE80211_SOFTMAC_ASSOC_RETRY_TIME); +#else + schedule_task(&ieee->associate_retry_wq); +#endif + + spin_unlock_irqrestore(&ieee->lock, flags); +} + +void ieee80211_associate_abort_cb(unsigned long dev) +{ + ieee80211_associate_abort((struct ieee80211_device *) dev); +} + + +void ieee80211_associate_step1(struct ieee80211_device *ieee) +{ + struct ieee80211_network *beacon = &ieee->current_network; + struct sk_buff *skb; + + IEEE80211_DEBUG_MGMT("Stopping scan\n"); + + ieee->softmac_stats.tx_auth_rq++; + skb=ieee80211_authentication_req(beacon, ieee, 0); + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode ) { + if(skb) + softmac_mgmt_xmit(skb, ieee); + return; + }else +#endif + if (!skb) + ieee80211_associate_abort(ieee); + else{ + ieee->state = IEEE80211_ASSOCIATING_AUTHENTICATING ; + IEEE80211_DEBUG_MGMT("Sending authentication request\n"); + //printk(KERN_WARNING "Sending authentication request\n"); + softmac_mgmt_xmit(skb, ieee); + //BUGON when you try to add_timer twice, using mod_timer may be better, john0709 + if(!timer_pending(&ieee->associate_timer)){ + ieee->associate_timer.expires = jiffies + (HZ / 2); + add_timer(&ieee->associate_timer); + } + dev_kfree_skb_any(skb);//edit by thomas + } +} + +void ieee80211_auth_challenge(struct ieee80211_device *ieee, u8 *challenge, int chlen) +{ + u8 *c; + struct sk_buff *skb; + struct ieee80211_network *beacon = &ieee->current_network; +// int hlen = sizeof(struct ieee80211_authentication); + + ieee->associate_seq++; + ieee->softmac_stats.tx_auth_rq++; + + skb = ieee80211_authentication_req(beacon, ieee, chlen+2); + if (!skb) + ieee80211_associate_abort(ieee); + else{ + c = skb_put(skb, chlen+2); + *(c++) = MFIE_TYPE_CHALLENGE; + *(c++) = chlen; + memcpy(c, challenge, chlen); + + IEEE80211_DEBUG_MGMT("Sending authentication challenge response\n"); + + ieee80211_encrypt_fragment(ieee, skb, sizeof(struct ieee80211_hdr_3addr )); + + softmac_mgmt_xmit(skb, ieee); + + if(!timer_pending(&ieee->associate_timer)){ + ieee->associate_timer.expires = jiffies + (HZ / 2); + add_timer(&ieee->associate_timer); + } + dev_kfree_skb_any(skb);//edit by thomas + } + kfree(challenge); +} + +#ifdef _RTL8187_EXT_PATCH_ + +// based on ieee80211_assoc_resp +struct sk_buff* ieee80211_assoc_resp_by_net(struct ieee80211_device *ieee, u8 *dest, unsigned short status, struct ieee80211_network *pstat, int pkt_type) +{ + struct sk_buff *skb; + u8* tag; + + struct ieee80211_crypt_data* crypt; + struct ieee80211_assoc_response_frame *assoc; + short encrypt; + + unsigned int rate_len = ieee80211_MFIE_rate_len(ieee); + int len = sizeof(struct ieee80211_assoc_response_frame) + rate_len; + + if(ieee->iw_mode == ieee->iw_ext_mode) + skb = dev_alloc_skb(len+256); // stanley + else + skb = dev_alloc_skb(len); + + if (!skb) + return NULL; + + assoc = (struct ieee80211_assoc_response_frame *) + skb_put(skb,sizeof(struct ieee80211_assoc_response_frame)); + + assoc->header.frame_ctl = cpu_to_le16(pkt_type); + + memcpy(assoc->header.addr1, dest,ETH_ALEN); + memcpy(assoc->header.addr3, ieee->dev->dev_addr, ETH_ALEN); + memcpy(assoc->header.addr2, ieee->dev->dev_addr, ETH_ALEN); + assoc->capability = cpu_to_le16(ieee->iw_mode == IW_MODE_MASTER ? + WLAN_CAPABILITY_BSS : WLAN_CAPABILITY_IBSS); + + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_assoc_resp_by_net_1) + ieee->ext_patch_ieee80211_assoc_resp_by_net_1(assoc); + + if(ieee->short_slot) + assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT); + + if (ieee->host_encrypt) +#ifdef _RTL8187_EXT_PATCH_ + crypt = ieee->cryptlist[0]->crypt[ieee->tx_keyidx]; +#else + crypt = ieee->crypt[ieee->tx_keyidx]; +#endif + + else crypt = NULL; + + encrypt = ( crypt && crypt->ops); + + if (encrypt) + assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + assoc->status = 0; + assoc->aid = cpu_to_le16(ieee->assoc_id); + if (ieee->assoc_id == 0x2007) ieee->assoc_id=0; + else ieee->assoc_id++; + + assoc->info_element.id = 230; // Stanley, an unused id (just a hot fix) + assoc->info_element.len = 0; + + tag = (u8*) skb_put(skb, rate_len); + + ieee80211_MFIE_Brate(ieee, &tag); + ieee80211_MFIE_Grate(ieee, &tag); + + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_assoc_resp_by_net_2) + ieee->ext_patch_ieee80211_assoc_resp_by_net_2(ieee, pstat, pkt_type, skb); + + return skb; +} + +// based on ieee80211_resp_to_assoc_rq +void ieee80211_ext_issue_assoc_rsp(struct ieee80211_device *ieee, u8 *dest, unsigned short status, struct ieee80211_network *pstat, int pkt_type) +{ + struct sk_buff *buf = ieee80211_assoc_resp_by_net(ieee, dest, status, pstat, pkt_type); + + if (buf) + softmac_mgmt_xmit(buf, ieee); +} + +// based on ieee80211_associate_step2 +void ieee80211_ext_issue_assoc_req(struct ieee80211_device *ieee, struct ieee80211_network *pstat) +{ + + struct sk_buff* skb; + + // printk("@@@@@ ieee80211_ext_issue_assoc_req on channel: %d\n", ieee->current_network.channel); + + ieee->softmac_stats.tx_ass_rq++; + skb=ieee80211_association_req(pstat, ieee); + if (skb) + softmac_mgmt_xmit(skb, ieee); +} + +void ieee80211_ext_issue_disassoc(struct ieee80211_device *ieee, struct ieee80211_network *pstat, int reason, unsigned char extReason) +{ + // do nothing + // printk("@@@@@ ieee80211_ext_issue_disassoc\n"); + return; +} +#endif // _RTL8187_EXT_PATCH_ + +void ieee80211_associate_step2(struct ieee80211_device *ieee) +{ + struct sk_buff* skb; + struct ieee80211_network *beacon = &ieee->current_network; + +// del_timer_sync(&ieee->associate_timer); + + IEEE80211_DEBUG_MGMT("Sending association request\n"); + + ieee->softmac_stats.tx_ass_rq++; + skb=ieee80211_association_req(beacon, ieee); + if (!skb) + ieee80211_associate_abort(ieee); + else{ + softmac_mgmt_xmit(skb, ieee); + if(!timer_pending(&ieee->associate_timer)){ + ieee->associate_timer.expires = jiffies + (HZ / 2); + add_timer(&ieee->associate_timer); + } + dev_kfree_skb_any(skb);//edit by thomas + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void ieee80211_associate_complete_wq(struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, associate_complete_wq); +#else +void ieee80211_associate_complete_wq(struct ieee80211_device *ieee) +{ +#endif + printk(KERN_INFO "Associated successfully\n"); + if(ieee80211_is_54g(ieee->current_network) && + (ieee->modulation & IEEE80211_OFDM_MODULATION)){ + + ieee->rate = 540; + printk(KERN_INFO"Using G rates\n"); + }else{ + ieee->rate = 110; + printk(KERN_INFO"Using B rates\n"); + } + +//by lizhaoming for LED LINK +#ifdef LED_SHIN + { + struct net_device *dev = ieee->dev; + ieee->ieee80211_led_contorl(dev, LED_CTL_LINK); + } +#endif + + ieee->link_change(ieee->dev); + notify_wx_assoc_event(ieee); + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + netif_carrier_on(ieee->dev); +} + +void ieee80211_associate_complete(struct ieee80211_device *ieee) +{ + int i; +// struct net_device *dev = ieee->dev; + del_timer_sync(&ieee->associate_timer); + + for(i = 0; i < 6; i++) { +// ieee->seq_ctrl[i] = 0; + } + ieee->state = IEEE80211_LINKED; + IEEE80211_DEBUG_MGMT("Successfully associated\n"); + + //by lizhaoming for LED LINK + //ieee->ieee80211_led_contorl(dev, LED_CTL_LINK); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->associate_complete_wq); +#else + schedule_task(&ieee->associate_complete_wq); +#endif +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void ieee80211_associate_procedure_wq(struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, associate_procedure_wq); +#else +void ieee80211_associate_procedure_wq(struct ieee80211_device *ieee) +{ +#endif + ieee->sync_scan_hurryup = 1; + down(&ieee->wx_sem); + + if (ieee->data_hard_stop) + ieee->data_hard_stop(ieee->dev); + + ieee80211_stop_scan(ieee); + //printk("=======>%s set chan:%d\n", __func__, ieee->current_network.channel); + ieee->set_chan(ieee->dev, ieee->current_network.channel); + + ieee->associate_seq = 1; + ieee80211_associate_step1(ieee); + + up(&ieee->wx_sem); +} +#ifdef _RTL8187_EXT_PATCH_ +// based on ieee80211_associate_procedure_wq + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void ieee80211_ext_stop_scan_wq(struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, ext_stop_scan_wq); +#else +void ieee80211_ext_stop_scan_wq(struct ieee80211_device *ieee) +{ +#endif +/* + if (ieee->scanning == 0) + { + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_ext_stop_scan_wq_set_channel + && ( ieee->current_network.channel == ieee->ext_patch_ieee80211_ext_stop_scan_wq_set_channel(ieee) ) ) + return; + } +*/ + ieee->sync_scan_hurryup = 1; + + down(&ieee->wx_sem); + + // printk("@@@@@@@@@@ ieee80211_ext_stop_scan_wq\n"); + if (ieee->data_hard_stop) + ieee->data_hard_stop(ieee->dev); + + ieee80211_stop_scan(ieee); + + // set channel + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_ext_stop_scan_wq_set_channel) + { + int ch = ieee->ext_patch_ieee80211_ext_stop_scan_wq_set_channel(ieee); + ieee->current_network.channel = ch; + ieee->set_chan(ieee->dev, ch); + } + else + { + ieee->set_chan(ieee->dev, ieee->current_network.channel); + } + // + up(&ieee->wx_sem); +} + + +void ieee80211_ext_send_11s_beacon(struct ieee80211_device *ieee) +{ + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->ext_send_beacon_wq); + #else + schedule_task(&ieee->ext_send_beacon_wq); + #endif + +} + +#endif // _RTL8187_EXT_PATCH_ + +inline void ieee80211_softmac_new_net(struct ieee80211_device *ieee, struct ieee80211_network *net) +{ + u8 tmp_ssid[IW_ESSID_MAX_SIZE+1]; + int tmp_ssid_len = 0; + + short apset,ssidset,ssidbroad,apmatch,ssidmatch; +// printk("===============>%s()\n",__FUNCTION__); + /* we are interested in new new only if we are not associated + * and we are not associating / authenticating + */ + if (ieee->state != IEEE80211_NOLINK) + return; + + if ((ieee->iw_mode == IW_MODE_INFRA) && !(net->capability & WLAN_CAPABILITY_BSS)) + return; + + if ((ieee->iw_mode == IW_MODE_ADHOC) && !(net->capability & WLAN_CAPABILITY_IBSS)) + return; + + + if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC){ + /* if the user specified the AP MAC, we need also the essid + * This could be obtained by beacons or, if the network does not + * broadcast it, it can be put manually. + */ + apset = ieee->wap_set;//(memcmp(ieee->current_network.bssid, zero,ETH_ALEN)!=0 ); + ssidset = ieee->ssid_set;//ieee->current_network.ssid[0] != '\0'; + ssidbroad = !(net->ssid_len == 0 || net->ssid[0]== '\0'); + apmatch = (memcmp(ieee->current_network.bssid, net->bssid, ETH_ALEN)==0); + if(ieee->current_network.ssid_len != net->ssid_len) + ssidmatch = 0; + else + ssidmatch = (0==strncmp(ieee->current_network.ssid, net->ssid, net->ssid_len)); + + + + if ( /* if the user set the AP check if match. + * if the network does not broadcast essid we check the user supplyed ANY essid + * if the network does broadcast and the user does not set essid it is OK + * if the network does broadcast and the user did set essid chech if essid match + */ + ( apset && apmatch && + //((ssidset && ssidbroad && ssidmatch) || (ssidbroad && !ssidset) || (!ssidbroad && ssidset)) ) || + ((ssidset && ssidbroad && ssidmatch) || (!ssidbroad && ssidset)) ) || + /* if the ap is not set, check that the user set the bssid + * and the network does bradcast and that those two bssid matches + */ + (!apset && ssidset && ssidbroad && ssidmatch) + ){ + /* if the essid is hidden replace it with the + * essid provided by the user. + */ + if (!ssidbroad){ + strncpy(tmp_ssid, ieee->current_network.ssid, IW_ESSID_MAX_SIZE); + tmp_ssid_len = ieee->current_network.ssid_len; + } + memcpy(&ieee->current_network, net, sizeof(struct ieee80211_network)); + + if (!ssidbroad){ + strncpy(ieee->current_network.ssid, tmp_ssid, IW_ESSID_MAX_SIZE); + ieee->current_network.ssid_len = tmp_ssid_len; + } + printk(KERN_INFO"Linking with %s, channel:%d\n",ieee->current_network.ssid, ieee->current_network.channel); + +#ifdef CONFIG_IPS + ieee->ieee80211_ips_leave(ieee->dev); +#endif + + if (ieee->iw_mode == IW_MODE_INFRA){ + ieee->state = IEEE80211_ASSOCIATING; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->associate_procedure_wq); +#else + schedule_task(&ieee->associate_procedure_wq); +#endif + }else{ + ieee->state = IEEE80211_LINKED; + if(ieee80211_is_54g(ieee->current_network) && + (ieee->modulation & IEEE80211_OFDM_MODULATION)){ + ieee->rate = 540; + printk(KERN_INFO"Using G rates\n"); + }else{ + ieee->rate = 110; + printk(KERN_INFO"Using B rates\n"); + } + } + + } + } + +} + +void ieee80211_softmac_check_all_nets(struct ieee80211_device *ieee) +{ + unsigned long flags; + struct ieee80211_network *target; + + spin_lock_irqsave(&ieee->lock, flags); +#if 0 + list_for_each_entry(target, &ieee->network_list, list) { + printk(KERN_INFO"check network list SSID: %s, channel: %d\n",target->ssid,target->channel); + } +#endif + list_for_each_entry(target, &ieee->network_list, list) { + + /* if the state become different that NOLINK means + * we had found what we are searching for + */ + + if (ieee->state != IEEE80211_NOLINK) + break; + + if (ieee->scan_age == 0 || time_after(target->last_scanned + ieee->scan_age, jiffies)) + ieee80211_softmac_new_net(ieee, target); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + + //printk("<=====%s\n", __func__); +} + + +static inline u16 auth_parse(struct sk_buff *skb, u8** challenge, int *chlen) +{ + struct ieee80211_authentication *a; + u8 *t; + if (skb->len < (sizeof(struct ieee80211_authentication)-sizeof(struct ieee80211_info_element))){ + IEEE80211_DEBUG_MGMT("invalid len in auth resp: %d\n",skb->len); + return 0xcafe; + } + *challenge = NULL; + a = (struct ieee80211_authentication*) skb->data; + if(skb->len > (sizeof(struct ieee80211_authentication) +3)){ + t = skb->data + sizeof(struct ieee80211_authentication); + + if(*(t++) == MFIE_TYPE_CHALLENGE){ + *chlen = *(t++); + *challenge = (u8*)kmalloc(*chlen, GFP_ATOMIC); + memcpy(*challenge, t, *chlen); + } + } + + return cpu_to_le16(a->status); + +} + + +int auth_rq_parse(struct sk_buff *skb,u8* dest) +{ + struct ieee80211_authentication *a; + + if (skb->len < (sizeof(struct ieee80211_authentication)-sizeof(struct ieee80211_info_element))){ + IEEE80211_DEBUG_MGMT("invalid len in auth request: %d\n",skb->len); + return -1; + } + a = (struct ieee80211_authentication*) skb->data; + + memcpy(dest,a->header.addr2, ETH_ALEN); + + if (le16_to_cpu(a->algorithm) != WLAN_AUTH_OPEN) + return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + + return WLAN_STATUS_SUCCESS; +} + +static short probe_rq_parse(struct ieee80211_device *ieee, struct sk_buff *skb, u8 *src) +{ + u8 *tag; + u8 *skbend; + u8 *ssid=NULL; + u8 ssidlen = 0; + + struct ieee80211_hdr_3addr *header = + (struct ieee80211_hdr_3addr *) skb->data; + + if (skb->len < sizeof (struct ieee80211_hdr_3addr )) + return -1; /* corrupted */ + + memcpy(src,header->addr2, ETH_ALEN); + + skbend = (u8*)skb->data + skb->len; + + tag = skb->data + sizeof (struct ieee80211_hdr_3addr ); + + while (tag+1 < skbend){ + if (*tag == 0){ + ssid = tag+2; + ssidlen = *(tag+1); + break; + } + tag++; /* point to the len field */ + tag = tag + *(tag); /* point to the last data byte of the tag */ + tag++; /* point to the next tag */ + } + + //IEEE80211DMESG("Card MAC address is "MACSTR, MAC2STR(src)); + if (ssidlen == 0) return 1; + + if (!ssid) return 1; /* ssid not found in tagged param */ + return (!strncmp(ssid, ieee->current_network.ssid, ssidlen)); + +} + +int assoc_rq_parse(struct sk_buff *skb,u8* dest) +{ + struct ieee80211_assoc_request_frame *a; + + if (skb->len < (sizeof(struct ieee80211_assoc_request_frame) - + sizeof(struct ieee80211_info_element))) { + + IEEE80211_DEBUG_MGMT("invalid len in auth request:%d \n", skb->len); + return -1; + } + + a = (struct ieee80211_assoc_request_frame*) skb->data; + + memcpy(dest,a->header.addr2,ETH_ALEN); + + return 0; +} + +static inline u16 assoc_parse(struct sk_buff *skb, int *aid) +{ + struct ieee80211_assoc_response_frame *a; + if (skb->len < sizeof(struct ieee80211_assoc_response_frame)){ + IEEE80211_DEBUG_MGMT("invalid len in auth resp: %d\n", skb->len); + return 0xcafe; + } + + a = (struct ieee80211_assoc_response_frame*) skb->data; + *aid = le16_to_cpu(a->aid) & 0x3fff; + return le16_to_cpu(a->status); +} + +static inline void +ieee80211_rx_probe_rq(struct ieee80211_device *ieee, struct sk_buff *skb) +{ + u8 dest[ETH_ALEN]; + + //IEEE80211DMESG("Rx probe"); + ieee->softmac_stats.rx_probe_rq++; + //DMESG("Dest is "MACSTR, MAC2STR(dest)); + if (probe_rq_parse(ieee, skb, dest)){ + //IEEE80211DMESG("Was for me!"); + ieee->softmac_stats.tx_probe_rs++; + ieee80211_resp_to_probe(ieee, dest); + } +} + +//static inline void +inline void ieee80211_rx_auth_rq(struct ieee80211_device *ieee, struct sk_buff *skb) +{ + u8 dest[ETH_ALEN]; + int status; + //IEEE80211DMESG("Rx probe"); + ieee->softmac_stats.rx_auth_rq++; + + if ((status = auth_rq_parse(skb, dest))!= -1){ + ieee80211_resp_to_auth(ieee, status, dest); + } + //DMESG("Dest is "MACSTR, MAC2STR(dest)); + +} + +//static inline void +inline void +ieee80211_rx_assoc_rq(struct ieee80211_device *ieee, struct sk_buff *skb) +{ + + u8 dest[ETH_ALEN]; + //unsigned long flags; + + ieee->softmac_stats.rx_ass_rq++; + if (assoc_rq_parse(skb,dest) != -1){ + ieee80211_resp_to_assoc_rq(ieee, dest); + } + + printk(KERN_INFO"New client associated: "MAC_FMT"\n", MAC_ARG(dest)); + //FIXME + #if 0 + spin_lock_irqsave(&ieee->lock,flags); + add_associate(ieee,dest); + spin_unlock_irqrestore(&ieee->lock,flags); + #endif +} + + + +void ieee80211_sta_ps_send_null_frame(struct ieee80211_device *ieee, short pwr) +{ + + struct sk_buff *buf = ieee80211_null_func(ieee, pwr); + + printk(KERN_ALERT "ieee80211_sta_ps_send_null_frame \n"); + if (buf) + softmac_ps_mgmt_xmit(buf, ieee); + +} + + +short ieee80211_sta_ps_sleep(struct ieee80211_device *ieee, u32 *time_h, u32 *time_l) +{ + int timeout = ieee->ps_timeout; + u8 dtim; + /*if(ieee->ps == IEEE80211_PS_DISABLED || + ieee->iw_mode != IW_MODE_INFRA || + ieee->state != IEEE80211_LINKED) + + return 0; + */ + dtim = ieee->current_network.dtim_data; + //printk("DTIM\n"); + if(!(dtim & IEEE80211_DTIM_VALID)) + return 0; + //printk("VALID\n"); + ieee->current_network.dtim_data = IEEE80211_DTIM_INVALID; + + if(dtim & ((IEEE80211_DTIM_UCAST | IEEE80211_DTIM_MBCAST)& ieee->ps)) + return 2; + + if(!time_after(jiffies, ieee->dev->trans_start + MSECS(timeout))) + return 0; + + if(!time_after(jiffies, ieee->last_rx_ps_time + MSECS(timeout))) + return 0; + + if((ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE ) && + (ieee->mgmt_queue_tail != ieee->mgmt_queue_head)) + return 0; + + if(time_l){ + *time_l = ieee->current_network.last_dtim_sta_time[0] + + (ieee->current_network.beacon_interval + * ieee->current_network.dtim_period) * 1000; + } + + if(time_h){ + *time_h = ieee->current_network.last_dtim_sta_time[1]; + if(time_l && *time_l < ieee->current_network.last_dtim_sta_time[0]) + *time_h += 1; + } + + return 1; + + +} + +inline void ieee80211_sta_ps(struct ieee80211_device *ieee) +{ + + u32 th,tl; + short sleep; + + unsigned long flags,flags2; + + spin_lock_irqsave(&ieee->lock, flags); + + if((ieee->ps == IEEE80211_PS_DISABLED || + ieee->iw_mode != IW_MODE_INFRA || + ieee->state != IEEE80211_LINKED)){ + + // #warning CHECK_LOCK_HERE + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + + ieee80211_sta_wakeup(ieee, 1); + + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + + sleep = ieee80211_sta_ps_sleep(ieee,&th, &tl); + /* 2 wake, 1 sleep, 0 do nothing */ + if(sleep == 0) + goto out; + + if(sleep == 1){ + + if(ieee->sta_sleep == 1) + ieee->enter_sleep_state(ieee->dev,th,tl); + + else if(ieee->sta_sleep == 0){ + // printk("send null 1\n"); + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + + if(ieee->ps_is_queue_empty(ieee->dev)){ + + + ieee->sta_sleep = 2; + + ieee->ps_request_tx_ack(ieee->dev); + + ieee80211_sta_ps_send_null_frame(ieee,1); + + ieee->ps_th = th; + ieee->ps_tl = tl; + } + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + + } + + + }else if(sleep == 2){ +//#warning CHECK_LOCK_HERE + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + + ieee80211_sta_wakeup(ieee,1); + + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + +out: + spin_unlock_irqrestore(&ieee->lock, flags); + +} + +void ieee80211_sta_wakeup(struct ieee80211_device *ieee, short nl) +{ + if(ieee->sta_sleep == 0){ + if(nl){ + printk("Warning: driver is probably failing to report TX ps error\n"); + ieee->ps_request_tx_ack(ieee->dev); + ieee80211_sta_ps_send_null_frame(ieee, 0); + } + return; + + } + + if(ieee->sta_sleep == 1) + ieee->sta_wake_up(ieee->dev); + + ieee->sta_sleep = 0; + + if(nl){ + ieee->ps_request_tx_ack(ieee->dev); + ieee80211_sta_ps_send_null_frame(ieee, 0); + } +} + +void ieee80211_ps_tx_ack(struct ieee80211_device *ieee, short success) +{ + unsigned long flags,flags2; + + spin_lock_irqsave(&ieee->lock, flags); + + if(ieee->sta_sleep == 2){ + /* Null frame with PS bit set */ + if(success){ + ieee->sta_sleep = 1; + ieee->enter_sleep_state(ieee->dev,ieee->ps_th,ieee->ps_tl); + } + /* if the card report not success we can't be sure the AP + * has not RXed so we can't assume the AP believe us awake + */ + } + /* 21112005 - tx again null without PS bit if lost */ + else { + + if((ieee->sta_sleep == 0) && !success){ + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + ieee80211_sta_ps_send_null_frame(ieee, 0); + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + } + spin_unlock_irqrestore(&ieee->lock, flags); +} + +inline int +ieee80211_rx_frame_softmac(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats, u16 type, + u16 stype) +{ + struct ieee80211_hdr_3addr *header = (struct ieee80211_hdr_3addr *) skb->data; + u16 errcode; + u8* challenge=NULL; + int chlen=0; + int aid=0; + struct ieee80211_assoc_response_frame *assoc_resp; + struct ieee80211_info_element *info_element; + + if(!ieee->proto_started) + return 0; + + if(ieee->sta_sleep || (ieee->ps != IEEE80211_PS_DISABLED && + ieee->iw_mode == IW_MODE_INFRA && + ieee->state == IEEE80211_LINKED)) + + tasklet_schedule(&ieee->ps_task); + + if(WLAN_FC_GET_STYPE(header->frame_ctl) != IEEE80211_STYPE_PROBE_RESP && + WLAN_FC_GET_STYPE(header->frame_ctl) != IEEE80211_STYPE_BEACON) + ieee->last_rx_ps_time = jiffies; + + switch (WLAN_FC_GET_STYPE(header->frame_ctl)) { + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + + IEEE80211_DEBUG_MGMT("received [RE]ASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + //printk(KERN_WARNING "Received association response\n"); + if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) && + ieee->state == IEEE80211_ASSOCIATING_AUTHENTICATED && + ieee->iw_mode == IW_MODE_INFRA){ + if (0 == (errcode=assoc_parse(skb, &aid))){ + u16 left; + + ieee->state=IEEE80211_LINKED; + ieee->assoc_id = aid; + ieee->softmac_stats.rx_ass_ok++; + + //printk(KERN_WARNING "nic_type = %s", (rx_stats->nic_type == 1)?"rtl8187":"rtl8187B"); + if(1 == rx_stats->nic_type) //card type is 8187 + { + goto associate_complete; + } + assoc_resp = (struct ieee80211_assoc_response_frame*)skb->data; + info_element = &assoc_resp->info_element; + left = skb->len - ((void*)info_element - (void*)assoc_resp); + + while (left >= sizeof(struct ieee80211_info_element_hdr)) { + if (sizeof(struct ieee80211_info_element_hdr) + info_element->len > left) { + printk(KERN_WARNING "[re]associate reeponse error!"); + return 1; + } + switch (info_element->id) { + case MFIE_TYPE_GENERIC: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n", info_element->len); + if (info_element->len >= 8 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x02 && + info_element->data[4] == 0x01) { + // Not care about version at present. + //WMM Parameter Element + memcpy(ieee->current_network.wmm_param,(u8*)(info_element->data\ + + 8),(info_element->len - 8)); + + if (((ieee->current_network.wmm_info^info_element->data[6])& \ + 0x0f)||(!ieee->init_wmmparam_flag)) { + //refresh paramete element for current network + // update the register parameter for hardware + ieee->init_wmmparam_flag = 1; + //ieee->wmm_param_update(ieee); + //schedule_work(&ieee->wmm_param_update_wq); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->wmm_param_update_wq); +#else + schedule_task(&ieee->wmm_param_update_wq); +#endif + + } + //update info_element for current network + ieee->current_network.wmm_info = info_element->data[6]; + } + break; + default: + //nothing to do at present!!! + break; + } + + left -= sizeof(struct ieee80211_info_element_hdr) + + info_element->len; + info_element = (struct ieee80211_info_element *) + &info_element->data[info_element->len]; + } + if(!ieee->init_wmmparam_flag) //legacy AP, reset the AC_xx_param register + { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq,&ieee->wmm_param_update_wq); +#else + schedule_task(&ieee->wmm_param_update_wq); +#endif + ieee->init_wmmparam_flag = 1;//indicate AC_xx_param upated since last associate + } +associate_complete: + ieee80211_associate_complete(ieee); + }else{ + ieee->softmac_stats.rx_ass_err++; + IEEE80211_DEBUG_MGMT( + "Association response status code 0x%x\n", + errcode); + printk(KERN_WARNING "Association response status code 0x%x\n", + errcode); + ieee80211_associate_abort(ieee); + } + } +#ifdef _RTL8187_EXT_PATCH_ + else if ((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp) + { + ieee->ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp(ieee, skb); + } +#endif + break; + + case IEEE80211_STYPE_ASSOC_REQ: + case IEEE80211_STYPE_REASSOC_REQ: + //printk("Received IEEE80211_STYPE_ASSOC_REQ\n"); + + if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) && + ieee->iw_mode == IW_MODE_MASTER) + + ieee80211_rx_assoc_rq(ieee, skb); +#ifdef _RTL8187_EXT_PATCH_ + else if ((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_frame_softmac_on_assoc_req) + { + ieee->ext_patch_ieee80211_rx_frame_softmac_on_assoc_req(ieee, skb); + } +#endif + break; + + case IEEE80211_STYPE_AUTH: + //printk("Received authentication response\n"); + +#ifdef _RTL8187_EXT_PATCH_ +//printk("IEEE80211_STYPE_AUTH\n"); + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_frame_softmac_on_auth) + if( ieee->ext_patch_ieee80211_rx_frame_softmac_on_auth(ieee, skb, rx_stats) ); +#endif + if (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE){ + if (ieee->state == IEEE80211_ASSOCIATING_AUTHENTICATING && + ieee->iw_mode == IW_MODE_INFRA){ + + IEEE80211_DEBUG_MGMT("Received authentication response"); + + if (0 == (errcode=auth_parse(skb, &challenge, &chlen))){ + if(ieee->open_wep || !challenge){ + ieee->state = IEEE80211_ASSOCIATING_AUTHENTICATED; + ieee->softmac_stats.rx_auth_rs_ok++; + + ieee80211_associate_step2(ieee); + }else{ + ieee80211_auth_challenge(ieee, challenge, chlen); + } + }else{ + ieee->softmac_stats.rx_auth_rs_err++; + IEEE80211_DEBUG_MGMT("Authentication respose status code 0x%x",errcode); + ieee80211_associate_abort(ieee); + } + + }else if (ieee->iw_mode == IW_MODE_MASTER){ + ieee80211_rx_auth_rq(ieee, skb); + } + } + break; + + case IEEE80211_STYPE_PROBE_REQ: + //printk("Received IEEE80211_STYPE_PROBE_REQ\n"); + + if ((ieee->softmac_features & IEEE_SOFTMAC_PROBERS) && + ((ieee->iw_mode == IW_MODE_ADHOC || + ieee->iw_mode == IW_MODE_MASTER) && + ieee->state == IEEE80211_LINKED)) + + ieee80211_rx_probe_rq(ieee, skb); + break; + + case IEEE80211_STYPE_DISASSOC: + case IEEE80211_STYPE_DEAUTH: + //printk("Received IEEE80211_STYPE_DISASSOC\n"); +#ifdef _RTL8187_EXT_PATCH_ +//printk("IEEE80211_STYPE_DEAUTH\n"); + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_rx_frame_softmac_on_deauth) + if( ieee->ext_patch_ieee80211_rx_frame_softmac_on_deauth(ieee, skb, rx_stats) ) ; +#endif + /* FIXME for now repeat all the association procedure + * both for disassociation and deauthentication + */ + if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) && + ieee->state == IEEE80211_LINKED && + ieee->iw_mode == IW_MODE_INFRA){ + + ieee->state = IEEE80211_ASSOCIATING; + ieee->softmac_stats.reassoc++; + + notify_wx_assoc_event(ieee); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->associate_procedure_wq); +#else + schedule_task(&ieee->associate_procedure_wq); +#endif + } + + break; + + default: + return -1; + break; + } + + //dev_kfree_skb_any(skb); + return 0; +} + + + +/* following are for a simplier TX queue management. + * Instead of using netif_[stop/wake]_queue the driver + * will uses these two function (plus a reset one), that + * will internally uses the kernel netif_* and takes + * care of the ieee802.11 fragmentation. + * So the driver receives a fragment per time and might + * call the stop function when it want without take care + * to have enought room to TX an entire packet. + * This might be useful if each fragment need it's own + * descriptor, thus just keep a total free memory > than + * the max fragmentation treshold is not enought.. If the + * ieee802.11 stack passed a TXB struct then you needed + * to keep N free descriptors where + * N = MAX_PACKET_SIZE / MIN_FRAG_TRESHOLD + * In this way you need just one and the 802.11 stack + * will take care of buffering fragments and pass them to + * to the driver later, when it wakes the queue. + */ + +void ieee80211_softmac_xmit(struct ieee80211_txb *txb, struct ieee80211_device *ieee) +{ + + + unsigned long flags; + int i; +#ifdef _RTL8187_EXT_PATCH_ + int rate = ieee->rate; +#endif + + spin_lock_irqsave(&ieee->lock,flags); + #if 0 + if(ieee->queue_stop){ + IEEE80211DMESG("EE: IEEE hard_start_xmit invoked when kernel queue should be stopped"); + netif_stop_queue(ieee->dev); + ieee->ieee_stats.swtxstop++; + //dev_kfree_skb_any(skb); + err = 1; + goto exit; + } + + ieee->stats.tx_bytes+=skb->len; + + + txb=ieee80211_skb_to_txb(ieee,skb); + + + if(txb==NULL){ + IEEE80211DMESG("WW: IEEE stack failed to provide txb"); + //dev_kfree_skb_any(skb); + err = 1; + goto exit; + } + #endif + +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_softmac_xmit_get_rate && txb->nr_frags) + { + rate = ieee->ext_patch_ieee80211_softmac_xmit_get_rate(ieee, txb->fragments[0]); + } +#endif + /* called with 2nd parm 0, no tx mgmt lock required */ + ieee80211_sta_wakeup(ieee,0); + + for(i = 0; i < txb->nr_frags; i++) { + + if (ieee->queue_stop){ + ieee->tx_pending.txb = txb; + ieee->tx_pending.frag = i; + goto exit; + }else{ + ieee->softmac_data_hard_start_xmit( + txb->fragments[i], +#ifdef _RTL8187_EXT_PATCH_ + ieee->dev, rate); +#else + ieee->dev,ieee->rate); +#endif + //(i+1)nr_frags); + ieee->stats.tx_packets++; + ieee->stats.tx_bytes += txb->fragments[i]->len; + ieee->dev->trans_start = jiffies; + } + } + + ieee80211_txb_free(txb); + + exit: + spin_unlock_irqrestore(&ieee->lock,flags); + +} + +/* called with ieee->lock acquired */ +void ieee80211_resume_tx(struct ieee80211_device *ieee) +{ + int i; + for(i = ieee->tx_pending.frag; i < ieee->tx_pending.txb->nr_frags; i++) { + + if (ieee->queue_stop){ + ieee->tx_pending.frag = i; + return; + }else{ + + ieee->softmac_data_hard_start_xmit( + ieee->tx_pending.txb->fragments[i], + ieee->dev,ieee->rate); + //(i+1)tx_pending.txb->nr_frags); + ieee->stats.tx_packets++; + ieee->dev->trans_start = jiffies; + } + } + + + ieee80211_txb_free(ieee->tx_pending.txb); + ieee->tx_pending.txb = NULL; +} + + +void ieee80211_reset_queue(struct ieee80211_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->lock,flags); + init_mgmt_queue(ieee); + if (ieee->tx_pending.txb){ + ieee80211_txb_free(ieee->tx_pending.txb); + ieee->tx_pending.txb = NULL; + } + ieee->queue_stop = 0; + spin_unlock_irqrestore(&ieee->lock,flags); + +} + +void ieee80211_wake_queue(struct ieee80211_device *ieee) +{ + + unsigned long flags; + struct sk_buff *skb; + struct ieee80211_hdr_3addr *header; + + spin_lock_irqsave(&ieee->lock,flags); + if (! ieee->queue_stop) goto exit; + + ieee->queue_stop = 0; + + if(ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE){ + while (!ieee->queue_stop && (skb = dequeue_mgmt(ieee))){ + + header = (struct ieee80211_hdr_3addr *) skb->data; + + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + printk(KERN_ALERT "ieee80211_wake_queue \n"); + ieee->softmac_data_hard_start_xmit(skb,ieee->dev,ieee->basic_rate); + dev_kfree_skb_any(skb);//edit by thomas + } + } + if (!ieee->queue_stop && ieee->tx_pending.txb) + ieee80211_resume_tx(ieee); + + if (!ieee->queue_stop && netif_queue_stopped(ieee->dev)){ + ieee->softmac_stats.swtxawake++; + netif_wake_queue(ieee->dev); + } + +exit : + spin_unlock_irqrestore(&ieee->lock,flags); +} + + +void ieee80211_stop_queue(struct ieee80211_device *ieee) +{ + //unsigned long flags; + //spin_lock_irqsave(&ieee->lock,flags); + + if (! netif_queue_stopped(ieee->dev)){ + netif_stop_queue(ieee->dev); + ieee->softmac_stats.swtxstop++; + } + ieee->queue_stop = 1; + //spin_unlock_irqrestore(&ieee->lock,flags); + +} + + +inline void ieee80211_randomize_cell(struct ieee80211_device *ieee) +{ + + get_random_bytes(ieee->current_network.bssid, ETH_ALEN); + + /* an IBSS cell address must have the two less significant + * bits of the first byte = 2 + */ + ieee->current_network.bssid[0] &= ~0x01; + ieee->current_network.bssid[0] |= 0x02; +} + +/* called in user context only */ +void ieee80211_start_master_bss(struct ieee80211_device *ieee) +{ + ieee->assoc_id = 1; + + if (ieee->current_network.ssid_len == 0){ + strncpy(ieee->current_network.ssid, + IEEE80211_DEFAULT_TX_ESSID, + IW_ESSID_MAX_SIZE); + + ieee->current_network.ssid_len = strlen(IEEE80211_DEFAULT_TX_ESSID); + ieee->ssid_set = 1; + } + + memcpy(ieee->current_network.bssid, ieee->dev->dev_addr, ETH_ALEN); + + ieee->set_chan(ieee->dev, ieee->current_network.channel); + ieee->state = IEEE80211_LINKED; + +//by lizhaoming for LED LINK +#ifdef LED_SHIN + { + struct net_device *dev = ieee->dev; + ieee->ieee80211_led_contorl(dev, LED_CTL_LINK); + } +#endif + ieee->link_change(ieee->dev); + notify_wx_assoc_event(ieee); + + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + + netif_carrier_on(ieee->dev); +} + +void ieee80211_start_monitor_mode(struct ieee80211_device *ieee) +{ + if(ieee->raw_tx){ + + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + + netif_carrier_on(ieee->dev); + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void ieee80211_start_ibss_wq(struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work, struct delayed_work, work); + struct ieee80211_device *ieee = container_of(dwork, struct ieee80211_device, start_ibss_wq); +#else +void ieee80211_start_ibss_wq(struct ieee80211_device *ieee) +{ +#endif + + /* iwconfig mode ad-hoc will schedule this and return + * on the other hand this will block further iwconfig SET + * operations because of the wx_sem hold. + * Anyway some most set operations set a flag to speed-up + * (abort) this wq (when syncro scanning) before sleeping + * on the semaphore + */ + + down(&ieee->wx_sem); + + if (ieee->current_network.ssid_len == 0){ + strcpy(ieee->current_network.ssid,IEEE80211_DEFAULT_TX_ESSID); + ieee->current_network.ssid_len = strlen(IEEE80211_DEFAULT_TX_ESSID); + ieee->ssid_set = 1; + } + +//by lizhaoming for LED BLINK 2008.6.23 +#ifdef LED_SHIN + { + struct net_device *dev = ieee->dev; + ieee->ieee80211_led_contorl(dev, LED_CTL_SITE_SURVEY); + } +#endif + + /* check if we have this cell in our network list */ + ieee80211_softmac_check_all_nets(ieee); + +#ifdef ENABLE_DOT11D + //[World wide 13]: + // Adhoc: + // (1) active scan from ch1~11 and passive scan from ch12~13 + // (2) IBSS can join ch1~13 adhoc, but only start at ch10. + if(ieee->state == IEEE80211_NOLINK) + if(ieee->IbssStartChnl != 0) + ieee->current_network.channel = ieee->IbssStartChnl;//chan 10 +#endif + + /* if not then the state is not linked. Maybe the user swithced to + * ad-hoc mode just after being in monitor mode, or just after + * being very few time in managed mode (so the card have had no + * time to scan all the chans..) or we have just run up the iface + * after setting ad-hoc mode. So we have to give another try.. + * Here, in ibss mode, should be safe to do this without extra care + * (in bss mode we had to make sure no-one tryed to associate when + * we had just checked the ieee->state and we was going to start the + * scan) beacause in ibss mode the ieee80211_new_net function, when + * finds a good net, just set the ieee->state to IEEE80211_LINKED, + * so, at worst, we waste a bit of time to initiate an unneeded syncro + * scan, that will stop at the first round because it sees the state + * associated. + */ + if (ieee->state == IEEE80211_NOLINK){ + ieee80211_start_scan_syncro(ieee); + } + + /* the network definitively is not here.. create a new cell */ + if (ieee->state == IEEE80211_NOLINK){ + printk("creating new IBSS cell\n"); + ieee->state = IEEE80211_LINKED; + if(!ieee->wap_set) + ieee80211_randomize_cell(ieee); + + if(ieee->modulation & IEEE80211_CCK_MODULATION){ + + ieee->current_network.rates_len = 4; + + ieee->current_network.rates[0] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; + ieee->current_network.rates[1] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; + ieee->current_network.rates[2] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; + ieee->current_network.rates[3] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + + }else + ieee->current_network.rates_len = 0; + + if(ieee->modulation & IEEE80211_OFDM_MODULATION){ + ieee->current_network.rates_ex_len = 8; + + ieee->current_network.rates_ex[0] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB; + ieee->current_network.rates_ex[1] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB; + ieee->current_network.rates_ex[2] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB; + ieee->current_network.rates_ex[3] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB; + ieee->current_network.rates_ex[4] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; + ieee->current_network.rates_ex[5] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB; + ieee->current_network.rates_ex[6] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB; + ieee->current_network.rates_ex[7] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB; + + ieee->rate = 540; + }else{ + ieee->current_network.rates_ex_len = 0; + ieee->rate = 110; + } + + // By default, WMM function will be disabled in IBSS mode + ieee->current_network.QoS_Enable = 0; + + ieee->current_network.atim_window = 0; + ieee->current_network.capability = WLAN_CAPABILITY_IBSS; + if(ieee->short_slot) + ieee->current_network.capability |= WLAN_CAPABILITY_SHORT_SLOT; + + } + + ieee->state = IEEE80211_LINKED; + +//by lizhaoming for LED LINK +#ifdef LED_SHIN + { + struct net_device *dev = ieee->dev; + ieee->ieee80211_led_contorl(dev, LED_CTL_LINK); + } +#endif + + ieee->set_chan(ieee->dev, ieee->current_network.channel); + ieee->link_change(ieee->dev); + + notify_wx_assoc_event(ieee); + + ieee80211_start_send_beacons(ieee); + printk(KERN_WARNING "after sending beacon packet!\n"); + + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + + netif_carrier_on(ieee->dev); + + up(&ieee->wx_sem); +} + +inline void ieee80211_start_ibss(struct ieee80211_device *ieee) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_delayed_work(ieee->wq, &ieee->start_ibss_wq, 150); //change to delayed work, delayed time is need to check +#else + schedule_task(&ieee->start_ibss_wq); +#endif +} + +/* this is called only in user context, with wx_sem held */ +void ieee80211_start_bss(struct ieee80211_device *ieee) +{ + unsigned long flags; + /* check if we have already found the net we + * are interested in (if any). + * if not (we are disassociated and we are not + * in associating / authenticating phase) start the background scanning. + */ + +//by lizhaoming for LED BLINK 2008.6.23 +#ifdef LED_SHIN + { + struct net_device *dev = ieee->dev; + ieee->ieee80211_led_contorl(dev, LED_CTL_SITE_SURVEY); + } +#endif + +#ifdef ENABLE_DOT11D + // + // Ref: 802.11d 11.1.3.3 + // STA shall not start a BSS unless properly formed Beacon frame including a Country IE. + // + if(IS_DOT11D_ENABLE(ieee) && !IS_COUNTRY_IE_VALID(ieee)) + { + if(! ieee->bGlobalDomain) + { + return; + } + } +#endif + //printk("======>%s()\n",__FUNCTION__); + ieee80211_softmac_check_all_nets(ieee); + + /* ensure no-one start an associating process (thus setting + * the ieee->state to ieee80211_ASSOCIATING) while we + * have just cheked it and we are going to enable scan. + * The ieee80211_new_net function is always called with + * lock held (from both ieee80211_softmac_check_all_nets and + * the rx path), so we cannot be in the middle of such function + */ + + spin_lock_irqsave(&ieee->lock, flags); + if (ieee->state == IEEE80211_NOLINK){ + //printk("Not find SSID in network list scan now\n"); + ieee80211_start_scan(ieee); + } + spin_unlock_irqrestore(&ieee->lock, flags); + +} + +/* called only in userspace context */ +void ieee80211_disassociate(struct ieee80211_device *ieee) +{ + netif_carrier_off(ieee->dev); + + if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE) + ieee80211_reset_queue(ieee); + + if (ieee->data_hard_stop) + ieee->data_hard_stop(ieee->dev); + +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(ieee)) + Dot11d_Reset(ieee); +#endif + + ieee->state = IEEE80211_NOLINK; + ieee->link_change(ieee->dev); + notify_wx_assoc_event(ieee); + +} +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void ieee80211_associate_retry_wq(struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work, struct delayed_work, work); + struct ieee80211_device *ieee = container_of(dwork, struct ieee80211_device, associate_retry_wq); +#else +void ieee80211_associate_retry_wq(struct ieee80211_device *ieee) +{ +#endif + unsigned long flags; + + down(&ieee->wx_sem); + if(!ieee->proto_started) + goto exit; + + if(ieee->state != IEEE80211_ASSOCIATING_RETRY) + goto exit; + + /* until we do not set the state to IEEE80211_NOLINK + * there are no possibility to have someone else trying + * to start an association procdure (we get here with + * ieee->state = IEEE80211_ASSOCIATING). + * When we set the state to IEEE80211_NOLINK it is possible + * that the RX path run an attempt to associate, but + * both ieee80211_softmac_check_all_nets and the + * RX path works with ieee->lock held so there are no + * problems. If we are still disassociated then start a scan. + * the lock here is necessary to ensure no one try to start + * an association procedure when we have just checked the + * state and we are going to start the scan. + */ + ieee->state = IEEE80211_NOLINK; + + ieee80211_softmac_check_all_nets(ieee); + + spin_lock_irqsave(&ieee->lock, flags); + if(ieee->state == IEEE80211_NOLINK) + { + printk("%s():Not find SSID:%s[ch=%d, mode=%s] in network list scan now\n", __FUNCTION__, + ieee->current_network.ssid,ieee->current_network.channel, + (ieee->iw_mode == IW_MODE_INFRA) ? "BSS" : "IBSS"); + + ieee80211_start_scan(ieee); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + +exit: + up(&ieee->wx_sem); +} + +struct sk_buff *ieee80211_get_beacon_(struct ieee80211_device *ieee) +{ + u8 broadcast_addr[] = {0xff,0xff,0xff,0xff,0xff,0xff}; + + struct sk_buff *skb = NULL; + struct ieee80211_probe_response *b; + +//rz +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_get_beacon_get_probersp ) + skb = ieee->ext_patch_get_beacon_get_probersp(ieee, broadcast_addr, &(ieee->current_network)); + else + skb = ieee80211_probe_resp(ieee, broadcast_addr); +#else + skb = ieee80211_probe_resp(ieee, broadcast_addr); +#endif +// + if (!skb) + return NULL; + + b = (struct ieee80211_probe_response *) skb->data; + b->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_BEACON); + + return skb; + +} + +struct sk_buff *ieee80211_get_beacon(struct ieee80211_device *ieee) +{ + struct sk_buff *skb; + struct ieee80211_probe_response *b; +// printk("=========>%s()\n", __FUNCTION__); + skb = ieee80211_get_beacon_(ieee); + if(!skb) + return NULL; + + b = (struct ieee80211_probe_response *) skb->data; + b->header.seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + return skb; +} + +void ieee80211_softmac_stop_protocol(struct ieee80211_device *ieee) +{ + ieee->sync_scan_hurryup = 1; + down(&ieee->wx_sem); + ieee80211_stop_protocol(ieee); + up(&ieee->wx_sem); +} + + +void ieee80211_stop_protocol(struct ieee80211_device *ieee) +{ + if (!ieee->proto_started) + return; + + ieee->proto_started = 0; + //printk("=====>%s\n", __func__); + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->ext_patch_ieee80211_stop_protocol) + ieee->ext_patch_ieee80211_stop_protocol(ieee); +//if call queue_delayed_work,can call this,or do nothing.. +//edit by lawrence,20071118 +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +// cancel_delayed_work(&ieee->ext_stop_scan_wq); +// cancel_delayed_work(&ieee->ext_send_beacon_wq); +#endif +#endif // _RTL8187_EXT_PATCH_ + + ieee80211_stop_send_beacons(ieee); + + del_timer_sync(&ieee->associate_timer); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + cancel_delayed_work(&ieee->associate_retry_wq); + cancel_delayed_work(&ieee->start_ibss_wq); //cancel ibss start workqueue when stop protocol +#endif + ieee80211_stop_scan(ieee); + + ieee80211_disassociate(ieee); + //printk("<=====%s\n", __func__); + +} + +void ieee80211_softmac_start_protocol(struct ieee80211_device *ieee) +{ + ieee->sync_scan_hurryup = 0; + down(&ieee->wx_sem); + ieee80211_start_protocol(ieee); + up(&ieee->wx_sem); +} + +void ieee80211_start_protocol(struct ieee80211_device *ieee) +{ + short ch = 0; + int i = 0; + + if (ieee->proto_started) + return; + + //printk("=====>%s\n", __func__); + + ieee->proto_started = 1; + + if (ieee->current_network.channel == 0){ + do{ + ch++; + if (ch > MAX_CHANNEL_NUMBER) + return; /* no channel found */ +#ifdef ENABLE_DOT11D + }while(!GET_DOT11D_INFO(ieee)->channel_map[ch]); +#else + }while(!ieee->channel_map[ch]); +#endif + ieee->current_network.channel = ch; + } + + if (ieee->current_network.beacon_interval == 0) + ieee->current_network.beacon_interval = 100; + + ieee->set_chan(ieee->dev,ieee->current_network.channel); + mdelay(10);//must or link change will fail lzm + + for(i = 0; i < 17; i++) { + ieee->last_rxseq_num[i] = -1; + ieee->last_rxfrag_num[i] = -1; + ieee->last_packet_time[i] = 0; + } + + ieee->init_wmmparam_flag = 0;//reinitialize AC_xx_PARAM registers. + + + /* if the user set the MAC of the ad-hoc cell and then + * switch to managed mode, shall we make sure that association + * attempts does not fail just because the user provide the essid + * and the nic is still checking for the AP MAC ?? + */ + + if (ieee->iw_mode == IW_MODE_INFRA){ + ieee80211_start_bss(ieee); + // printk("==========> IW_MODE_INFRA\n"); + } + else if (ieee->iw_mode == IW_MODE_ADHOC){ + // printk("==========> IW_MODE_ADHOC\n"); + ieee80211_start_ibss(ieee); + } + else if (ieee->iw_mode == IW_MODE_MASTER){ + ieee80211_start_master_bss(ieee); +// printk("==========> IW_MODE_MASTER\n"); + } + else if(ieee->iw_mode == IW_MODE_MONITOR){ + ieee80211_start_monitor_mode(ieee); +// printk("==========> IW_MODE_MONITOR\n"); + } + +#ifdef _RTL8187_EXT_PATCH_ +// else if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_start_protocol && ieee->ext_patch_ieee80211_start_protocol(ieee)) + else if((ieee->iw_mode == ieee->iw_ext_mode) && ieee->ext_patch_ieee80211_start_protocol) + { + ieee->ext_patch_ieee80211_start_mesh(ieee); + } +#endif +} + + +#define DRV_NAME "Ieee80211" +void ieee80211_softmac_init(struct ieee80211_device *ieee) +{ + int i; + memset(&ieee->current_network, 0, sizeof(struct ieee80211_network)); + + ieee->state = IEEE80211_NOLINK; + ieee->sync_scan_hurryup = 0; + for(i = 0; i < 5; i++) { + ieee->seq_ctrl[i] = 0; + } + + ieee->assoc_id = 0; + ieee->queue_stop = 0; + ieee->scanning = 0; + ieee->scan_watchdog = 0;//lzm add 081215 for roaming + ieee->softmac_features = 0; //so IEEE2100-like driver are happy + ieee->wap_set = 0; + ieee->ssid_set = 0; + ieee->proto_started = 0; + ieee->basic_rate = IEEE80211_DEFAULT_BASIC_RATE; + ieee->rate = 3; + ieee->ps = IEEE80211_PS_DISABLED; + ieee->sta_sleep = 0; +//by amy + ieee->bInactivePs = false; + ieee->actscanning = false; + ieee->ListenInterval = 2; + ieee->NumRxData = 0; + ieee->NumRxDataInPeriod = 0; //YJ,add,080828 + ieee->NumRxBcnInPeriod = 0; //YJ,add,080828 + ieee->bHwRadioOff = false;//by lizhaoming +//by amy +#ifdef _RTL8187_EXT_PATCH_ + ieee->iw_ext_mode = 999; +#endif + + init_mgmt_queue(ieee); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + init_timer(&ieee->scan_timer); + ieee->scan_timer.data = (unsigned long)ieee; + ieee->scan_timer.function = ieee80211_softmac_scan_cb; +#endif + ieee->tx_pending.txb = NULL; + + init_timer(&ieee->associate_timer); + ieee->associate_timer.data = (unsigned long)ieee; + ieee->associate_timer.function = ieee80211_associate_abort_cb; + + init_timer(&ieee->beacon_timer); + ieee->beacon_timer.data = (unsigned long) ieee; + ieee->beacon_timer.function = ieee80211_send_beacon_cb; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +#ifdef PF_SYNCTHREAD + ieee->wq = create_workqueue(DRV_NAME,0); +#else + ieee->wq = create_workqueue(DRV_NAME); +#endif +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)//added by lawrence,070702 + INIT_DELAYED_WORK(&ieee->start_ibss_wq, ieee80211_start_ibss_wq); + INIT_WORK(&ieee->associate_complete_wq, ieee80211_associate_complete_wq); + INIT_WORK(&ieee->associate_procedure_wq, ieee80211_associate_procedure_wq); + INIT_DELAYED_WORK(&ieee->softmac_scan_wq, ieee80211_softmac_scan_wq); + INIT_DELAYED_WORK(&ieee->associate_retry_wq, ieee80211_associate_retry_wq); + INIT_WORK(&ieee->wx_sync_scan_wq, ieee80211_wx_sync_scan_wq); +//added by lawrence,20071118 +#ifdef _RTL8187_EXT_PATCH_ + INIT_WORK(&ieee->ext_stop_scan_wq, ieee80211_ext_stop_scan_wq); + //INIT_WORK(&ieee->ext_send_beacon_wq, ieee80211_beacons_start,ieee); + INIT_WORK(&ieee->ext_send_beacon_wq, ext_ieee80211_send_beacon_wq); +#endif //_RTL8187_EXT_PATCH_ +#else + INIT_WORK(&ieee->start_ibss_wq,(void(*)(void*)) ieee80211_start_ibss_wq,ieee); + INIT_WORK(&ieee->associate_retry_wq,(void(*)(void*)) ieee80211_associate_retry_wq,ieee); + INIT_WORK(&ieee->associate_complete_wq,(void(*)(void*)) ieee80211_associate_complete_wq,ieee); + INIT_WORK(&ieee->associate_procedure_wq,(void(*)(void*)) ieee80211_associate_procedure_wq,ieee); + INIT_WORK(&ieee->softmac_scan_wq,(void(*)(void*)) ieee80211_softmac_scan_wq,ieee); + INIT_WORK(&ieee->wx_sync_scan_wq,(void(*)(void*)) ieee80211_wx_sync_scan_wq,ieee); +#ifdef _RTL8187_EXT_PATCH_ + INIT_WORK(&ieee->ext_stop_scan_wq,(void(*)(void*)) ieee80211_ext_stop_scan_wq,ieee); + //INIT_WORK(&ieee->ext_send_beacon_wq,(void(*)(void*)) ieee80211_beacons_start,ieee); + INIT_WORK(&ieee->ext_send_beacon_wq,(void(*)(void*)) ext_ieee80211_send_beacon_wq,ieee); +#endif +#endif +#else + tq_init(&ieee->start_ibss_wq,(void(*)(void*)) ieee80211_start_ibss_wq,ieee); + tq_init(&ieee->associate_retry_wq,(void(*)(void*)) ieee80211_associate_retry_wq,ieee); + tq_init(&ieee->associate_complete_wq,(void(*)(void*)) ieee80211_associate_complete_wq,ieee); + tq_init(&ieee->associate_procedure_wq,(void(*)(void*)) ieee80211_associate_procedure_wq,ieee); + tq_init(&ieee->softmac_scan_wq,(void(*)(void*)) ieee80211_softmac_scan_wq,ieee); + tq_init(&ieee->wx_sync_scan_wq,(void(*)(void*)) ieee80211_wx_sync_scan_wq,ieee); +#ifdef _RTL8187_EXT_PATCH_ + tq_init(&ieee->ext_stop_scan_wq,(void(*)(void*)) ieee80211_ext_stop_scan_wq,ieee); + //tq_init(&ieee->ext_send_beacon_wq,(void(*)(void*)) ieee80211_beacons_start,ieee); + tq_init(&ieee->ext_send_beacon_wq,(void(*)(void*)) ext_ieee80211_send_beacon_wq,ieee); +#endif +#endif + sema_init(&ieee->wx_sem, 1); + sema_init(&ieee->scan_sem, 1); + sema_init(&ieee->ips_sem,1); + spin_lock_init(&ieee->mgmt_tx_lock); + spin_lock_init(&ieee->beacon_lock); + spin_lock_init(&ieee->beaconflag_lock); + tasklet_init(&ieee->ps_task, + (void(*)(unsigned long)) ieee80211_sta_ps, + (unsigned long)ieee); +#ifdef ENABLE_DOT11D + ieee->pDot11dInfo = kmalloc(sizeof(RT_DOT11D_INFO), GFP_ATOMIC); +#endif + +} + +void ieee80211_softmac_free(struct ieee80211_device *ieee) +{ + down(&ieee->wx_sem); + + del_timer_sync(&ieee->associate_timer); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + cancel_delayed_work(&ieee->associate_retry_wq); + + + +#ifdef _RTL8187_EXT_PATCH_ + //When kernel>2.6.20,crash.... +// cancel_delayed_work(&ieee->ext_stop_scan_wq); +// cancel_delayed_work(&ieee->ext_send_beacon_wq); +#endif + destroy_workqueue(ieee->wq); +#endif + +#ifdef ENABLE_DOT11D + if(NULL != ieee->pDot11dInfo) + kfree(ieee->pDot11dInfo); +#endif + + up(&ieee->wx_sem); +} + +/******************************************************** + * Start of WPA code. * + * this is stolen from the ipw2200 driver * + ********************************************************/ + + +static int ieee80211_wpa_enable(struct ieee80211_device *ieee, int value) +{ + /* This is called when wpa_supplicant loads and closes the driver + * interface. */ + printk("%s WPA\n",value ? "enabling" : "disabling"); + ieee->wpa_enabled = value; + return 0; +} + + +void ieee80211_wpa_assoc_frame(struct ieee80211_device *ieee, char *wpa_ie, int wpa_ie_len) +{ + /* make sure WPA is enabled */ + ieee80211_wpa_enable(ieee, 1); + + ieee80211_disassociate(ieee); +} + + +static int ieee80211_wpa_mlme(struct ieee80211_device *ieee, int command, int reason) +{ + + int ret = 0; + + switch (command) { + case IEEE_MLME_STA_DEAUTH: + // silently ignore + break; + + case IEEE_MLME_STA_DISASSOC: + ieee80211_disassociate(ieee); + break; + + default: + printk("Unknown MLME request: %d\n", command); + ret = -EOPNOTSUPP; + } + + return ret; +} + + +static int ieee80211_wpa_set_wpa_ie(struct ieee80211_device *ieee, + struct ieee_param *param, int plen) +{ + u8 *buf; + + if (param->u.wpa_ie.len > MAX_WPA_IE_LEN || + (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL)) + return -EINVAL; + + if (param->u.wpa_ie.len) { + buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len); + kfree(ieee->wpa_ie); + ieee->wpa_ie = buf; + ieee->wpa_ie_len = param->u.wpa_ie.len; + } else { + kfree(ieee->wpa_ie); + ieee->wpa_ie = NULL; + ieee->wpa_ie_len = 0; + } + + ieee80211_wpa_assoc_frame(ieee, ieee->wpa_ie, ieee->wpa_ie_len); + return 0; +} + +#define AUTH_ALG_OPEN_SYSTEM 0x1 +#define AUTH_ALG_SHARED_KEY 0x2 + +static int ieee80211_wpa_set_auth_algs(struct ieee80211_device *ieee, int value) +{ + + struct ieee80211_security sec = { + .flags = SEC_AUTH_MODE, + }; + int ret = 0; + + if (value & AUTH_ALG_SHARED_KEY) { + sec.auth_mode = WLAN_AUTH_SHARED_KEY; + ieee->open_wep = 0; + } else { + sec.auth_mode = WLAN_AUTH_OPEN; + ieee->open_wep = 1; + } + + if (ieee->set_security) + ieee->set_security(ieee->dev, &sec); + else + ret = -EOPNOTSUPP; + + return ret; +} + +static int ieee80211_wpa_set_param(struct ieee80211_device *ieee, u8 name, u32 value) +{ + int ret=0; + unsigned long flags; + + switch (name) { + case IEEE_PARAM_WPA_ENABLED: + ret = ieee80211_wpa_enable(ieee, value); + break; + + case IEEE_PARAM_TKIP_COUNTERMEASURES: + ieee->tkip_countermeasures=value; + break; + + case IEEE_PARAM_DROP_UNENCRYPTED: { + /* HACK: + * + * wpa_supplicant calls set_wpa_enabled when the driver + * is loaded and unloaded, regardless of if WPA is being + * used. No other calls are made which can be used to + * determine if encryption will be used or not prior to + * association being expected. If encryption is not being + * used, drop_unencrypted is set to false, else true -- we + * can use this to determine if the CAP_PRIVACY_ON bit should + * be set. + */ + struct ieee80211_security sec = { + .flags = SEC_ENABLED, + .enabled = value, + }; + ieee->drop_unencrypted = value; + /* We only change SEC_LEVEL for open mode. Others + * are set by ipw_wpa_set_encryption. + */ + if (!value) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_0; + } + else { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } + if (ieee->set_security) + ieee->set_security(ieee->dev, &sec); + break; + } + + case IEEE_PARAM_PRIVACY_INVOKED: + ieee->privacy_invoked=value; + break; + + case IEEE_PARAM_AUTH_ALGS: + ret = ieee80211_wpa_set_auth_algs(ieee, value); + break; + + case IEEE_PARAM_IEEE_802_1X: + ieee->ieee802_1x=value; + break; + case IEEE_PARAM_WPAX_SELECT: + // added for WPA2 mixed mode + //printk(KERN_WARNING "------------------------>wpax value = %x\n", value); + spin_lock_irqsave(&ieee->wpax_suitlist_lock,flags); + ieee->wpax_type_set = 1; + ieee->wpax_type_notify = value; + spin_unlock_irqrestore(&ieee->wpax_suitlist_lock,flags); + break; + + default: + printk("Unknown WPA param: %d\n",name); + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* implementation borrowed from hostap driver */ + +static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, + struct ieee_param *param, int param_len) +{ + int ret = 0; + + struct ieee80211_crypto_ops *ops; + struct ieee80211_crypt_data **crypt; + + struct ieee80211_security sec = { + .flags = 0, + }; + + param->u.crypt.err = 0; + param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len != + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len) { + printk("Len mismatch %d, %d\n", param_len, + param->u.crypt.key_len); + return -EINVAL; + } + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + if (param->u.crypt.idx >= WEP_KEYS) + return -EINVAL; +#ifdef _RTL8187_EXT_PATCH_ + crypt = &ieee->cryptlist[0]->crypt[param->u.crypt.idx]; +#else + crypt = &ieee->crypt[param->u.crypt.idx]; +#endif + + } else { + return -EINVAL; + } + + if (strcmp(param->u.crypt.alg, "none") == 0) { + if (crypt) { + sec.enabled = 0; + // FIXME FIXME + //sec.encrypt = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL; + ieee80211_crypt_delayed_deinit(ieee, crypt); + } + goto done; + } + sec.enabled = 1; +// FIXME FIXME +// sec.encrypt = 1; + sec.flags |= SEC_ENABLED; + + /* IPW HW cannot build TKIP MIC, host decryption still needed. */ + if (!(ieee->host_encrypt || ieee->host_decrypt) && + strcmp(param->u.crypt.alg, "TKIP")) + goto skip_host_crypt; + + ops = ieee80211_get_crypto_ops(param->u.crypt.alg); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { + request_module("ieee80211_crypt_wep"); + ops = ieee80211_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { + request_module("ieee80211_crypt_tkip"); + ops = ieee80211_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { + request_module("ieee80211_crypt_ccmp"); + ops = ieee80211_get_crypto_ops(param->u.crypt.alg); + } + if (ops == NULL) { + printk("unknown crypto alg '%s'\n", param->u.crypt.alg); + param->u.crypt.err = IEEE_CRYPT_ERR_UNKNOWN_ALG; + ret = -EINVAL; + goto done; + } + +#ifdef _RTL8187_EXT_PATCH_ + u8 i; + for (i=0; icryptlist[i]->crypt[param->u.crypt.idx]; +// if (crypt != NULL) printk("crypt not null\n", crypt); + + *crypt = ieee->cryptlist[i]->crypt[param->u.crypt.idx]; +#endif + if (*crypt == NULL || (*crypt)->ops != ops) { + struct ieee80211_crypt_data *new_crypt; + + ieee80211_crypt_delayed_deinit(ieee, crypt); + + new_crypt = (struct ieee80211_crypt_data *) + kmalloc(sizeof(*new_crypt), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); + new_crypt->ops = ops; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) +#else + if (new_crypt->ops && try_inc_mod_count(new_crypt->ops->owner)) +#endif + new_crypt->priv = + new_crypt->ops->init(param->u.crypt.idx); + + if (new_crypt->priv == NULL) { + kfree(new_crypt); + param->u.crypt.err = IEEE_CRYPT_ERR_CRYPT_INIT_FAILED; + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(param->u.crypt.key, + param->u.crypt.key_len, param->u.crypt.seq, + (*crypt)->priv) < 0) { + printk("key setting failed\n"); + param->u.crypt.err = IEEE_CRYPT_ERR_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } +#ifdef _RTL8187_EXT_PATCH_ + } +#endif + skip_host_crypt: + if (param->u.crypt.set_tx) { + ieee->tx_keyidx = param->u.crypt.idx; + sec.active_key = param->u.crypt.idx; + sec.flags |= SEC_ACTIVE_KEY; + } else + sec.flags &= ~SEC_ACTIVE_KEY; + + if (param->u.crypt.alg != NULL) { + memcpy(sec.keys[param->u.crypt.idx], + param->u.crypt.key, + param->u.crypt.key_len); + sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len; + sec.flags |= (1 << param->u.crypt.idx); + + if (strcmp(param->u.crypt.alg, "WEP") == 0) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_2; + } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_3; + } + } + done: + if (ieee->set_security) + ieee->set_security(ieee->dev, &sec); +#if 1 +#ifdef _RTL8187_EXT_PATCH_ + if (ret != 0)//error out + { + for (i=0; icryptlist[i]->crypt[param->u.crypt.idx]==NULL){ + break; + } + else{ + //if (ieee->cryptlist[i]->crypt[param->u.crypt.idx] != NULL) + // { + kfree(ieee->cryptlist[i]->crypt[param->u.crypt.idx]); + ieee->cryptlist[i]->crypt[param->u.crypt.idx] = NULL; + // } + // kfree(ieee->cryptlist[i]); + // ieee->cryptlist[i] = NULL; + } + } + } +#endif +#endif + /* Do not reset port if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. If your hardware requires a reset after WEP + * configuration (for example... Prism2), implement the reset_port in + * the callbacks structures used to initialize the 802.11 stack. */ + if (ieee->reset_on_keychange && + ieee->iw_mode != IW_MODE_INFRA && + ieee->reset_port && + ieee->reset_port(ieee->dev)) { + printk("reset_port failed\n"); + param->u.crypt.err = IEEE_CRYPT_ERR_CARD_CONF_FAILED; + return -EINVAL; + } + + return ret; +} + +int ieee80211_wpa_supplicant_ioctl(struct ieee80211_device *ieee, struct iw_point *p) +{ + struct ieee_param *param; + int ret=0; + + down(&ieee->wx_sem); + //IEEE_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length); + + if (p->length < sizeof(struct ieee_param) || !p->pointer){ + ret = -EINVAL; + goto out; + } + + param = (struct ieee_param *)kmalloc(p->length, GFP_KERNEL); + if (param == NULL){ + ret = -ENOMEM; + goto out; + } + if (copy_from_user(param, p->pointer, p->length)) { + kfree(param); + ret = -EFAULT; + goto out; + } + + switch (param->cmd) { + + case IEEE_CMD_SET_WPA_PARAM: + ret = ieee80211_wpa_set_param(ieee, param->u.wpa_param.name, + param->u.wpa_param.value); + break; + + case IEEE_CMD_SET_WPA_IE: + ret = ieee80211_wpa_set_wpa_ie(ieee, param, p->length); + break; + + case IEEE_CMD_SET_ENCRYPTION: + ret = ieee80211_wpa_set_encryption(ieee, param, p->length); + break; + + case IEEE_CMD_MLME: + ret = ieee80211_wpa_mlme(ieee, param->u.mlme.command, + param->u.mlme.reason_code); + break; + + default: + printk("Unknown WPA supplicant request: %d\n",param->cmd); + ret = -EOPNOTSUPP; + break; + } + + if (ret == 0 && copy_to_user(p->pointer, param, p->length)) + ret = -EFAULT; + + kfree(param); +out: + up(&ieee->wx_sem); + + return ret; +} + +void notify_wx_assoc_event(struct ieee80211_device *ieee) +{ + union iwreq_data wrqu; + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (ieee->state == IEEE80211_LINKED) + memcpy(wrqu.ap_addr.sa_data, ieee->current_network.bssid, ETH_ALEN); + else + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wireless_send_event(ieee->dev, SIOCGIWAP, &wrqu, NULL); +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_get_beacon); +EXPORT_SYMBOL(ieee80211_wake_queue); +EXPORT_SYMBOL(ieee80211_stop_queue); +EXPORT_SYMBOL(ieee80211_reset_queue); +EXPORT_SYMBOL(ieee80211_softmac_stop_protocol); +EXPORT_SYMBOL(ieee80211_softmac_start_protocol); +EXPORT_SYMBOL(ieee80211_is_shortslot); +EXPORT_SYMBOL(ieee80211_is_54g); +EXPORT_SYMBOL(ieee80211_wpa_supplicant_ioctl); +EXPORT_SYMBOL(ieee80211_ps_tx_ack); +EXPORT_SYMBOL(notify_wx_assoc_event); +EXPORT_SYMBOL(ieee80211_stop_send_beacons); +EXPORT_SYMBOL(ieee80211_start_send_beacons); +EXPORT_SYMBOL(ieee80211_start_scan_syncro); +EXPORT_SYMBOL(ieee80211_start_protocol); +EXPORT_SYMBOL(ieee80211_stop_protocol); +EXPORT_SYMBOL(ieee80211_start_scan); +EXPORT_SYMBOL(ieee80211_stop_scan); +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL(ieee80211_ext_issue_assoc_req); +EXPORT_SYMBOL(ieee80211_ext_issue_disassoc); +EXPORT_SYMBOL(ieee80211_ext_issue_assoc_rsp); +EXPORT_SYMBOL(softmac_mgmt_xmit); +EXPORT_SYMBOL(ieee80211_ext_probe_resp_by_net); +EXPORT_SYMBOL(ieee80211_stop_scan); +EXPORT_SYMBOL(ieee80211_ext_send_11s_beacon); +EXPORT_SYMBOL(ieee80211_rx_auth_rq); +EXPORT_SYMBOL(ieee80211_associate_step1); +#endif // _RTL8187_EXT_PATCH_ +#else +EXPORT_SYMBOL_NOVERS(ieee80211_get_beacon); +EXPORT_SYMBOL_NOVERS(ieee80211_wake_queue); +EXPORT_SYMBOL_NOVERS(ieee80211_stop_queue); +EXPORT_SYMBOL_NOVERS(ieee80211_reset_queue); +EXPORT_SYMBOL_NOVERS(ieee80211_softmac_stop_protocol); +EXPORT_SYMBOL_NOVERS(ieee80211_softmac_start_protocol); +EXPORT_SYMBOL_NOVERS(ieee80211_is_shortslot); +EXPORT_SYMBOL_NOVERS(ieee80211_is_54g); +EXPORT_SYMBOL_NOVERS(ieee80211_wpa_supplicant_ioctl); +EXPORT_SYMBOL_NOVERS(ieee80211_ps_tx_ack); +EXPORT_SYMBOL_NOVERS(ieee80211_start_scan); +EXPORT_SYMBOL_NOVERS(ieee80211_stop_scan); +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL_NOVERS(ieee80211_ext_issue_assoc_req); +EXPORT_SYMBOL_NOVERS(ieee80211_ext_issue_disassoc); +EXPORT_SYMBOL_NOVERS(ieee80211_ext_issue_assoc_rsp); +EXPORT_SYMBOL_NOVERS(softmac_mgmt_xmit); +EXPORT_SYMBOL_NOVERS(ieee80211_ext_probe_resp_by_net); +EXPORT_SYMBOL_NOVERS(ieee80211_stop_scan); +EXPORT_SYMBOL_NOVERS(ieee80211_ext_send_11s_beacon); +EXPORT_SYMBOL_NOVERS(ieee80211_rx_auth_rq); +EXPORT_SYMBOL(ieee80211_associate_step1); +#endif // _RTL8187_EXT_PATCH_ + +#endif +//EXPORT_SYMBOL(ieee80211_sta_ps_send_null_frame); diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_softmac_wx.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_softmac_wx.c new file mode 100644 index 0000000..a23b9d0 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_softmac_wx.c @@ -0,0 +1,629 @@ +/* IEEE 802.11 SoftMAC layer + * Copyright (c) 2005 Andrea Merello + * + * Mostly extracted from the rtl8180-sa2400 driver for the + * in-kernel generic ieee802.11 stack. + * + * Some pieces of code might be stolen from ipw2100 driver + * copyright of who own it's copyright ;-) + * + * PS wx handler mostly stolen from hostap, copyright who + * own it's copyright ;-) + * + * released under the GPL + */ + + +#include "ieee80211.h" +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + +/* FIXME: add A freqs */ + +const long ieee80211_wlan_frequencies[] = { + 2412, 2417, 2422, 2427, + 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, + 2472, 2484 +}; + + +int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct iw_freq *fwrq = & wrqu->freq; + + down(&ieee->wx_sem); + + if(ieee->iw_mode == IW_MODE_INFRA){ + ret = -EOPNOTSUPP; + goto out; + } + + /* if setting by freq convert to channel */ + if (fwrq->e == 1) { + if ((fwrq->m >= (int) 2.412e8 && + fwrq->m <= (int) 2.487e8)) { + int f = fwrq->m / 100000; + int c = 0; + + while ((c < 14) && (f != ieee80211_wlan_frequencies[c])) + c++; + + /* hack to fall through */ + fwrq->e = 0; + fwrq->m = c + 1; + } + } + + if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1 ){ + ret = -EOPNOTSUPP; + goto out; + + }else { /* Set the channel */ + +#ifdef ENABLE_DOT11D + if(!IsLegalChannel(ieee, fwrq->m) ) + { + printk("channel(%d). is invalide\n", fwrq->m); + ret = -EOPNOTSUPP; + goto out; + } + else + { + if(ieee->iw_mode == IW_MODE_ADHOC) + { + if(ieee->MinPassiveChnlNum != MAX_CHANNEL_NUMBER+1) + { + if(fwrq->m >= ieee->MinPassiveChnlNum) + { + ret = -EOPNOTSUPP; + goto out; + } + } + } + } +#endif + ieee->current_network.channel = fwrq->m; + ieee->set_chan(ieee->dev, ieee->current_network.channel); + + if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER) + if(ieee->state == IEEE80211_LINKED){ + + ieee80211_stop_send_beacons(ieee); + ieee80211_start_send_beacons(ieee); + } + } + + ret = 0; +out: + up(&ieee->wx_sem); + return ret; +} + + +int ieee80211_wx_get_freq(struct ieee80211_device *ieee, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct iw_freq *fwrq = & wrqu->freq; + + if (ieee->current_network.channel == 0) + return -1; + + fwrq->m = ieee->current_network.channel; + fwrq->e = 0; + + return 0; +} + +int ieee80211_wx_get_wap(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + unsigned long flags; + + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return -1; + + /* We want avoid to give to the user inconsistent infos*/ + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->state != IEEE80211_LINKED && + ieee->state != IEEE80211_LINKED_SCANNING && + ieee->wap_set == 0) + + memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); + else + memcpy(wrqu->ap_addr.sa_data, + ieee->current_network.bssid, ETH_ALEN); + + spin_unlock_irqrestore(&ieee->lock, flags); + + return 0; +} + + +int ieee80211_wx_set_wap(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra) +{ + + int ret = 0; + u8 zero[] = {0,0,0,0,0,0}; + unsigned long flags; + + short ifup = ieee->proto_started;//dev->flags & IFF_UP; + struct sockaddr *temp = (struct sockaddr *)awrq; + + ieee->sync_scan_hurryup = 1; + + down(&ieee->wx_sem); + /* use ifconfig hw ether */ + if (ieee->iw_mode == IW_MODE_MASTER){ + ret = -1; + goto out; + } + + if (temp->sa_family != ARPHRD_ETHER){ + ret = -EINVAL; + goto out; + } + + if (ifup) + ieee80211_stop_protocol(ieee); + + /* just to avoid to give inconsistent infos in the + * get wx method. not really needed otherwise + */ + spin_lock_irqsave(&ieee->lock, flags); + + memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN); + ieee->wap_set = memcmp(temp->sa_data, zero,ETH_ALEN)!=0; + + spin_unlock_irqrestore(&ieee->lock, flags); + + if (ifup) + ieee80211_start_protocol(ieee); + +out: + up(&ieee->wx_sem); + return ret; +} + + int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b) +{ + int len,ret = 0; + unsigned long flags; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return -1; + + /* We want avoid to give to the user inconsistent infos*/ + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->current_network.ssid[0] == '\0' || + ieee->current_network.ssid_len == 0){ + ret = -1; + goto out; + } + + if (ieee->state != IEEE80211_LINKED && + ieee->state != IEEE80211_LINKED_SCANNING && + ieee->ssid_set == 0){ + ret = -1; + goto out; + } + len = ieee->current_network.ssid_len; + wrqu->essid.length = len; + strncpy(b,ieee->current_network.ssid,len); + wrqu->essid.flags = 1; + +out: + spin_unlock_irqrestore(&ieee->lock, flags); + + return ret; + +} + +int ieee80211_wx_set_rate(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + u32 target_rate = wrqu->bitrate.value; + + ieee->rate = target_rate/100000; + //FIXME: we might want to limit rate also in management protocols. + return 0; +} + + + +int ieee80211_wx_get_rate(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + wrqu->bitrate.value = ieee->rate * 100000; + + return 0; +} + +int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + + ieee->sync_scan_hurryup = 1; + + down(&ieee->wx_sem); + //printk("=======>%s\n", __func__); + + if (wrqu->mode == ieee->iw_mode) + goto out; + + if (wrqu->mode == IW_MODE_MONITOR){ + + ieee->dev->type = ARPHRD_IEEE80211; + }else{ + ieee->dev->type = ARPHRD_ETHER; + } + + if (!ieee->proto_started){ + ieee->iw_mode = wrqu->mode; + }else{ + ieee80211_stop_protocol(ieee); + ieee->iw_mode = wrqu->mode; + ieee80211_start_protocol(ieee); + } + +out: + up(&ieee->wx_sem); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void ieee80211_wx_sync_scan_wq(struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, wx_sync_scan_wq); +#else +void ieee80211_wx_sync_scan_wq(struct ieee80211_device *ieee) +{ +#endif + short chan; + + chan = ieee->current_network.channel; + + netif_carrier_off(ieee->dev); + + if (ieee->data_hard_stop) + ieee->data_hard_stop(ieee->dev); + + ieee80211_stop_send_beacons(ieee); + + ieee->state = IEEE80211_LINKED_SCANNING; + ieee->link_change(ieee->dev); + + ieee80211_start_scan_syncro(ieee); + + ieee->set_chan(ieee->dev, chan); + + ieee->state = IEEE80211_LINKED; + ieee->link_change(ieee->dev); + + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + + if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER) + ieee80211_start_send_beacons(ieee); + + netif_carrier_on(ieee->dev); + + up(&ieee->wx_sem); + +} + +int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret = 0; + + down(&ieee->wx_sem); + + if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)){ + ret = -1; + goto out; + } + + if ( ieee->state == IEEE80211_LINKED){ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + queue_work(ieee->wq, &ieee->wx_sync_scan_wq); +#else + schedule_task(&ieee->wx_sync_scan_wq); +#endif + /* intentionally forget to up sem */ + return 0; + } + +out: + up(&ieee->wx_sem); + return ret; +} + +int ieee80211_wx_set_essid(struct ieee80211_device *ieee, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + + int ret=0,len; + short proto_started; + unsigned long flags; + + ieee->sync_scan_hurryup = 1; + + down(&ieee->wx_sem); + + //printk("=======>%s\n", __func__); + proto_started = ieee->proto_started; + + if (wrqu->essid.length > IW_ESSID_MAX_SIZE){ + ret= -E2BIG; + goto out; + } + + if (ieee->iw_mode == IW_MODE_MONITOR){ + ret= -1; + goto out; + } + + if(proto_started){ + ieee80211_stop_protocol(ieee); + } + + /* this is just to be sure that the GET wx callback + * has consisten infos. not needed otherwise + */ + spin_lock_irqsave(&ieee->lock, flags); + + if (wrqu->essid.flags && wrqu->essid.length) { + len = ((wrqu->essid.length-1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length-1) : IW_ESSID_MAX_SIZE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + strncpy(ieee->current_network.ssid, extra, len); + ieee->current_network.ssid_len = len; +#else + strncpy(ieee->current_network.ssid, extra, len+1); + ieee->current_network.ssid_len = len+1; +#endif + ieee->ssid_set = 1; + //YJ,add,080819,for hidden ap + if(len == 0){ + memset(ieee->current_network.bssid, 0, ETH_ALEN); + ieee->current_network.capability = 0; + } + //YJ,add,080819,for hidden ap,end + } + else{ + ieee->ssid_set = 0; + ieee->current_network.ssid[0] = '\0'; + ieee->current_network.ssid_len = 0; + } + + spin_unlock_irqrestore(&ieee->lock, flags); + + if (proto_started){ + ieee80211_start_protocol(ieee); + } +out: + up(&ieee->wx_sem); + return ret; +} + + int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + + wrqu->mode = ieee->iw_mode; + return 0; +} + + int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + int *parms = (int *)extra; + int enable = (parms[0] > 0); + short prev = ieee->raw_tx; + + down(&ieee->wx_sem); + + if(enable) + ieee->raw_tx = 1; + else + ieee->raw_tx = 0; + + printk(KERN_INFO"raw TX is %s\n", + ieee->raw_tx ? "enabled" : "disabled"); + + if(ieee->iw_mode == IW_MODE_MONITOR) + { + if(prev == 0 && ieee->raw_tx){ + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + + netif_carrier_on(ieee->dev); + } + + if(prev && ieee->raw_tx == 1) + netif_carrier_off(ieee->dev); + } + + up(&ieee->wx_sem); + + return 0; +} + +int ieee80211_wx_get_name(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + strcpy(wrqu->name, "802.11"); + if(ieee->modulation & IEEE80211_CCK_MODULATION){ + strcat(wrqu->name, "b"); + if(ieee->modulation & IEEE80211_OFDM_MODULATION) + strcat(wrqu->name, "/g"); + }else if(ieee->modulation & IEEE80211_OFDM_MODULATION) + strcat(wrqu->name, "g"); + + if((ieee->state == IEEE80211_LINKED) || + (ieee->state == IEEE80211_LINKED_SCANNING)) + strcat(wrqu->name," linked"); + else if(ieee->state != IEEE80211_NOLINK) + strcat(wrqu->name," link.."); + + + return 0; +} + + +/* this is mostly stolen from hostap */ +int ieee80211_wx_set_power(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + + if( + (!ieee->sta_wake_up) || + (!ieee->ps_request_tx_ack) || + (!ieee->enter_sleep_state) || + (!ieee->ps_is_queue_empty)){ + + printk("ERROR. PS mode is tryied to be use but\ +driver missed a callback\n\n"); + + return -1; + } + + down(&ieee->wx_sem); + + if (wrqu->power.disabled){ + ieee->ps = IEEE80211_PS_DISABLED; + + goto exit; + } + switch (wrqu->power.flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + ieee->ps = IEEE80211_PS_UNICAST; + + break; + case IW_POWER_ALL_R: + ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST; + break; + + case IW_POWER_ON: + ieee->ps = IEEE80211_PS_DISABLED; + break; + + default: + ret = -EINVAL; + goto exit; + } + + if (wrqu->power.flags & IW_POWER_TIMEOUT) { + + ieee->ps_timeout = wrqu->power.value / 1000; + printk("Timeout %d\n",ieee->ps_timeout); + } + + if (wrqu->power.flags & IW_POWER_PERIOD) { + + ret = -EOPNOTSUPP; + goto exit; + //wrq->value / 1024; + + } +exit: + up(&ieee->wx_sem); + return ret; + +} + +/* this is stolen from hostap */ +int ieee80211_wx_get_power(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret =0; + + down(&ieee->wx_sem); + + if(ieee->ps == IEEE80211_PS_DISABLED){ + wrqu->power.disabled = 1; + goto exit; + } + + wrqu->power.disabled = 0; + +// if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + wrqu->power.flags = IW_POWER_TIMEOUT; + wrqu->power.value = ieee->ps_timeout * 1000; +// } else { +// ret = -EOPNOTSUPP; +// goto exit; + //wrqu->power.flags = IW_POWER_PERIOD; + //wrqu->power.value = ieee->current_network.dtim_period * + // ieee->current_network.beacon_interval * 1024; +// } + + + if (ieee->ps & IEEE80211_PS_MBCAST) + wrqu->power.flags |= IW_POWER_ALL_R; + else + wrqu->power.flags |= IW_POWER_UNICAST_R; + +exit: + up(&ieee->wx_sem); + return ret; + +} +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_wx_get_essid); +EXPORT_SYMBOL(ieee80211_wx_set_essid); +EXPORT_SYMBOL(ieee80211_wx_set_rate); +EXPORT_SYMBOL(ieee80211_wx_get_rate); +EXPORT_SYMBOL(ieee80211_wx_set_wap); +EXPORT_SYMBOL(ieee80211_wx_get_wap); +EXPORT_SYMBOL(ieee80211_wx_set_mode); +EXPORT_SYMBOL(ieee80211_wx_get_mode); +EXPORT_SYMBOL(ieee80211_wx_set_scan); +EXPORT_SYMBOL(ieee80211_wx_get_freq); +EXPORT_SYMBOL(ieee80211_wx_set_freq); +EXPORT_SYMBOL(ieee80211_wx_set_rawtx); +EXPORT_SYMBOL(ieee80211_wx_get_name); +EXPORT_SYMBOL(ieee80211_wx_set_power); +EXPORT_SYMBOL(ieee80211_wx_get_power); +EXPORT_SYMBOL(ieee80211_wlan_frequencies); +#else +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_essid); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_essid); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_rate); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_rate); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_wap); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_wap); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_mode); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_mode); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_scan); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_freq); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_freq); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_rawtx); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_name); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_power); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_power); +EXPORT_SYMBOL_NOVERS(ieee80211_wlan_frequencies); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_tx.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_tx.c new file mode 100644 index 0000000..cad850f --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_tx.c @@ -0,0 +1,876 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + James P. Ketrenos + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +****************************************************************************** + + Few modifications for Realtek's Wi-Fi drivers by + Andrea Merello + + A special thanks goes to Realtek for their support ! + +******************************************************************************/ + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + + +/* + + +802.11 Data Frame + + +802.11 frame_contorl for data frames - 2 bytes + ,-----------------------------------------------------------------------------------------. +bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | + |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------| +val | 0 | 0 | 0 | 1 | x | 0 | 0 | 0 | 1 | 0 | x | x | x | x | x | + |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------| +desc | ^-ver-^ | ^type-^ | ^-----subtype-----^ | to |from |more |retry| pwr |more |wep | + | | | x=0 data,x=1 data+ack | DS | DS |frag | | mgm |data | | + '-----------------------------------------------------------------------------------------' + /\ + | +802.11 Data Frame | + ,--------- 'ctrl' expands to >-----------' + | + ,--'---,-------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `--------------------------------------------------| |------' +Total: 28 non-data bytes `----.----' + | + .- 'Frame data' expands to <---------------------------' + | + V + ,---------------------------------------------------. +Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | + |------|------|---------|----------|------|---------| +Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | + | DSAP | SSAP | | | | Packet | + | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | + `-----------------------------------------| | +Total: 8 non-data bytes `----.----' + | + .- 'IP Packet' expands, if WEP enabled, to <--' + | + V + ,-----------------------. +Bytes | 4 | 0-2296 | 4 | + |-----|-----------|-----| +Desc. | IV | Encrypted | ICV | + | | IP Packet | | + `-----------------------' +Total: 8 non-data bytes + + +802.3 Ethernet Data Frame + + ,-----------------------------------------. +Bytes | 6 | 6 | 2 | Variable | 4 | + |-------|-------|------|-----------|------| +Desc. | Dest. | Source| Type | IP Packet | fcs | + | MAC | MAC | | | | + `-----------------------------------------' +Total: 18 non-data bytes + +In the event that fragmentation is required, the incoming payload is split into +N parts of size ieee->fts. The first fragment contains the SNAP header and the +remaining packets are just data. + +If encryption is enabled, each fragment payload size is reduced by enough space +to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP) +So if you have 1500 bytes of payload with ieee->fts set to 500 without +encryption it will take 3 frames. With WEP it will take 4 frames as the +payload of each frame is reduced to 492 bytes. + +* SKB visualization +* +* ,- skb->data +* | +* | ETHERNET HEADER ,-<-- PAYLOAD +* | | 14 bytes from skb->data +* | 2 bytes for Type --> ,T. | (sizeof ethhdr) +* | | | | +* |,-Dest.--. ,--Src.---. | | | +* | 6 bytes| | 6 bytes | | | | +* v | | | | | | +* 0 | v 1 | v | v 2 +* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +* ^ | ^ | ^ | +* | | | | | | +* | | | | `T' <---- 2 bytes for Type +* | | | | +* | | '---SNAP--' <-------- 6 bytes for SNAP +* | | +* `-IV--' <-------------------- 4 bytes for IV (WEP) +* +* SNAP HEADER +* +*/ + +static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; +static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; + +static inline int ieee80211_put_snap(u8 *data, u16 h_proto) +{ + struct ieee80211_snap_hdr *snap; + u8 *oui; + + snap = (struct ieee80211_snap_hdr *)data; + snap->dsap = 0xaa; + snap->ssap = 0xaa; + snap->ctrl = 0x03; + + if (h_proto == 0x8137 || h_proto == 0x80f3) + oui = P802_1H_OUI; + else + oui = RFC1042_OUI; + snap->oui[0] = oui[0]; + snap->oui[1] = oui[1]; + snap->oui[2] = oui[2]; + + *(u16 *)(data + SNAP_SIZE) = htons(h_proto); + + return SNAP_SIZE + sizeof(u16); +} + +int ieee80211_encrypt_fragment( + struct ieee80211_device *ieee, + struct sk_buff *frag, + int hdr_len) +{ + struct ieee80211_crypt_data* crypt = NULL;//ieee->crypt[ieee->tx_keyidx]; + int res;//, i; +// printk("====>wwwwww%s():ieee:%x, hdr_len:%d\n", __FUNCTION__, ieee, hdr_len); +/* printk("\n%s(), hdr_len:%d\n", __FUNCTION__, hdr_len); + for (i = 0; i < 48; i++) { + if (i % 16 == 0) printk("\n\t"); + printk("%2x ", *(frag->data+i)); + } +*/ + +#ifdef _RTL8187_EXT_PATCH_ +#if 0 + i = ieee80211_find_MP(ieee, ((struct ieee80211_hdr*) frag->data)->addr1); + if (i== -1){ + printk("error find MP entry in %s()\n", __FUNCTION__); + return i; + } + // printk("%s():"MAC_FMT", find in index:%d\n", __FUNCTION__, MAC_ARG(((struct ieee80211_hdr*)frag->data)->addr1), i); +#endif +// crypt = ieee->cryptlist[MAX_MP-1]->crypt[ieee->tx_keyidx]; + crypt = ieee->cryptlist[0]->crypt[ieee->tx_keyidx]; +#else + crypt = ieee->crypt[ieee->tx_keyidx]; +#endif + /*added to care about null crypt condition, to solve that system hangs when shared keys error*/ + if (!crypt || !crypt->ops) + return -1; + +#ifdef CONFIG_IEEE80211_CRYPT_TKIP + struct ieee80211_hdr *header; + + if (ieee->tkip_countermeasures && + crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) { + header = (struct ieee80211_hdr *) frag->data; + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "TX packet to " MAC_FMT "\n", + ieee->dev->name, MAC_ARG(header->addr1)); + } + return -1; + } +#endif + /* To encrypt, frame format is: + * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ + + // PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption. + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv); + if (res == 0 && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_INFO "%s: Encryption failed: len=%d.\n", + ieee->dev->name, frag->len); + ieee->ieee_stats.tx_discards++; + return -1; + } + + return 0; +} + + +void ieee80211_txb_free(struct ieee80211_txb *txb) { + int i; + if (unlikely(!txb)) + return; + for (i = 0; i < txb->nr_frags; i++) + if (txb->fragments[i]) + dev_kfree_skb_any(txb->fragments[i]); + kfree(txb); +} + +struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size, + int gfp_mask) +{ + struct ieee80211_txb *txb; + int i; + txb = kmalloc( + sizeof(struct ieee80211_txb) + (sizeof(u8*) * nr_frags), + gfp_mask); + if (!txb) + return NULL; + + memset(txb, 0, sizeof(struct ieee80211_txb)); + txb->nr_frags = nr_frags; + txb->frag_size = txb_size; + + for (i = 0; i < nr_frags; i++) { + txb->fragments[i] = dev_alloc_skb(txb_size); + if (unlikely(!txb->fragments[i])) { + i--; + break; + } + } + if (unlikely(i != nr_frags)) { + while (i >= 0) + dev_kfree_skb_any(txb->fragments[i--]); + kfree(txb); + return NULL; + } + return txb; +} + +// Classify the to-be send data packet +// Need to acquire the sent queue index. +static int +ieee80211_classify(struct sk_buff *skb, struct ieee80211_network *network) +{ + struct ether_header *eh = (struct ether_header*)skb->data; + unsigned int wme_UP = 0; + + if(!network->QoS_Enable) { + skb->priority = 0; + return(wme_UP); + } + + if(eh->ether_type == __constant_htons(ETHERTYPE_IP)) { + const struct iphdr *ih = (struct iphdr*)(skb->data + \ + sizeof(struct ether_header)); + wme_UP = (ih->tos >> 5)&0x07; + } else if (vlan_tx_tag_present(skb)) {//vtag packet +#ifndef VLAN_PRI_SHIFT +#define VLAN_PRI_SHIFT 13 /* Shift to find VLAN user priority */ +#define VLAN_PRI_MASK 7 /* Mask for user priority bits in VLAN */ +#endif + u32 tag = vlan_tx_tag_get(skb); + wme_UP = (tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK; + } else if(ETH_P_PAE == ntohs(((struct ethhdr *)skb->data)->h_proto)) { + //printk(KERN_WARNING "type = normal packet\n"); + wme_UP = 7; + } + skb->priority = wme_UP; +/* + if (network->QoS_Enable) { + skb->priority = wme_UP; + }else { + skb->priority = 0; + } +*/ + return(wme_UP); +} + +#ifdef _RTL8187_EXT_PATCH_ +// based on part of ieee80211_xmit. Mainly allocate txb. ieee->lock is held +struct ieee80211_txb *ieee80211_ext_alloc_txb(struct sk_buff *skb, struct net_device *dev, struct ieee80211_hdr_3addr *header, int hdr_len, u8 isQoS, u16 *pQOS_ctl, int isEncrypt, struct ieee80211_crypt_data* crypt) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + struct ieee80211_device *ieee = netdev_priv(dev); +#else + struct ieee80211_device *ieee = (struct ieee80211_device *)dev->priv; +#endif + struct ieee80211_txb *txb = NULL; + struct ieee80211_hdr_3addr *frag_hdr; + int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size; + int ether_type; + int bytes, QOS_ctl; + struct sk_buff *skb_frag; + + ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto); + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + + /* Determine fragmentation size based on destination (multicast + * and broadcast are not fragmented) */ + // if (is_multicast_ether_addr(dest) || + // is_broadcast_ether_addr(dest)) { + if (is_multicast_ether_addr(header->addr1) || + is_broadcast_ether_addr(header->addr1)) { + frag_size = MAX_FRAG_THRESHOLD; + QOS_ctl = QOS_CTL_NOTCONTAIN_ACK; + } + else { + //printk(KERN_WARNING "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&frag_size = %d\n", frag_size); + frag_size = ieee->fts;//default:392 + QOS_ctl = 0; + } + + if(isQoS) { + QOS_ctl |= skb->priority; //set in the ieee80211_classify + *pQOS_ctl = cpu_to_le16(QOS_ctl); + } + //printk(KERN_WARNING "header size = %d, QOS_ctl = %x\n", hdr_len,QOS_ctl); + /* Determine amount of payload per fragment. Regardless of if + * this stack is providing the full 802.11 header, one will + * eventually be affixed to this fragment -- so we must account for + * it when determining the amount of payload space. */ + //bytes_per_frag = frag_size - (IEEE80211_3ADDR_LEN + (ieee->current_network->QoS_Enable ? 2:0)); + bytes_per_frag = frag_size - hdr_len; + if (ieee->config & + (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + bytes_per_frag -= IEEE80211_FCS_LEN; + + /* Each fragment may need to have room for encryptiong pre/postfix */ + if (isEncrypt) + bytes_per_frag -= crypt->ops->extra_prefix_len + + crypt->ops->extra_postfix_len; + + /* Number of fragments is the total bytes_per_frag / + * payload_per_fragment */ + nr_frags = bytes / bytes_per_frag; + bytes_last_frag = bytes % bytes_per_frag; + if (bytes_last_frag) + nr_frags++; + else + bytes_last_frag = bytes_per_frag; + + /* When we allocate the TXB we allocate enough space for the reserve + * and full fragment bytes (bytes_per_frag doesn't include prefix, + * postfix, header, FCS, etc.) */ + txb = ieee80211_alloc_txb(nr_frags, frag_size, GFP_ATOMIC); + if (unlikely(!txb)) { + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + return NULL; + } + txb->encrypted = isEncrypt; + txb->payload_size = bytes; + + for (i = 0; i < nr_frags; i++) { + skb_frag = txb->fragments[i]; + skb_frag->priority = UP2AC(skb->priority); + if (isEncrypt) + skb_reserve(skb_frag, crypt->ops->extra_prefix_len); + + frag_hdr = (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len); + memcpy(frag_hdr, (void *)header, hdr_len); + + /* If this is not the last fragment, then add the MOREFRAGS + * bit to the frame control */ + if (i != nr_frags - 1) { + frag_hdr->frame_ctl = cpu_to_le16( + header->frame_ctl | IEEE80211_FCTL_MOREFRAGS); + bytes = bytes_per_frag; + + } else { + /* The last fragment takes the remaining length */ + bytes = bytes_last_frag; + } + + frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4 | i); + //frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl<<4 | i); + // + + /* Put a SNAP header on the first fragment */ + if (i == 0) { + ieee80211_put_snap( + skb_put(skb_frag, SNAP_SIZE + sizeof(u16)), ether_type); + bytes -= SNAP_SIZE + sizeof(u16); + } + + memcpy(skb_put(skb_frag, bytes), skb->data, bytes); + + /* Advance the SKB... */ + skb_pull(skb, bytes); + + /* Encryption routine will move the header forward in order + * to insert the IV between the header and the payload */ + if (isEncrypt) + ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); + if (ieee->config & + (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + skb_put(skb_frag, 4); + } + // Advance sequence number in data frame. + //printk(KERN_WARNING "QoS Enalbed? %s\n", ieee->current_network.QoS_Enable?"Y":"N"); + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + // stanley, just for debug +/* +{ + int j=0; + for(j=0;jfragments[j]; + printk("send(%d): ", j); + for (i=0;ilen;i++) + printk("%02X ", skb->data[i]&0xff); + printk("\n"); + } +} +*/ + + return txb; +} + + +// based on part of ieee80211_xmit. Mainly allocate txb. ieee->lock is held +// Assume no encryption, no FCS computing +struct ieee80211_txb *ieee80211_ext_reuse_txb(struct sk_buff *skb, struct net_device *dev, struct ieee80211_hdr_3addr *header, int hdr_len, u8 isQoS, u16 *pQOS_ctl, int isEncrypt, struct ieee80211_crypt_data* crypt) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + struct ieee80211_device *ieee = netdev_priv(dev); +#else + struct ieee80211_device *ieee = (struct ieee80211_device *)dev->priv; +#endif + struct ieee80211_txb *txb = NULL; + struct ieee80211_hdr_3addr *frag_hdr; + int ether_type; + int bytes, QOS_ctl; + + ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto); + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + + if (is_multicast_ether_addr(header->addr1) || + is_broadcast_ether_addr(header->addr1)) { + QOS_ctl = QOS_CTL_NOTCONTAIN_ACK; + } + else { + QOS_ctl = 0; + } + + if(isQoS) { + QOS_ctl |= skb->priority; //set in the ieee80211_classify + *pQOS_ctl = cpu_to_le16(QOS_ctl); + } + + txb = kmalloc( sizeof(struct ieee80211_txb) + sizeof(u8*), GFP_ATOMIC ); + if (unlikely(!txb)) { + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + return NULL; + } + + txb->nr_frags = 1; + txb->frag_size = bytes; + txb->encrypted = isEncrypt; + txb->payload_size = bytes; + + txb->fragments[0] = skb; + ieee80211_put_snap( + skb_push(skb, SNAP_SIZE + sizeof(u16)), ether_type); + frag_hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, hdr_len); + memcpy(frag_hdr, (void *)header, hdr_len); + frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4 | 0); + skb->priority = UP2AC(skb->priority); + if(isEncrypt) + ieee80211_encrypt_fragment(ieee,skb,hdr_len); + + // Advance sequence number in data frame. + //printk(KERN_WARNING "QoS Enalbed? %s\n", ieee->current_network.QoS_Enable?"Y":"N"); + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + return txb; +} + +#endif // _RTL8187_EXT_PATCH_ + +/* SKBs are added to the ieee->tx_queue. */ +int ieee80211_xmit(struct sk_buff *skb, + struct net_device *dev) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) + struct ieee80211_device *ieee = netdev_priv(dev); +#else + struct ieee80211_device *ieee = (struct ieee80211_device *)dev->priv; +#endif + struct ieee80211_txb *txb = NULL; + struct ieee80211_hdr_3addr_QOS *frag_hdr; + int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size; + unsigned long flags; + struct net_device_stats *stats = &ieee->stats; + int ether_type, encrypt; + int bytes, fc, QOS_ctl, hdr_len; + struct sk_buff *skb_frag; + //struct ieee80211_hdr header = { /* Ensure zero initialized */ + // .duration_id = 0, + // .seq_ctl = 0 + //}; + struct ieee80211_hdr_3addr_QOS header = { /* Ensure zero initialized */ + .duration_id = 0, + .seq_ctl = 0, + .QOS_ctl = 0 + }; + u8 dest[ETH_ALEN], src[ETH_ALEN]; + + struct ieee80211_crypt_data* crypt; + + //printk(KERN_WARNING "upper layer packet!\n"); + spin_lock_irqsave(&ieee->lock, flags); + + /* If there is no driver handler to take the TXB, dont' bother + * creating it... */ + if ((!ieee->hard_start_xmit && !(ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE))|| + ((!ieee->softmac_data_hard_start_xmit && (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE)))) { + printk(KERN_WARNING "%s: No xmit handler.\n", + ieee->dev->name); + goto success; + } + + ieee80211_classify(skb,&ieee->current_network); + if(likely(ieee->raw_tx == 0)){ + + if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { + printk(KERN_WARNING "%s: skb too small (%d).\n", + ieee->dev->name, skb->len); + goto success; + } + + +#ifdef _RTL8187_EXT_PATCH_ + // note, skb->priority which was set by ieee80211_classify, and used by physical tx + if((ieee->iw_mode == ieee->iw_ext_mode) && (ieee->ext_patch_ieee80211_xmit)) + { + txb = ieee->ext_patch_ieee80211_xmit(skb, dev); + goto success; + } +#endif + + ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto); +#ifdef _RTL8187_EXT_PATCH_ + crypt = ieee->cryptlist[0]->crypt[ieee->tx_keyidx]; +#else + crypt = ieee->crypt[ieee->tx_keyidx]; +#endif + encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) && + ieee->host_encrypt && crypt && crypt->ops; + + if (!encrypt && ieee->ieee802_1x && + ieee->drop_unencrypted && ether_type != ETH_P_PAE) { + stats->tx_dropped++; + goto success; + } + + #ifdef CONFIG_IEEE80211_DEBUG + if (crypt && !encrypt && ether_type == ETH_P_PAE) { + struct eapol *eap = (struct eapol *)(skb->data + + sizeof(struct ethhdr) - SNAP_SIZE - sizeof(u16)); + IEEE80211_DEBUG_EAP("TX: IEEE 802.11 EAPOL frame: %s\n", + eap_get_type(eap->type)); + } + #endif + + /* Save source and destination addresses */ + memcpy(&dest, skb->data, ETH_ALEN); + memcpy(&src, skb->data+ETH_ALEN, ETH_ALEN); + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + + if(ieee->current_network.QoS_Enable) { + if (encrypt) + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA | + IEEE80211_FCTL_WEP; + else + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA; + + } else { + if (encrypt) + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | + IEEE80211_FCTL_WEP; + else + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + } + + if (ieee->iw_mode == IW_MODE_INFRA) { + fc |= IEEE80211_FCTL_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, + Addr3 = DA */ + memcpy(&header.addr1, ieee->current_network.bssid, ETH_ALEN); + memcpy(&header.addr2, &src, ETH_ALEN); + memcpy(&header.addr3, &dest, ETH_ALEN); + } else if (ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, + Addr3 = BSSID */ + memcpy(&header.addr1, dest, ETH_ALEN); + memcpy(&header.addr2, src, ETH_ALEN); + memcpy(&header.addr3, ieee->current_network.bssid, ETH_ALEN); + } + // printk(KERN_WARNING "essid MAC address is "MAC_FMT, MAC_ARG(&header.addr1)); + header.frame_ctl = cpu_to_le16(fc); + //hdr_len = IEEE80211_3ADDR_LEN; + + /* Determine fragmentation size based on destination (multicast + * and broadcast are not fragmented) */ +// if (is_multicast_ether_addr(dest) || +// is_broadcast_ether_addr(dest)) { + if (is_multicast_ether_addr(header.addr1) || + is_broadcast_ether_addr(header.addr1)) { + frag_size = MAX_FRAG_THRESHOLD; + QOS_ctl = QOS_CTL_NOTCONTAIN_ACK; + } + else { + //printk(KERN_WARNING "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&frag_size = %d\n", frag_size); + frag_size = ieee->fts;//default:392 + QOS_ctl = 0; + } + + if (ieee->current_network.QoS_Enable) { + hdr_len = IEEE80211_3ADDR_LEN + 2; + QOS_ctl |= skb->priority; //set in the ieee80211_classify + header.QOS_ctl = cpu_to_le16(QOS_ctl); + } else { + hdr_len = IEEE80211_3ADDR_LEN; + } + //printk(KERN_WARNING "header size = %d, QOS_ctl = %x\n", hdr_len,QOS_ctl); + /* Determine amount of payload per fragment. Regardless of if + * this stack is providing the full 802.11 header, one will + * eventually be affixed to this fragment -- so we must account for + * it when determining the amount of payload space. */ + //bytes_per_frag = frag_size - (IEEE80211_3ADDR_LEN + (ieee->current_network->QoS_Enable ? 2:0)); + bytes_per_frag = frag_size - hdr_len; + if (ieee->config & + (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + bytes_per_frag -= IEEE80211_FCS_LEN; + + /* Each fragment may need to have room for encryptiong pre/postfix */ + if (encrypt) + bytes_per_frag -= crypt->ops->extra_prefix_len + + crypt->ops->extra_postfix_len; + + /* Number of fragments is the total bytes_per_frag / + * payload_per_fragment */ + nr_frags = bytes / bytes_per_frag; + bytes_last_frag = bytes % bytes_per_frag; + if (bytes_last_frag) + nr_frags++; + else + bytes_last_frag = bytes_per_frag; + + /* When we allocate the TXB we allocate enough space for the reserve + * and full fragment bytes (bytes_per_frag doesn't include prefix, + * postfix, header, FCS, etc.) */ + txb = ieee80211_alloc_txb(nr_frags, frag_size, GFP_ATOMIC); + if (unlikely(!txb)) { + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + goto failed; + } + txb->encrypted = encrypt; + txb->payload_size = bytes; + + for (i = 0; i < nr_frags; i++) { + skb_frag = txb->fragments[i]; + skb_frag->priority = UP2AC(skb->priority); + if (encrypt) + skb_reserve(skb_frag, crypt->ops->extra_prefix_len); + + frag_hdr = (struct ieee80211_hdr_3addr_QOS *)skb_put(skb_frag, hdr_len); + memcpy(frag_hdr, &header, hdr_len); + + /* If this is not the last fragment, then add the MOREFRAGS + * bit to the frame control */ + if (i != nr_frags - 1) { + frag_hdr->frame_ctl = cpu_to_le16( + fc | IEEE80211_FCTL_MOREFRAGS); + bytes = bytes_per_frag; + + } else { + /* The last fragment takes the remaining length */ + bytes = bytes_last_frag; + } + if(ieee->current_network.QoS_Enable) { + // add 1 only indicate to corresponding seq number control 2006/7/12 + frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl[UP2AC(skb->priority)+1]<<4 | i); + //printk(KERN_WARNING "skb->priority = %d,", skb->priority); + //printk(KERN_WARNING "type:%d: seq = %d\n",UP2AC(skb->priority),ieee->seq_ctrl[UP2AC(skb->priority)+1]); + } else { + frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4 | i); + } + //frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl<<4 | i); + // + + /* Put a SNAP header on the first fragment */ + if (i == 0) { + ieee80211_put_snap( + skb_put(skb_frag, SNAP_SIZE + sizeof(u16)), + ether_type); + bytes -= SNAP_SIZE + sizeof(u16); + } + + memcpy(skb_put(skb_frag, bytes), skb->data, bytes); + + /* Advance the SKB... */ + skb_pull(skb, bytes); + + /* Encryption routine will move the header forward in order + * to insert the IV between the header and the payload */ + if (encrypt) + ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); + if (ieee->config & + (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + skb_put(skb_frag, 4); + } + // Advance sequence number in data frame. + //printk(KERN_WARNING "QoS Enalbed? %s\n", ieee->current_network.QoS_Enable?"Y":"N"); + if (ieee->current_network.QoS_Enable) { + if (ieee->seq_ctrl[UP2AC(skb->priority) + 1] == 0xFFF) + ieee->seq_ctrl[UP2AC(skb->priority) + 1] = 0; + else + ieee->seq_ctrl[UP2AC(skb->priority) + 1]++; + } else { + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + } + //--- + }else{ + if (unlikely(skb->len < sizeof(struct ieee80211_hdr_3addr))) { + printk(KERN_WARNING "%s: skb too small (%d).\n", + ieee->dev->name, skb->len); + goto success; + } + + txb = ieee80211_alloc_txb(1, skb->len, GFP_ATOMIC); + if(!txb){ + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + goto failed; + } + + txb->encrypted = 0; + txb->payload_size = skb->len; + memcpy(skb_put(txb->fragments[0],skb->len), skb->data, skb->len); + } + + success: + spin_unlock_irqrestore(&ieee->lock, flags); +#ifdef _RTL8187_EXT_PATCH_ + // Sometimes, extension mode can reuse skb (by txb->fragments[0]) + if( ! ((ieee->iw_mode == ieee->iw_ext_mode) && txb && (txb->fragments[0] == skb)) ) +#endif + dev_kfree_skb_any(skb); + if (txb) { + if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE){ + ieee80211_softmac_xmit(txb, ieee); + }else{ + if ((*ieee->hard_start_xmit)(txb, dev) == 0) { + stats->tx_packets++; + stats->tx_bytes += txb->payload_size; + return 0; + } + ieee80211_txb_free(txb); + } + } + + return 0; + + failed: + spin_unlock_irqrestore(&ieee->lock, flags); + netif_stop_queue(dev); + printk("netif_stop_queue in ieee80211_xmit \n"); + stats->tx_errors++; + return 1; + +} + + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +EXPORT_SYMBOL(ieee80211_txb_free); +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL(ieee80211_alloc_txb); +EXPORT_SYMBOL(ieee80211_ext_alloc_txb); +EXPORT_SYMBOL(ieee80211_ext_reuse_txb); + +EXPORT_SYMBOL(ieee80211_encrypt_fragment); +#endif // _RTL8187_EXT_PATCH_ +#else +EXPORT_SYMBOL_NOVERS(ieee80211_txb_free); +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL_NOVERS(ieee80211_alloc_txb); +EXPORT_SYMBOL_NOVERS(ieee80211_ext_reuse_txb); + +EXPORT_SYMBOL_NOVERS(ieee80211_encrypt_fragment); +#endif // _RTL8187_EXT_PATCH_ +#endif + diff --git a/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_wx.c b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_wx.c new file mode 100644 index 0000000..a5dffeb --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/ieee80211_wx.c @@ -0,0 +1,926 @@ +/****************************************************************************** + + Copyright(c) 2004 Intel Corporation. All rights reserved. + + Portions of this file are based on the WEP enablement code provided by the + Host AP project hostap-drivers v0.1.3 + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + James P. Ketrenos + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include + +#include "ieee80211.h" +static const char *ieee80211_modes[] = { + "?", "a", "b", "ab", "g", "ag", "bg", "abg" +}; + +#define MAX_CUSTOM_LEN 64 +static inline char *rtl819x_translate_scan(struct ieee80211_device *ieee, + char *start, char *stop, + struct ieee80211_network *network, + struct iw_request_info *info) +{ + char custom[MAX_CUSTOM_LEN]; + char *p; + struct iw_event iwe; + int i, j; + u8 max_rate, rate; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); +#else + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); +#endif + /* Remaining entries will be displayed in the order we provide them */ + + /* Add the ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + //YJ,modified,080903,for hidden ap + //if (network->flags & NETWORK_EMPTY_ESSID) { + if (network->ssid_len == 0) { + iwe.u.data.length = sizeof(""); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, ""); +#else + start = iwe_stream_add_point(start, stop, &iwe, ""); +#endif + } else { + iwe.u.data.length = min(network->ssid_len, (u8)32); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid); +#else + start = iwe_stream_add_point(start, stop, &iwe, network->ssid); +#endif + } + + /* Add the protocol name */ + iwe.cmd = SIOCGIWNAME; + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); +#else + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); +#endif + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (network->capability & + (WLAN_CAPABILITY_BSS | WLAN_CAPABILITY_IBSS)) { + if (network->capability & WLAN_CAPABILITY_BSS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_event(info, start, stop, &iwe, + IW_EV_UINT_LEN); +#else + start = iwe_stream_add_event(start, stop, &iwe, + IW_EV_UINT_LEN); +#endif + } + + /* Add frequency/channel */ + iwe.cmd = SIOCGIWFREQ; +/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode); + iwe.u.freq.e = 3; */ + iwe.u.freq.m = network->channel; + iwe.u.freq.e = 0; + iwe.u.freq.i = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); +#else + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); +#endif + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (network->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; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid); +#else + start = iwe_stream_add_point(start, stop, &iwe, network->ssid); +#endif + /* Add basic and extended rates */ + max_rate = 0; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); + for (i = 0, j = 0; i < network->rates_len; ) { + if (j < network->rates_ex_len && + ((network->rates_ex[j] & 0x7F) < + (network->rates[i] & 0x7F))) + rate = network->rates_ex[j++] & 0x7F; + else + rate = network->rates[i++] & 0x7F; + if (rate > max_rate) + max_rate = rate; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + } + for (; j < network->rates_ex_len; j++) { + rate = network->rates_ex[j] & 0x7F; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + if (rate > max_rate) + max_rate = rate; + } + + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = max_rate * 500000; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_event(info, start, stop, &iwe,IW_EV_PARAM_LEN); +#else + start = iwe_stream_add_event(start, stop, &iwe,IW_EV_PARAM_LEN); +#endif + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = p - custom; + if (iwe.u.data.length) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); +#else + start = iwe_stream_add_point(start, stop, &iwe, custom); +#endif + /* Add quality statistics */ + /* TODO: Fix these values... */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = network->stats.signalstrength;//network->stats.signal; + iwe.u.qual.level = network->stats.signal;//network->stats.rssi; + iwe.u.qual.noise = network->stats.noise; +#if 0 + iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK; + if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) + iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; + if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) + iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; + if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) + iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID; +#endif + + iwe.u.qual.updated = 0x7;//network->stats.mask & IEEE80211_STATMASK_WEMASK; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); +#else + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); +#endif + iwe.cmd = IWEVCUSTOM; + p = custom; + + iwe.u.data.length = p - custom; + if (iwe.u.data.length) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); +#else + start = iwe_stream_add_point(start, stop, &iwe, custom); +#endif +#if 0 + if (ieee->wpa_enabled && network->wpa_ie_len){ + char buf[MAX_WPA_IE_LEN * 2 + 30]; + // printk("WPA IE\n"); + u8 *p = buf; + p += sprintf(p, "wpa_ie="); + for (i = 0; i < network->wpa_ie_len; i++) { + p += sprintf(p, "%02x", network->wpa_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + + if (ieee->wpa_enabled && network->rsn_ie_len){ + char buf[MAX_WPA_IE_LEN * 2 + 30]; + + u8 *p = buf; + p += sprintf(p, "rsn_ie="); + for (i = 0; i < network->rsn_ie_len; i++) { + p += sprintf(p, "%02x", network->rsn_ie[i]); + } + + +#else + memset(&iwe, 0, sizeof(iwe)); + if (network->wpa_ie_len) { + //printk("wpa_ie_len:%d\n", network->wpa_ie_len); + char buf[MAX_WPA_IE_LEN]; + memcpy(buf, network->wpa_ie, network->wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->wpa_ie_len; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, buf); +#else + start = iwe_stream_add_point(start, stop, &iwe, buf); +#endif + } + + memset(&iwe, 0, sizeof(iwe)); + if (network->rsn_ie_len) { + //printk("=====>rsn_ie_len:\n", network->rsn_ie_len); + #if 0 + { + int i; + for (i=0; irsn_ie_len; i++); + printk("%2x ", network->rsn_ie[i]); + printk("\n"); + } + #endif + char buf[MAX_WPA_IE_LEN]; + memcpy(buf, network->rsn_ie, network->rsn_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->rsn_ie_len; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, buf); +#else + start = iwe_stream_add_point(start, stop, &iwe, buf); +#endif + } + +#endif + + /* Add EXTRA: Age to display seconds since last beacon/probe response + * for given network. */ + iwe.cmd = IWEVCUSTOM; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + " Last beacon: %lums ago", (jiffies - network->last_scanned) * 100 / HZ ); + iwe.u.data.length = p - custom; + if (iwe.u.data.length) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) || defined (QMI_26_6)) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); +#else + start = iwe_stream_add_point(start, stop, &iwe, custom); +#endif + + return start; +} + +int ieee80211_wx_get_scan(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ieee80211_network *network; + unsigned long flags; + int err = 0; + char *ev = extra; + char *stop = ev + wrqu->data.length;//IW_SCAN_MAX_DATA; + //char *stop = ev + IW_SCAN_MAX_DATA; + int i = 0; + + IEEE80211_DEBUG_WX("Getting scan\n"); + down(&ieee->wx_sem); + spin_lock_irqsave(&ieee->lock, flags); + + if(!ieee->bHwRadioOff) + { + list_for_each_entry(network, &ieee->network_list, list) { + i++; + + if((stop-ev)<200) + { + err = -E2BIG; + break; + } + + if (ieee->scan_age == 0 || + time_after(network->last_scanned + ieee->scan_age, jiffies)) + { + ev = rtl819x_translate_scan(ieee, ev, stop, network, info); + } + else + IEEE80211_DEBUG_SCAN( + "Not showing network '%s (" + MAC_FMT ")' due to age (%lums).\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid), + (jiffies - network->last_scanned) / (HZ / 100)); + } + } + spin_unlock_irqrestore(&ieee->lock, flags); + up(&ieee->wx_sem); + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; + + IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i); + + return err; +} + +int ieee80211_wx_set_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &(wrqu->encoding); + struct net_device *dev = ieee->dev; + struct ieee80211_security sec = { + .flags = 0 + }; + int i, key, key_provided, len; + struct ieee80211_crypt_data **crypt; + + IEEE80211_DEBUG_WX("SET_ENCODE\n"); + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + key_provided = 1; + } else { + key_provided = 0; + key = ieee->tx_keyidx; + } + + IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? + "provided" : "default"); +#ifdef _RTL8187_EXT_PATCH_ +#if 0 +{ + int j; + for(j=0; jcryptlist[j]->crypt[key]; +#else + crypt = &ieee->cryptlist[0]->crypt[key]; +#endif +#else + crypt = &ieee->crypt[key]; +#endif + if (erq->flags & IW_ENCODE_DISABLED) { + if (key_provided && *crypt) { + IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n", + key); + ieee80211_crypt_delayed_deinit(ieee, crypt); + } else + IEEE80211_DEBUG_WX("Disabling encryption.\n"); + + /* Check all the keys to see if any are still configured, + * and if no key index was provided, de-init them all */ + for (i = 0; i < WEP_KEYS; i++) { +#ifdef _RTL8187_EXT_PATCH_ + + if (ieee->cryptlist[0]->crypt[i] != NULL){ +#else + + if (ieee->crypt[i] != NULL) { +#endif + if (key_provided) + break; + ieee80211_crypt_delayed_deinit( +#ifdef _RTL8187_EXT_PATCH_ + ieee, &ieee->cryptlist[0]->crypt[i]); +#else + ieee, &ieee->crypt[i]); +#endif + } + } + + if (i == WEP_KEYS) { + sec.enabled = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL; + } + + goto done; + } + + + + sec.enabled = 1; + sec.flags |= SEC_ENABLED; + + if (*crypt != NULL && (*crypt)->ops != NULL && + strcmp((*crypt)->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm + * on this key */ + ieee80211_crypt_delayed_deinit(ieee, crypt); + } + + if (*crypt == NULL) { + struct ieee80211_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); + new_crypt->ops = ieee80211_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("ieee80211_crypt_wep"); + new_crypt->ops = ieee80211_get_crypto_ops("WEP"); + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) +#else + if (new_crypt->ops && try_inc_mod_count(new_crypt->ops->owner)) +#endif + new_crypt->priv = new_crypt->ops->init(key); + + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module ieee80211_crypt_wep\n", + dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + /* If a new key was provided, set it up */ + if (erq->length > 0) { + len = erq->length <= 5 ? 5 : 13; + memcpy(sec.keys[key], keybuf, erq->length); + if (len > erq->length) + memset(sec.keys[key] + erq->length, 0, + len - erq->length); + IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", + key, escape_essid(sec.keys[key], len), + erq->length, len); + sec.key_sizes[key] = len; + (*crypt)->ops->set_key(sec.keys[key], len, NULL, + (*crypt)->priv); + sec.flags |= (1 << key); + /* This ensures a key will be activated if no key is + * explicitely set */ + if (key == sec.active_key) + sec.flags |= SEC_ACTIVE_KEY; + + ieee->tx_keyidx = key; //we need it to support multi_key setting. added by wb 2008_2_22 + } else { + len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, + NULL, (*crypt)->priv); + if (len == 0) { + /* Set a default key of all 0 */ + IEEE80211_DEBUG_WX("Setting key %d to all zero.\n", + key); + memset(sec.keys[key], 0, 13); + (*crypt)->ops->set_key(sec.keys[key], 13, NULL, + (*crypt)->priv); + sec.key_sizes[key] = 13; + sec.flags |= (1 << key); + } + + /* No key data - just set the default TX key index */ + if (key_provided) { + IEEE80211_DEBUG_WX( + "Setting key %d to default Tx key.\n", key); + ieee->tx_keyidx = key; + sec.active_key = key; + sec.flags |= SEC_ACTIVE_KEY; + } + } +#ifdef _RTL8187_EXT_PATCH_ +#if 0 +} +} +#endif +#endif + done: + ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); + sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; + sec.flags |= SEC_AUTH_MODE; + IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ? + "OPEN" : "SHARED KEY"); + + /* For now we just support WEP, so only set that security level... + * TODO: When WPA is added this is one place that needs to change */ + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ + + if (ieee->set_security) + ieee->set_security(dev, &sec); + + /* Do not reset port if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. If your hardware requires a reset after WEP + * configuration (for example... Prism2), implement the reset_port in + * the callbacks structures used to initialize the 802.11 stack. */ + if (ieee->reset_on_keychange && + ieee->iw_mode != IW_MODE_INFRA && + ieee->reset_port && ieee->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + return 0; +} + +int ieee80211_wx_get_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &(wrqu->encoding); + int len, key; + struct ieee80211_crypt_data *crypt; + + IEEE80211_DEBUG_WX("GET_ENCODE\n"); + + if(ieee->iw_mode == IW_MODE_MONITOR) + return -1; + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + } else + key = ieee->tx_keyidx; +#ifdef _RTL8187_EXT_PATCH_ + crypt = ieee->cryptlist[0]->crypt[key]; +#else + crypt = ieee->crypt[key]; +#endif + erq->flags = key + 1; + + if (crypt == NULL || crypt->ops == NULL) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + return 0; + } + + len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv); + erq->length = (len >= 0 ? len : 0); + + erq->flags |= IW_ENCODE_ENABLED; + + if (ieee->open_wep) + erq->flags |= IW_ENCODE_OPEN; + else + erq->flags |= IW_ENCODE_RESTRICTED; + + return 0; +} + +int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct net_device *dev = ieee->dev; + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int i, idx, ret = 0; + int group_key = 0; + const char *alg, *module; + struct ieee80211_crypto_ops *ops; + struct ieee80211_crypt_data **crypt; + + struct ieee80211_security sec = { + .flags = 0, + }; + //printk("======>encoding flag:%x,ext flag:%x, ext alg:%d\n", encoding->flags,ext->ext_flags, ext->alg); + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > WEP_KEYS) + return -EINVAL; + idx--; + } else + idx = ieee->tx_keyidx; + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { +#ifdef _RTL8187_EXT_PATCH_ + crypt = &ieee->cryptlist[0]->crypt[idx]; +#else + crypt = &ieee->crypt[idx]; +#endif + group_key = 1; + } else { + /* some Cisco APs use idx>0 for unicast in dynamic WEP */ + //printk("not group key, flags:%x, ext->alg:%d\n", ext->ext_flags, ext->alg); + if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) + return -EINVAL; + if (ieee->iw_mode == IW_MODE_INFRA) +#ifdef _RTL8187_EXT_PATCH_ + crypt = &ieee->cryptlist[0]->crypt[idx]; +#else + crypt = &ieee->crypt[idx]; +#endif + else + return -EINVAL; + } + + sec.flags |= SEC_ENABLED;// | SEC_ENCRYPT; + if ((encoding->flags & IW_ENCODE_DISABLED) || + ext->alg == IW_ENCODE_ALG_NONE) { + if (*crypt) + ieee80211_crypt_delayed_deinit(ieee, crypt); + + for (i = 0; i < WEP_KEYS; i++) +#ifdef _RTL8187_EXT_PATCH_ + if (ieee->cryptlist[0]->crypt[i] != NULL) +#else + if (ieee->crypt[i] != NULL) +#endif + break; + + if (i == WEP_KEYS) { + sec.enabled = 0; + // sec.encrypt = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_LEVEL; + } + //printk("disabled: flag:%x\n", encoding->flags); + goto done; + } + + sec.enabled = 1; + // sec.encrypt = 1; +#if 0 + if (group_key ? !ieee->host_mc_decrypt : + !(ieee->host_encrypt || ieee->host_decrypt || + ieee->host_encrypt_msdu)) + goto skip_host_crypt; +#endif + switch (ext->alg) { + case IW_ENCODE_ALG_WEP: + alg = "WEP"; + module = "ieee80211_crypt_wep"; + break; + case IW_ENCODE_ALG_TKIP: + alg = "TKIP"; + module = "ieee80211_crypt_tkip"; + break; + case IW_ENCODE_ALG_CCMP: + alg = "CCMP"; + module = "ieee80211_crypt_ccmp"; + break; + default: + IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", + dev->name, ext->alg); + ret = -EINVAL; + goto done; + } + printk("alg name:%s\n",alg); + + ops = ieee80211_get_crypto_ops(alg); + if (ops == NULL) { + request_module(module); + ops = ieee80211_get_crypto_ops(alg); + } + if (ops == NULL) { + IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", + dev->name, ext->alg); + printk("========>unknown crypto alg %d\n", ext->alg); + ret = -EINVAL; + goto done; + } + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct ieee80211_crypt_data *new_crypt; + + ieee80211_crypt_delayed_deinit(ieee, crypt); + + new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + new_crypt->ops = ops; + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(idx); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + ret = -EINVAL; + goto done; + } + *crypt = new_crypt; + + } + //I need to deinit other crypt here in mesh mode instead deinit them while use them to tx&rx. +#ifdef _RTL8187_EXT_PATCH_ + if (ieee->iw_mode == ieee->iw_ext_mode) + { + int j; + for (j=1; jcryptlist[j]->crypt[idx]; + if (*crypttmp == NULL) + break; + if (*crypttmp && (*crypttmp)->ops != ops) + ieee80211_crypt_delayed_deinit(ieee, crypttmp); + } + } +#endif + if (ext->key_len > 0 && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, + (*crypt)->priv) < 0) { + IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name); + printk("key setting failed\n"); + ret = -EINVAL; + goto done; + } +#if 1 +// skip_host_crypt: + //printk("skip_host_crypt:ext_flags:%x\n", ext->ext_flags); + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + ieee->tx_keyidx = idx; + sec.active_key = idx; + sec.flags |= SEC_ACTIVE_KEY; + } + + if (ext->alg != IW_ENCODE_ALG_NONE) { + memcpy(sec.keys[idx], ext->key, ext->key_len); + sec.key_sizes[idx] = ext->key_len; + sec.flags |= (1 << idx); + if (ext->alg == IW_ENCODE_ALG_WEP) { + // sec.encode_alg[idx] = SEC_ALG_WEP; + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } else if (ext->alg == IW_ENCODE_ALG_TKIP) { + // sec.encode_alg[idx] = SEC_ALG_TKIP; + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_2; + } else if (ext->alg == IW_ENCODE_ALG_CCMP) { + // sec.encode_alg[idx] = SEC_ALG_CCMP; + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_3; + } + /* Don't set sec level for group keys. */ + if (group_key) + sec.flags &= ~SEC_LEVEL; + } +#endif +done: + if (ieee->set_security) + ieee->set_security(ieee->dev, &sec); + + if (ieee->reset_on_keychange && + ieee->iw_mode != IW_MODE_INFRA && + ieee->reset_port && ieee->reset_port(dev)) { + IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name); + return -EINVAL; + } + + return ret; +} +int ieee80211_wx_set_mlme(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_mlme *mlme = (struct iw_mlme *) extra; +// printk("\ndkgadfslkdjgalskdf===============>%s(), cmd:%x\n", __FUNCTION__, mlme->cmd); +#if 1 + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + case IW_MLME_DISASSOC: + // printk("disassoc now\n"); + ieee80211_disassociate(ieee); + break; + default: + return -EOPNOTSUPP; + } +#endif + return 0; +} + +int ieee80211_wx_set_auth(struct ieee80211_device *ieee, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ +/* + struct ieee80211_security sec = { + .flags = SEC_AUTH_MODE, + } +*/ + //printk("set auth:flag:%x, data value:%x\n", data->flags, data->value); + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + /*need to support wpa2 here*/ + //printk("wpa version:%x\n", data->value); + break; + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * * Host AP driver does not use these parameters and allows + * * wpa_supplicant to control them internally. + * */ + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + ieee->tkip_countermeasures = data->value; + break; + case IW_AUTH_DROP_UNENCRYPTED: + ieee->drop_unencrypted = data->value; + break; + + case IW_AUTH_80211_AUTH_ALG: + ieee->open_wep = (data->value&IW_AUTH_ALG_OPEN_SYSTEM)?1:0; + //printk("open_wep:%d\n", ieee->open_wep); + break; + +#if 1 + case IW_AUTH_WPA_ENABLED: + ieee->wpa_enabled = (data->value)?1:0; + //printk("enalbe wpa:%d\n", ieee->wpa_enabled); + break; + +#endif + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + ieee->ieee802_1x = data->value; + break; + case IW_AUTH_PRIVACY_INVOKED: + ieee->privacy_invoked = data->value; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +#if 1 +int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len) +{ +#if 0 + printk("====>%s()\n", __FUNCTION__); + { + int i; + for (i=0; iMAX_WPA_IE_LEN || (len && ie == NULL)) + { + // printk("return error out, len:%d\n", len); + return -EINVAL; + } + if (len) + { + + if (len != ie[1]+2) printk("len:%d, ie:%d\n", (int)len, ie[1]); + buf = kmalloc(len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + memcpy(buf, ie, len); + kfree(ieee->wpa_ie); + ieee->wpa_ie = buf; + ieee->wpa_ie_len = len; + } + else{ + if (ieee->wpa_ie) + kfree(ieee->wpa_ie); + ieee->wpa_ie = NULL; + ieee->wpa_ie_len = 0; + } +// printk("<=====out %s()\n", __FUNCTION__); + + return 0; + +} +#endif + +EXPORT_SYMBOL(ieee80211_wx_set_gen_ie); +EXPORT_SYMBOL(ieee80211_wx_set_mlme); +EXPORT_SYMBOL(ieee80211_wx_set_auth); +EXPORT_SYMBOL(ieee80211_wx_set_encode_ext); +EXPORT_SYMBOL(ieee80211_wx_get_scan); +EXPORT_SYMBOL(ieee80211_wx_set_encode); +EXPORT_SYMBOL(ieee80211_wx_get_encode); +#if 0 +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_scan); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_set_encode); +EXPORT_SYMBOL_NOVERS(ieee80211_wx_get_encode); +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/internal.h b/drivers/net/wireless/rtl8187b/ieee80211/internal.h new file mode 100644 index 0000000..189f285 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/internal.h @@ -0,0 +1,115 @@ +/* + * Cryptographic API. + * + * Copyright (c) 2002 James Morris + * + * 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 _CRYPTO_INTERNAL_H +#define _CRYPTO_INTERNAL_H + + +//#include +#include "rtl_crypto.h" +#include +#include +#include +#include +#include +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)) +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +static inline void cond_resched(void) +{ + if (need_resched()) { + set_current_state(TASK_RUNNING); + schedule(); + } +} +#endif + +extern enum km_type crypto_km_types[]; + +static inline enum km_type crypto_kmap_type(int out) +{ + return crypto_km_types[(in_softirq() ? 2 : 0) + out]; +} + +static inline void *crypto_kmap(struct page *page, int out) +{ + return kmap_atomic(page, crypto_kmap_type(out)); +} + +static inline void crypto_kunmap(void *vaddr, int out) +{ + kunmap_atomic(vaddr, crypto_kmap_type(out)); +} + +static inline void crypto_yield(struct crypto_tfm *tfm) +{ + if (!in_softirq()) + cond_resched(); +} + +static inline void *crypto_tfm_ctx(struct crypto_tfm *tfm) +{ + return (void *)&tfm[1]; +} + +struct crypto_alg *crypto_alg_lookup(const char *name); + +#ifdef CONFIG_KMOD +void crypto_alg_autoload(const char *name); +struct crypto_alg *crypto_alg_mod_lookup(const char *name); +#else +static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name) +{ + return crypto_alg_lookup(name); +} +#endif + +#ifdef CONFIG_CRYPTO_HMAC +int crypto_alloc_hmac_block(struct crypto_tfm *tfm); +void crypto_free_hmac_block(struct crypto_tfm *tfm); +#else +static inline int crypto_alloc_hmac_block(struct crypto_tfm *tfm) +{ + return 0; +} + +static inline void crypto_free_hmac_block(struct crypto_tfm *tfm) +{ } +#endif + +#ifdef CONFIG_PROC_FS +void __init crypto_init_proc(void); +#else +static inline void crypto_init_proc(void) +{ } +#endif + +int crypto_init_digest_flags(struct crypto_tfm *tfm, u32 flags); +int crypto_init_cipher_flags(struct crypto_tfm *tfm, u32 flags); +int crypto_init_compress_flags(struct crypto_tfm *tfm, u32 flags); + +int crypto_init_digest_ops(struct crypto_tfm *tfm); +int crypto_init_cipher_ops(struct crypto_tfm *tfm); +int crypto_init_compress_ops(struct crypto_tfm *tfm); + +void crypto_exit_digest_ops(struct crypto_tfm *tfm); +void crypto_exit_cipher_ops(struct crypto_tfm *tfm); +void crypto_exit_compress_ops(struct crypto_tfm *tfm); + +#endif /* _CRYPTO_INTERNAL_H */ + diff --git a/drivers/net/wireless/rtl8187b/ieee80211/kmap_types.h b/drivers/net/wireless/rtl8187b/ieee80211/kmap_types.h new file mode 100644 index 0000000..de67bb0 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/kmap_types.h @@ -0,0 +1,20 @@ +#ifndef __KMAP_TYPES_H + +#define __KMAP_TYPES_H + + +enum km_type { + KM_BOUNCE_READ, + KM_SKB_SUNRPC_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_BH_IRQ, + KM_SOFTIRQ0, + KM_SOFTIRQ1, + KM_TYPE_NR +}; + +#define _ASM_KMAP_TYPES_H + +#endif diff --git a/drivers/net/wireless/rtl8187b/ieee80211/readme b/drivers/net/wireless/rtl8187b/ieee80211/readme new file mode 100644 index 0000000..bc04ffb --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/readme @@ -0,0 +1,162 @@ +What this layer should do + +- It mantain the old mechanism as alternative, so the + ipw2100 driver works with really few changes. +- Encapsulate / Decapsulate ieee80211 packet +- Handle fragmentation +- Optionally provide an alterantive mechanism for netif queue stop/wake, + so that the ieee80211 layer will pass one fragment per time instead of + one txb struct per time. so the driver can stop the queue in the middle + of a packet. +- Provide two different TX interfaces for cards that can handle management + frames on one HW queue, and data on another, and for cards that have only + one HW queue (the latter untested and very, very rough). +- Optionally provide the logic for handling IBSS/MASTER/MONITOR/BSS modes + and for the channel, essid and wap get/set wireless extension requests. + so that the driver has only to change channel when the ieee stack tell it. +- Optionally provide a scanning mechanism so that the driver has not to + worry about this, just implement the set channel calback and pass + frames to the upper layer +- Optionally provide the bss client protocol handshaking (just with open + authentication) +- Optionally provide the probe request send mechanism +- Optionally provide the bss master mode logic to handle association + protocol (only open authentication) and probe responses. +- SW wep encryption (with open authentication) +- It collects some stats +- It provides beacons to the card when it ask for them + +What this layer doesn't do (yet) +- Perform shared authentication +- Have full support for master mode (the AP should loop back in the air + frames from an associated client to another. This could be done easily + with few lines of code, and it is done in my previous version of the + stach, but a table of association must be keept and a disassociation + policy must be decided and implemented. +- Handle cleanly the full ieee 802.11 protocol. In AP mode it never + disassociate clients, and it is really prone to always allow access. + In bss client mode it is a bit rough with AP deauth and disassoc requests. +- It has not any entry point to view the collected stats. +- Altought it takes care of the card supported rates in the management frame + it sends, support for rate changing on TXed packet is not complete. +- Give up once associated in bss client mode (it never detect a + signal loss condition to disassociate and restart scanning) +- Provide a mechanism for enabling the TX in monitor mode, so + userspace programs can TX raw packets. +- Provide a mechanism for cards that need that the SW take care of beacon + TX completely, in sense that the SW has to enqueue by itself beacons + to the card so it TX them (if any...) +APIs + +Callback functions in the original stack has been mantained. +following has been added (from ieee80211.h) + + /* Softmac-generated frames (mamagement) are TXed via this + * callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is + * not set. As some cards may have different HW queues that + * one might want to use for data and management frames + * the option to have two callbacks might be useful. + * This fucntion can't sleep. + */ + int (*softmac_hard_start_xmit)(struct sk_buff *skb, + struct net_device *dev); + + /* used instead of hard_start_xmit (not softmac_hard_start_xmit) + * if the IEEE_SOFTMAC_TX_QUEUE feature is used to TX data + * frames. I the option IEEE_SOFTMAC_SINGLE_QUEUE is also set + * then also management frames are sent via this callback. + * This function can't sleep. + */ + void (*softmac_data_hard_start_xmit)(struct sk_buff *skb, + struct net_device *dev); + + /* stops the HW queue for DATA frames. Useful to avoid + * waste time to TX data frame when we are reassociating + * This function can sleep. + */ + void (*data_hard_stop)(struct net_device *dev); + + /* OK this is complementar to data_poll_hard_stop */ + void (*data_hard_resume)(struct net_device *dev); + + /* ask to the driver to retune the radio . + * This function can sleep. the driver should ensure + * the radio has been swithced before return. + */ + void (*set_chan)(struct net_device *dev,short ch); + + /* These are not used if the ieee stack takes care of + * scanning (IEEE_SOFTMAC_SCAN feature set). + * In this case only the set_chan is used. + * + * The syncro version is similar to the start_scan but + * does not return until all channels has been scanned. + * this is called in user context and should sleep, + * it is called in a work_queue when swithcing to ad-hoc mode + * or in behalf of iwlist scan when the card is associated + * and root user ask for a scan. + * the fucntion stop_scan should stop both the syncro and + * background scanning and can sleep. + * The fucntion start_scan should initiate the background + * scanning and can't sleep. + */ + void (*scan_syncro)(struct net_device *dev); + void (*start_scan)(struct net_device *dev); + void (*stop_scan)(struct net_device *dev); + + /* indicate the driver that the link state is changed + * for example it may indicate the card is associated now. + * Driver might be interested in this to apply RX filter + * rules or simply light the LINK led + */ + void (*link_change)(struct net_device *dev); + +Functions hard_data_[resume/stop] are optional and should not be used +if the driver decides to uses data+management frames enqueue in a +single HQ queue (thus using just the softmac_hard_data_start_xmit +callback). + +Function that the driver can use are: + +ieee80211_get_beacon - this is called by the driver when + the HW needs a beacon. +ieee80211_softmac_start_protocol - this should normally be called in the + driver open function +ieee80211_softmac_stop_protocol - the opposite of the above +ieee80211_wake_queue - this is similar to netif_wake_queue +ieee80211_reset_queue - this throw away fragments pending(if any) +ieee80211_stop_queue - this is similar to netif_stop_queue + + +known BUGS: +- When performing syncro scan (possiblily when swithcing to ad-hoc mode + and when running iwlist scan when associated) there is still an odd + behaviour.. I have not looked in this more accurately (yet). + +locking: +locking is done by means of three structures. +1- ieee->lock (by means of spin_[un]lock_irq[save/restore] +2- ieee->wx_sem +3- ieee->scan_sem + +the lock 1 is what protect most of the critical sections in the ieee stack. +the lock 2 is used to avoid that more than one of the SET wireless extension +handlers (as well as start/stop protocol function) are running at the same time. +the lock 1 is used when we need to modify or read the shared data in the wx handlers. +In other words the lock 2 will prevent one SET action will run across another SET +action (by make sleep the 2nd one) but allow GET actions, while the lock 1 +make atomic those little shared data access in both GET and SET operation. +So get operation will be never be delayed really: they will never sleep.. +Furthermore in the top of some SET operations a flag is set before acquiring +the lock. This is an help to make the previous running SET operation to +finish faster if needed (just in case the second one will totally undo the +first, so there is not need to complete the 1st really.. ). +The background scanning mechaninsm is protected by the lock 1 except for the +workqueue. this wq is here just to let the set_chan callback sleep (I thinked it +might be appreciated by USB network card driver developer). In this case the lock 3 +take its turn. +Thus the stop function needs both the locks. +Funny in the syncro scan the lock 2 play its role (as both the syncro_scan +function and the stop scan function are called with this semaphore held). + + diff --git a/drivers/net/wireless/rtl8187b/ieee80211/rtl8187_mesh.h b/drivers/net/wireless/rtl8187b/ieee80211/rtl8187_mesh.h new file mode 100644 index 0000000..80f23ea --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/rtl8187_mesh.h @@ -0,0 +1,282 @@ +#ifndef _RTL8187_MESH_H_ +#define _RTL8187_MESH_H_ + +#include "msh_class.h" // struct mshclass +#include "mesh.h" // struct MESH-Neighbor-Entry +#include "ieee80211.h" // struct ieee80211-network +#include "mesh_8185_util.h" // DOT11-QUEUE +#include "hash_table.h" // hash-table +#include "8185s_pathsel.h" +#include + +#define GET_MESH_PRIV(x) ((struct mshclass_priv *)(x->priv)) + +struct ieee80211_hdr_mesh { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; + unsigned char mesh_flag; + INT8 TTL; + UINT16 segNum; + unsigned char DestMACAddr[ETH_ALEN]; // modify for 6 address + unsigned char SrcMACAddr[ETH_ALEN]; +} __attribute__ ((packed)); + +struct myMeshIDNode { + struct list_head list; + char id[MESH_ID_LEN+1]; + short popEN; + char tried; + unsigned long expire; + struct ieee80211_network mesh_network; +}; + +struct ieee80211_hdr_mesh_QOS { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; + u16 QOS_ctl; + unsigned char mesh_flag; + INT8 TTL; + UINT16 segNum; + unsigned char DestMACAddr[ETH_ALEN]; // modify for 6 address + unsigned char SrcMACAddr[ETH_ALEN]; +} __attribute__ ((packed)); + + +struct mesh_PeerEntry { + // based on 8185ag.h + struct list_head hash_list; + unsigned int used; ///< used == TRUE => has been allocated, \n used == FALSE => can be allocated + unsigned char hwaddr[MACADDRLEN]; ///< hardware address + + // struct list_head mesh_unEstablish_ptr; // ©|¥¼(©Î±q¤w³s½u -> ¥¼³s½u) ¤§ MP list + struct list_head mesh_mp_ptr; // MP list + + /*mesh_neighbor: + * Inited by "Neighbor Discovering" + * cleaned by "Disassociation" or "Expired" + */ + struct MESH_Neighbor_Entry mesh_neighbor_TBL; + + struct ieee80211_network * pstat; // a backward pointer + + // 802.11 seq checking + u16 last_rxseq; /* rx seq previous per-tid */ + u16 last_rxfrag;/* tx frag previous per-tid */ + unsigned long last_time; + // +}; + + +struct mshclass_priv { + + struct mesh_PeerEntry *meshEntries; // 1-to-1 for priv->ieee80211->networks + + spinlock_t lock_stainfo; // lock for accessing the data structure of stat info + spinlock_t lock_queue; // lock for DOT11_EnQueue2/DOT11_DeQueue2/enque/dequeue + spinlock_t lock_Rreq; // lock for rreq_retry. Some function like aodv_expire/tx use lock_queue simultaneously +// spinlock_t lock_meshlist; + + // struct _DOT11_QUEUE *pevent_queue; ///< 802.11 QUEUEµ²ºc + // struct hash_table *pathsel_table; // GANTOE + //tsananiu + struct _DOT11_QUEUE *pathsel_queue; ///< 802.11 QUEUEµ²ºc + + //tsananiu end + + //add by shlu 20070518 + unsigned char RreqMAC[AODV_RREQ_TABLE_SIZE][6]; + unsigned int RreqBegin; + unsigned int RreqEnd; + +#if defined(MESH_ROLE_ROOT) || defined(MESH_ROLE_PORTAL) +#define MAX_SZ_BAD_MAC 3 + unsigned char BadMac[MAX_SZ_BAD_MAC][MACADDRLEN]; + int idx_BadMac; +#endif // MESH_ROLE_ROOT || MESH_ROLE_PORTAL + + //------------- + unsigned char root_mac[MACADDRLEN]; + struct mesh_info dot11MeshInfo; // extrated from wifi_mib (ieee802_mib.h) + struct hash_table *proxy_table, *mesh_rreq_retry_queue; //GANTOE //GANTOE + struct hash_table *pathsel_table; // add by chuangch 2007.09.13 + // add by Jason + struct mpp_tb *pann_mpp_tb; + + struct timer_list expire_timer; // 1sec timer + + struct timer_list beacon_timer; // 1sec timer + struct list_head stat_hash[MAX_NETWORK_COUNT]; // sta_info hash table (aid_obj) + + struct list_head meshList[MAX_CHANNEL_NUMBER]; + int scanMode; + + struct { + struct ieee80211_network *pstat; + unsigned char hwaddr[MACADDRLEN]; + } stainfo_cache; + + // The following elements are used by 802.11s. + // For copyright-pretection, we use an independent (binary) module. + // Note that it can also be put either under r8180_priv or ieee80211_device. The adv of put under + // r8180_priv is to get "higher encapsulation". On the other hand, r8180_priv was originally designed + // for "hardward specific." + char mesh_mac_filter_allow[8][13]; + char mesh_mac_filter_deny[8][13]; + + struct MESH_Share meshare; // mesh share data + + struct { + + int prev_iw_mode; // Save this->iw_mode for r8180_wx->r8180_wx_enable_mesh. No init requirement + + struct MESH_Profile mesh_profile; // contains MESHID + + struct mesh_info dot11MeshInfo; // contains meshMaxAssocNum + + struct net_device_stats mesh_stats; + + UINT8 mesh_Version; // ¨Ï¥Îªºª©¥» + // WLAN Mesh Capability + INT16 mesh_PeerCAP_cap; // peer capability-Cap number (¦³¸¹¼Æ) + UINT8 mesh_PeerCAP_flags; // peer capability-flags + UINT8 mesh_PowerSaveCAP; // Power Save capability + UINT8 mesh_SyncCAP; // Synchronization capability + UINT8 mesh_MDA_CAP; // MDA capability + UINT32 mesh_ChannelPrecedence; // Channel Precedence + + // neighbor -> candidate neighbor, if mesh_available_peerlink > 0, page 56, D0.02 + UINT8 mesh_AvailablePeerLink; // ¦¹¬O§_¦³»Ý­n?(¦]¥¦µ¥¦P©ó mesh_PeerCAP)=>¼È ®É«O ¯d + + UINT8 mesh_HeaderFlags; // mesh header ¤ºªº mesh flags field + + // MKD domain element [MKDDIE] + UINT8 mesh_MKD_DomainID[6]; + UINT8 mesh_MKDDIE_SecurityConfiguration; + + // EMSA Handshake element [EMSAIE] + UINT8 mesh_EMSAIE_ANonce[32]; + UINT8 mesh_EMSAIE_SNonce[32]; + UINT8 mesh_EMSAIE_MA_ID[6]; + UINT16 mesh_EMSAIE_MIC_Control; + UINT8 mesh_EMSAIE_MIC[16]; + + struct timer_list mesh_peer_link_timer; ///< ¹ï©|¥¼³s ½u(»P³s½u°h¦Ü¥¼³s½u) MP mesh_unEstablish_hdr §@ peer link time out + +// struct timer_list mesh_beacon_timer; + // mesh_unEstablish_hdr: + // It is a list structure, only stores unEstablish (or Establish -> unEstablish [MP_HOLDING])MP entry + // Each entry is a pointer pointing to an entry in "stat_info->mesh_mp_ptr" + // and removed by successful "Peer link setup" or "Expired" + struct list_head mesh_unEstablish_hdr; + + // mesh_mp_hdr: + // It is a list of MP/MAP/MPP who has already passed "Peer link setup" + // Each entry is a pointer pointing to an entry in "stat_info->mesh_mp_ptr" + // Every entry is inserted by "successful peer link setup" + // and removed by "Expired" + struct list_head mesh_mp_hdr; + + } mesh; + + int iCurChannel; // remember the working channel +}; + +// Stanley, 04/23/07 +// The following mode is used by ieee80211_device->iw_mode +// Although it is better to put the definition under linux/wireless.h (or wireless_copy.h), it is a system file +// that we shouldn't modify directly. +#define IW_MODE_MESH 11 /* 802.11s mesh mode */ + +// Default MESHID +#define IEEE80211S_DEFAULT_MESHID "802.11s" + +// callback for 802.11s +extern short rtl8187_patch_ieee80211_probe_req_1 (struct ieee80211_device *ieee); +extern u8* rtl8187_patch_ieee80211_probe_req_2 (struct ieee80211_device *ieee, struct sk_buff *skb, u8 *tag); + +// wx +extern int rtl8187_patch_r8180_wx_get_meshinfo(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_enable_mesh(struct net_device *dev); +extern int rtl8187_patch_r8180_wx_disable_mesh(struct net_device *dev); +extern int rtl8187_patch_r8180_wx_wx_set_meshID(struct net_device *dev, char *ext,unsigned char channel); +extern void rtl8187_patch_r8180_wx_set_channel (struct ieee80211_device *ieee, int ch); +extern int rtl8187_patch_r8180_wx_set_add_mac_allow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_set_del_mac_allow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_set_add_mac_deny(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_set_del_mac_deny(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_get_mac_allow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_get_mac_deny(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + +extern int rtl8187_patch_r8180_wx_get_mesh_list(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_mesh_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int rtl8187_patch_r8180_wx_get_selected_mesh(struct net_device *dev, int en, char *cho, char* id); +//by amy for networkmanager UI +extern int rtl8187_patch_r8180_wx_get_selected_mesh_channel(struct net_device *dev, char *extmeshid, char *cho); +//by amy for networkmanager UI +// osdep +extern int rtl8187_patch_ieee80211_start_protocol (struct ieee80211_device *ieee); +extern u8 rtl8187_patch_rtl8180_up(struct mshclass *priv); +extern void rtl8187_patch_ieee80211_stop_protocol(struct ieee80211_device *ieee); + +// issue_assocreq_MP +extern void rtl8187_patch_ieee80211_association_req_1 (struct ieee80211_assoc_request_frame *hdr); +extern u8* rtl8187_patch_ieee80211_association_req_2 (struct ieee80211_device *ieee, struct ieee80211_network *pstat, struct sk_buff *skb); + +// OnAssocReq_MP +extern int rtl8187_patch_ieee80211_rx_frame_softmac_on_assoc_req (struct ieee80211_device *ieee, struct sk_buff *skb); + +// issue_assocrsp_MP +extern void rtl8187_patch_ieee80211_assoc_resp_by_net_1 (struct ieee80211_assoc_response_frame *assoc); +u8* rtl8187_patch_ieee80211_assoc_resp_by_net_2 (struct ieee80211_device *ieee, struct ieee80211_network *pstat, int pkt_type, struct sk_buff *skb); + +// OnAssocRsp_MP +extern int rtl8187_patch_ieee80211_rx_frame_softmac_on_assoc_rsp (struct ieee80211_device *ieee, struct sk_buff *skb); + + +extern int rtl8187_patch_ieee80211_rx_frame_softmac_on_auth(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); +extern int rtl8187_patch_ieee80211_rx_frame_softmac_on_deauth(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); +extern unsigned int rtl8187_patch_ieee80211_process_probe_response_1( struct ieee80211_device *ieee, struct ieee80211_probe_response *beacon, struct ieee80211_rx_stats *stats); +extern void rtl8187_patch_ieee80211_rx_mgt_on_probe_req( struct ieee80211_device *ieee, struct ieee80211_probe_request *beacon, struct ieee80211_rx_stats *stats); +extern void rtl8187_patch_ieee80211_rx_mgt_update_expire ( struct ieee80211_device *ieee, struct sk_buff *skb); + +// set channel +extern int rtl8187_patch_ieee80211_ext_stop_scan_wq_set_channel (struct ieee80211_device *ieee); + +// on rx (rx isr) +extern int rtl8187_patch_ieee80211_rx_on_rx (struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats, u16 type, u16 stype); + +// r8187_core +// handle ioctl +extern int rtl8187_patch_rtl8180_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +// create proc +extern void rtl8187_patch_create_proc(struct r8180_priv *priv); +extern void rtl8187_patch_remove_proc(struct r8180_priv *priv); + +// tx, xmit +// locked by ieee->lock. Call ieee80211_softmac_xmit afterward +extern struct ieee80211_txb* rtl8187_patch_ieee80211_xmit (struct sk_buff *skb, struct net_device *dev); + +// given a skb, output header's length +extern int rtl8187_patch_ieee80211_rx_frame_get_hdrlen (struct ieee80211_device *ieee, struct sk_buff *skb); + +// check the frame control field, return 0: not accept, 1: accept +extern int rtl8187_patch_ieee80211_rx_is_valid_framectl (struct ieee80211_device *ieee, u16 fc, u16 type, u16 stype); + +// process_dataframe +extern int rtl8187_patch_ieee80211_rx_process_dataframe (struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); + +extern int rtl8187_patch_is_duplicate_packet (struct ieee80211_device *ieee, struct ieee80211_hdr *header, u16 type, u16 stype); + +extern int rtl8187_patch_ieee80211_softmac_xmit_get_rate (struct ieee80211_device *ieee, struct sk_buff *skb); +extern void ieee80211_start_mesh(struct ieee80211_device *ieee); +#endif // _RTL8187_MESH_H_ diff --git a/drivers/net/wireless/rtl8187b/ieee80211/rtl_crypto.h b/drivers/net/wireless/rtl8187b/ieee80211/rtl_crypto.h new file mode 100644 index 0000000..82fa2b7 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/rtl_crypto.h @@ -0,0 +1,399 @@ +/* + * Scatterlist Cryptographic API. + * + * Copyright (c) 2002 James Morris + * Copyright (c) 2002 David S. Miller (davem@redhat.com) + * + * Portions derived from Cryptoapi, by Alexander Kjeldaas + * and Nettle, by Niels Mé°ˆler. + * + * 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 _LINUX_CRYPTO_H +#define _LINUX_CRYPTO_H + +#include +#include +#include +#include +#include +#include +#include + +#define crypto_register_alg crypto_register_alg_rtl +#define crypto_unregister_alg crypto_unregister_alg_rtl +#define crypto_alloc_tfm crypto_alloc_tfm_rtl +#define crypto_free_tfm crypto_free_tfm_rtl +#define crypto_alg_available crypto_alg_available_rtl + +/* + * Algorithm masks and types. + */ +#define CRYPTO_ALG_TYPE_MASK 0x000000ff +#define CRYPTO_ALG_TYPE_CIPHER 0x00000001 +#define CRYPTO_ALG_TYPE_DIGEST 0x00000002 +#define CRYPTO_ALG_TYPE_COMPRESS 0x00000004 + +/* + * Transform masks and values (for crt_flags). + */ +#define CRYPTO_TFM_MODE_MASK 0x000000ff +#define CRYPTO_TFM_REQ_MASK 0x000fff00 +#define CRYPTO_TFM_RES_MASK 0xfff00000 + +#define CRYPTO_TFM_MODE_ECB 0x00000001 +#define CRYPTO_TFM_MODE_CBC 0x00000002 +#define CRYPTO_TFM_MODE_CFB 0x00000004 +#define CRYPTO_TFM_MODE_CTR 0x00000008 + +#define CRYPTO_TFM_REQ_WEAK_KEY 0x00000100 +#define CRYPTO_TFM_RES_WEAK_KEY 0x00100000 +#define CRYPTO_TFM_RES_BAD_KEY_LEN 0x00200000 +#define CRYPTO_TFM_RES_BAD_KEY_SCHED 0x00400000 +#define CRYPTO_TFM_RES_BAD_BLOCK_LEN 0x00800000 +#define CRYPTO_TFM_RES_BAD_FLAGS 0x01000000 + +/* + * Miscellaneous stuff. + */ +#define CRYPTO_UNSPEC 0 +#define CRYPTO_MAX_ALG_NAME 64 + +struct scatterlist; + +/* + * Algorithms: modular crypto algorithm implementations, managed + * via crypto_register_alg() and crypto_unregister_alg(). + */ +struct cipher_alg { + unsigned int cia_min_keysize; + unsigned int cia_max_keysize; + int (*cia_setkey)(void *ctx, const u8 *key, + unsigned int keylen, u32 *flags); + void (*cia_encrypt)(void *ctx, u8 *dst, const u8 *src); + void (*cia_decrypt)(void *ctx, u8 *dst, const u8 *src); +}; + +struct digest_alg { + unsigned int dia_digestsize; + void (*dia_init)(void *ctx); + void (*dia_update)(void *ctx, const u8 *data, unsigned int len); + void (*dia_final)(void *ctx, u8 *out); + int (*dia_setkey)(void *ctx, const u8 *key, + unsigned int keylen, u32 *flags); +}; + +struct compress_alg { + int (*coa_init)(void *ctx); + void (*coa_exit)(void *ctx); + int (*coa_compress)(void *ctx, const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen); + int (*coa_decompress)(void *ctx, const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen); +}; + +#define cra_cipher cra_u.cipher +#define cra_digest cra_u.digest +#define cra_compress cra_u.compress + +struct crypto_alg { + struct list_head cra_list; + u32 cra_flags; + unsigned int cra_blocksize; + unsigned int cra_ctxsize; + const char cra_name[CRYPTO_MAX_ALG_NAME]; + + union { + struct cipher_alg cipher; + struct digest_alg digest; + struct compress_alg compress; + } cra_u; + + struct module *cra_module; +}; + +/* + * Algorithm registration interface. + */ +int crypto_register_alg(struct crypto_alg *alg); +int crypto_unregister_alg(struct crypto_alg *alg); + +/* + * Algorithm query interface. + */ +int crypto_alg_available(const char *name, u32 flags); + +/* + * Transforms: user-instantiated objects which encapsulate algorithms + * and core processing logic. Managed via crypto_alloc_tfm() and + * crypto_free_tfm(), as well as the various helpers below. + */ +struct crypto_tfm; + +struct cipher_tfm { + void *cit_iv; + unsigned int cit_ivsize; + u32 cit_mode; + int (*cit_setkey)(struct crypto_tfm *tfm, + const u8 *key, unsigned int keylen); + int (*cit_encrypt)(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes); + int (*cit_encrypt_iv)(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes, u8 *iv); + int (*cit_decrypt)(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes); + int (*cit_decrypt_iv)(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes, u8 *iv); + void (*cit_xor_block)(u8 *dst, const u8 *src); +}; + +struct digest_tfm { + void (*dit_init)(struct crypto_tfm *tfm); + void (*dit_update)(struct crypto_tfm *tfm, + struct scatterlist *sg, unsigned int nsg); + void (*dit_final)(struct crypto_tfm *tfm, u8 *out); + void (*dit_digest)(struct crypto_tfm *tfm, struct scatterlist *sg, + unsigned int nsg, u8 *out); + int (*dit_setkey)(struct crypto_tfm *tfm, + const u8 *key, unsigned int keylen); +#ifdef CONFIG_CRYPTO_HMAC + void *dit_hmac_block; +#endif +}; + +struct compress_tfm { + int (*cot_compress)(struct crypto_tfm *tfm, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen); + int (*cot_decompress)(struct crypto_tfm *tfm, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen); +}; + +#define crt_cipher crt_u.cipher +#define crt_digest crt_u.digest +#define crt_compress crt_u.compress + +struct crypto_tfm { + + u32 crt_flags; + + union { + struct cipher_tfm cipher; + struct digest_tfm digest; + struct compress_tfm compress; + } crt_u; + + struct crypto_alg *__crt_alg; +}; + +/* + * Transform user interface. + */ + +/* + * crypto_alloc_tfm() will first attempt to locate an already loaded algorithm. + * If that fails and the kernel supports dynamically loadable modules, it + * will then attempt to load a module of the same name or alias. A refcount + * is grabbed on the algorithm which is then associated with the new transform. + * + * crypto_free_tfm() frees up the transform and any associated resources, + * then drops the refcount on the associated algorithm. + */ +struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags); +void crypto_free_tfm(struct crypto_tfm *tfm); + +/* + * Transform helpers which query the underlying algorithm. + */ +static inline const char *crypto_tfm_alg_name(struct crypto_tfm *tfm) +{ + return tfm->__crt_alg->cra_name; +} + +static inline const char *crypto_tfm_alg_modname(struct crypto_tfm *tfm) +{ + struct crypto_alg *alg = tfm->__crt_alg; + + if (alg->cra_module) + return alg->cra_module->name; + else + return NULL; +} + +static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm) +{ + return tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK; +} + +static inline unsigned int crypto_tfm_alg_min_keysize(struct crypto_tfm *tfm) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + return tfm->__crt_alg->cra_cipher.cia_min_keysize; +} + +static inline unsigned int crypto_tfm_alg_max_keysize(struct crypto_tfm *tfm) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + return tfm->__crt_alg->cra_cipher.cia_max_keysize; +} + +static inline unsigned int crypto_tfm_alg_ivsize(struct crypto_tfm *tfm) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + return tfm->crt_cipher.cit_ivsize; +} + +static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm) +{ + return tfm->__crt_alg->cra_blocksize; +} + +static inline unsigned int crypto_tfm_alg_digestsize(struct crypto_tfm *tfm) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); + return tfm->__crt_alg->cra_digest.dia_digestsize; +} + +/* + * API wrappers. + */ +static inline void crypto_digest_init(struct crypto_tfm *tfm) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); + tfm->crt_digest.dit_init(tfm); +} + +static inline void crypto_digest_update(struct crypto_tfm *tfm, + struct scatterlist *sg, + unsigned int nsg) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); + tfm->crt_digest.dit_update(tfm, sg, nsg); +} + +static inline void crypto_digest_final(struct crypto_tfm *tfm, u8 *out) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); + tfm->crt_digest.dit_final(tfm, out); +} + +static inline void crypto_digest_digest(struct crypto_tfm *tfm, + struct scatterlist *sg, + unsigned int nsg, u8 *out) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); + tfm->crt_digest.dit_digest(tfm, sg, nsg, out); +} + +static inline int crypto_digest_setkey(struct crypto_tfm *tfm, + const u8 *key, unsigned int keylen) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST); + if (tfm->crt_digest.dit_setkey == NULL) + return -ENOSYS; + return tfm->crt_digest.dit_setkey(tfm, key, keylen); +} + +static inline int crypto_cipher_setkey(struct crypto_tfm *tfm, + const u8 *key, unsigned int keylen) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + return tfm->crt_cipher.cit_setkey(tfm, key, keylen); +} + +static inline int crypto_cipher_encrypt(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + return tfm->crt_cipher.cit_encrypt(tfm, dst, src, nbytes); +} + +static inline int crypto_cipher_encrypt_iv(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes, u8 *iv) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + BUG_ON(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB); + return tfm->crt_cipher.cit_encrypt_iv(tfm, dst, src, nbytes, iv); +} + +static inline int crypto_cipher_decrypt(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + return tfm->crt_cipher.cit_decrypt(tfm, dst, src, nbytes); +} + +static inline int crypto_cipher_decrypt_iv(struct crypto_tfm *tfm, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes, u8 *iv) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + BUG_ON(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB); + return tfm->crt_cipher.cit_decrypt_iv(tfm, dst, src, nbytes, iv); +} + +static inline void crypto_cipher_set_iv(struct crypto_tfm *tfm, + const u8 *src, unsigned int len) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + memcpy(tfm->crt_cipher.cit_iv, src, len); +} + +static inline void crypto_cipher_get_iv(struct crypto_tfm *tfm, + u8 *dst, unsigned int len) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER); + memcpy(dst, tfm->crt_cipher.cit_iv, len); +} + +static inline int crypto_comp_compress(struct crypto_tfm *tfm, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMPRESS); + return tfm->crt_compress.cot_compress(tfm, src, slen, dst, dlen); +} + +static inline int crypto_comp_decompress(struct crypto_tfm *tfm, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen) +{ + BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMPRESS); + return tfm->crt_compress.cot_decompress(tfm, src, slen, dst, dlen); +} + +/* + * HMAC support. + */ +#ifdef CONFIG_CRYPTO_HMAC +void crypto_hmac_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen); +void crypto_hmac_update(struct crypto_tfm *tfm, + struct scatterlist *sg, unsigned int nsg); +void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key, + unsigned int *keylen, u8 *out); +void crypto_hmac(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen, + struct scatterlist *sg, unsigned int nsg, u8 *out); +#endif /* CONFIG_CRYPTO_HMAC */ + +#endif /* _LINUX_CRYPTO_H */ + diff --git a/drivers/net/wireless/rtl8187b/ieee80211/scatterwalk.h b/drivers/net/wireless/rtl8187b/ieee80211/scatterwalk.h new file mode 100644 index 0000000..b164465 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/ieee80211/scatterwalk.h @@ -0,0 +1,51 @@ +/* + * Cryptographic API. + * + * Copyright (c) 2002 James Morris + * Copyright (c) 2002 Adam J. Richter + * Copyright (c) 2004 Jean-Luc Cooke + * + * 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 _CRYPTO_SCATTERWALK_H +#define _CRYPTO_SCATTERWALK_H +#include +#include + +struct scatter_walk { + struct scatterlist *sg; + struct page *page; + void *data; + unsigned int len_this_page; + unsigned int len_this_segment; + unsigned int offset; +}; + +/* Define sg_next is an inline routine now in case we want to change + scatterlist to a linked list later. */ +static inline struct scatterlist *sg_next(struct scatterlist *sg) +{ + return sg + 1; +} + +static inline int scatterwalk_samebuf(struct scatter_walk *walk_in, + struct scatter_walk *walk_out, + void *src_p, void *dst_p) +{ + return walk_in->page == walk_out->page && + walk_in->offset == walk_out->offset && + walk_in->data == src_p && walk_out->data == dst_p; +} + +void *scatterwalk_whichbuf(struct scatter_walk *walk, unsigned int nbytes, void *scratch); +void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg); +int scatterwalk_copychunks(void *buf, struct scatter_walk *walk, size_t nbytes, int out); +void scatterwalk_map(struct scatter_walk *walk, int out); +void scatterwalk_done(struct scatter_walk *walk, int out, int more); + +#endif /* _CRYPTO_SCATTERWALK_H */ diff --git a/drivers/net/wireless/rtl8187b/msh_class.h b/drivers/net/wireless/rtl8187b/msh_class.h new file mode 100644 index 0000000..ccaa939 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/msh_class.h @@ -0,0 +1,117 @@ +/*! \file msh_class.h + \brief msh CLASS extension + + \date 2007/5/2 + \author Stanley Chang +*/ + +#ifndef _MESH_CLASS_HDR_H_ +#define _MESH_CLASS_HDR_H_ + +#include /* ETH_ALEN */ +#include /* ARRAY_SIZE */ +#include +#include +#include +#include + +#include "ieee80211/ieee80211.h" // for struct ieee80211-xxxx +#include "r8187.h" // for struct r8180-priv + +#define MAC_TABLE_SIZE 8 + +struct mshclass { + struct r8180_priv * p8187; + + // callback functions + // ieee80211_softmac.c + int (*ext_patch_ieee80211_start_protocol) (struct ieee80211_device *ieee); // start special mode + + short (*ext_patch_ieee80211_probe_req_1) (struct ieee80211_device *ieee); // return = 0: no more phases, >0: another phase + u8* (*ext_patch_ieee80211_probe_req_2) (struct ieee80211_device *ieee, struct sk_buff *skb, u8 *tag); // return tag + + void (*ext_patch_ieee80211_association_req_1) (struct ieee80211_assoc_request_frame *hdr); + u8* (*ext_patch_ieee80211_association_req_2) (struct ieee80211_device *ieee, struct ieee80211_network *pstat, struct sk_buff *skb); + + int (*ext_patch_ieee80211_rx_frame_softmac_on_assoc_req) (struct ieee80211_device *ieee, struct sk_buff *skb); + int (*ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp) (struct ieee80211_device *ieee, struct sk_buff *skb); + + void (*ext_patch_ieee80211_stop_protocol) (struct ieee80211_device *ieee); // stop timer + + void (*ext_patch_ieee80211_assoc_resp_by_net_1) (struct ieee80211_assoc_response_frame *assoc); + u8* (*ext_patch_ieee80211_assoc_resp_by_net_2) (struct ieee80211_device *ieee, struct ieee80211_network *pstat, int pkt_type, struct sk_buff *skb); + + int (*ext_patch_ieee80211_ext_stop_scan_wq_set_channel) (struct ieee80211_device *ieee); + + struct sk_buff* (*ext_patch_get_beacon_get_probersp)(struct ieee80211_device *ieee, u8 *dest, struct ieee80211_network *net); + + int (*ext_patch_ieee80211_softmac_xmit_get_rate) (struct ieee80211_device *ieee, struct sk_buff *skb); + int (*ext_patch_ieee80211_rx_frame_softmac_on_auth)(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); + int (*ext_patch_ieee80211_rx_frame_softmac_on_deauth)(struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); +//by amy for mesh + void (*ext_patch_ieee80211_start_mesh)(struct ieee80211_device *ieee); +//by amy for mesh + /// r8180_wx.c + int (*ext_patch_r8180_wx_get_meshinfo) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_enable_mesh) (struct net_device *dev); + int (*ext_patch_r8180_wx_disable_mesh) (struct net_device *dev); + int (*ext_patch_r8180_wx_set_meshID) ( struct net_device *dev, char *ext); +//by amy for mesh + int (*ext_patch_r8180_wx_set_mesh_chan)(struct net_device *dev, unsigned char channel); +//by amy for mesh + void (*ext_patch_r8180_wx_set_channel) (struct ieee80211_device *ieee, int ch); + + int (*ext_patch_r8180_wx_set_add_mac_allow) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_set_del_mac_allow) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_set_add_mac_deny) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_set_del_mac_deny) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_get_mac_allow) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_get_mac_deny) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + + int (*ext_patch_r8180_wx_get_mesh_list) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_mesh_scan) (struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); + int (*ext_patch_r8180_wx_get_selected_mesh)(struct net_device *dev, int en, char *cho, char* id); +//by amy for networkmanager UI + int (*ext_patch_r8180_wx_get_selected_mesh_channel)(struct net_device *dev, char* extmeshid, char *cho); +//by amy for networkmanager UI + /// r8187_core.c + u8 (*ext_patch_rtl8180_up) (struct mshclass *priv); + + // ieee80211_rx.c + unsigned int (*ext_patch_ieee80211_process_probe_response_1) ( struct ieee80211_device *ieee, struct ieee80211_probe_response *beacon, struct ieee80211_rx_stats *stats); + void (*ext_patch_ieee80211_rx_mgt_on_probe_req) ( struct ieee80211_device *ieee, struct ieee80211_probe_request *beacon, struct ieee80211_rx_stats *stats); + + void (*ext_patch_ieee80211_rx_mgt_update_expire) ( struct ieee80211_device *ieee, struct sk_buff *skb); + + int (*ext_patch_ieee80211_rx_on_rx) (struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats, u16 type, u16 stype); + + int (*ext_patch_ieee80211_rx_frame_get_hdrlen) (struct ieee80211_device *ieee, struct sk_buff *skb); + + int (*ext_patch_ieee80211_rx_is_valid_framectl) (struct ieee80211_device *ieee, u16 fc, u16 type, u16 stype); + + // return > 0 is success. 0 when failed + int (*ext_patch_ieee80211_rx_process_dataframe) (struct ieee80211_device *ieee, struct sk_buff *skb, struct ieee80211_rx_stats *rx_stats); + + int (*ext_patch_is_duplicate_packet) (struct ieee80211_device *ieee, struct ieee80211_hdr *header, u16 type, u16 stype); + /* added by david for setting acl dynamically */ + u8 (*ext_patch_ieee80211_acl_query) (struct ieee80211_device *ieee, u8 *sa); + + // r8187_core.c + int (*ext_patch_rtl8180_ioctl) (struct net_device *dev, struct ifreq *rq, int cmd); + void (*ext_patch_create_proc) (struct r8180_priv *priv); + void (*ext_patch_remove_proc) (struct r8180_priv *priv); + + // ieee80211_tx.c + + // locked by ieee->lock. Call ieee80211_softmac_xmit afterward + struct ieee80211_txb* (*ext_patch_ieee80211_xmit) (struct sk_buff *skb, struct net_device *dev); + + // DO NOT MODIFY ANY STRUCTURE BELOW THIS LINE + u8 priv[0]; // mshclass_priv; +}; + +extern void free_mshobj(struct mshclass **pObj); +extern struct mshclass *alloc_mshobj(struct r8180_priv *caller_priv); + + +#endif // _MESH_CLASS_HDR_H_ diff --git a/drivers/net/wireless/rtl8187b/r8180_93cx6.c b/drivers/net/wireless/rtl8187b/r8180_93cx6.c new file mode 100644 index 0000000..8de9153 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_93cx6.c @@ -0,0 +1,146 @@ +/* + This files contains card eeprom (93c46 or 93c56) programming routines, + memory is addressed by 16 bits words. + + This is part of rtl8180 OpenSource driver. + Copyright (C) Andrea Merello 2004 + Released under the terms of GPL (General Public Licence) + + Parts of this driver are based on the GPL part of the + official realtek driver. + + Parts of this driver are based on the rtl8180 driver skeleton + from Patric Schenke & Andres Salomon. + + Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver. + + We want to tanks the Authors of those projects and the Ndiswrapper + project Authors. +*/ + +#include "r8180_93cx6.h" + +void eprom_cs(struct net_device *dev, short bit) +{ + if(bit) + write_nic_byte(dev, EPROM_CMD, + (1<epromtype==EPROM_93c56){ + addr_str[7]=addr & 1; + addr_str[6]=addr & (1<<1); + addr_str[5]=addr & (1<<2); + addr_str[4]=addr & (1<<3); + addr_str[3]=addr & (1<<4); + addr_str[2]=addr & (1<<5); + addr_str[1]=addr & (1<<6); + addr_str[0]=addr & (1<<7); + addr_len=8; + }else{ + addr_str[5]=addr & 1; + addr_str[4]=addr & (1<<1); + addr_str[3]=addr & (1<<2); + addr_str[2]=addr & (1<<3); + addr_str[1]=addr & (1<<4); + addr_str[0]=addr & (1<<5); + addr_len=6; + } + eprom_cs(dev, 1); + eprom_ck_cycle(dev); + eprom_send_bits_string(dev, read_cmd, 3); + eprom_send_bits_string(dev, addr_str, addr_len); + + //keep chip pin D to low state while reading. + //I'm unsure if it is necessary, but anyway shouldn't hurt + eprom_w(dev, 0); + + for(i=0;i<16;i++){ + //eeprom needs a clk cycle between writing opcode&adr + //and reading data. (eeprom outs a dummy 0) + eprom_ck_cycle(dev); + ret |= (eprom_r(dev)<<(15-i)); + } + + eprom_cs(dev, 0); + eprom_ck_cycle(dev); + + //disable EPROM programming + write_nic_byte(dev, EPROM_CMD, + (EPROM_CMD_NORMAL< + Released under the terms of GPL (General Public Licence) + + Parts of this driver are based on the GPL part of the official realtek driver + Parts of this driver are based on the rtl8180 driver skeleton from Patric Schenke & Andres Salomon + Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver + + We want to tanks the Authors of such projects and the Ndiswrapper project Authors. +*/ + +/*This files contains card eeprom (93c46 or 93c56) programming routines*/ +/*memory is addressed by WORDS*/ + +#include "r8187.h" +#include "r8180_hw.h" + +#define EPROM_DELAY 10 + +#define EPROM_ANAPARAM_ADDRLWORD 0xd +#define EPROM_ANAPARAM_ADDRHWORD 0xe + +#define EPROM_CHANNEL_PLAN 0x3 //0x6>>1 +//0x77 BIT[0]0:use gpio 1 bit 1, 1:use gpio 1 bit 2. +#define EPROM_SELECT_GPIO (0x77 >> 1) +//#define EEPROM_COUNTRY_CODE 0x2E//87se channel plan is here + +#define EPROM_RFCHIPID 0x6 +#define EPROM_TXPW_BASE 0x05 +#define EPROM_RFCHIPID_RTL8225U 5 +#define EPROM_RFCHIPID_RTL8225U_VF 6 +#define EPROM_RF_PARAM 0x4 +#define EPROM_CONFIG2 0xc + +#define EPROM_VERSION 0x1E +#define MAC_ADR 0x7 + +#define CIS 0x18 + +#define EPROM_TXPW0 0x16 +#define EPROM_TXPW2 0x1b +#define EPROM_TXPW1 0x3d + + +u32 eprom_read(struct net_device *dev,u32 addr); //reads a 16 bits word diff --git a/drivers/net/wireless/rtl8187b/r8180_dm.c b/drivers/net/wireless/rtl8187b/r8180_dm.c new file mode 100644 index 0000000..684610e --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_dm.c @@ -0,0 +1,882 @@ +/*++ +Copyright (c) Realtek Semiconductor Corp. All rights reserved. + +Module Name: + r8180_dig.c + +Abstract: + Hardware dynamic mechanism for RTL8187B + +Major Change History: + When Who What + ---------- --------------- ------------------------------- + 2006-11-15 david Created + +Notes: + This file is ported from RTL8187B Windows driver. + + +--*/ +#include "r8180_dm.h" +#include "r8180_hw.h" +#include "r8180_rtl8225.h" + +//================================================================================ +// Local Constant. +//================================================================================ +#define Z1_HIPWR_UPPER_TH 99 +#define Z1_HIPWR_LOWER_TH 70 +#define Z2_HIPWR_UPPER_TH 99 +#define Z2_HIPWR_LOWER_TH 90 + +bool CheckDig(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + + if(ieee->state != IEEE80211_LINKED) + return false; + + if(priv->card_8187 == NIC_8187B) { + // + // We need to schedule dig workitem on either of the below mechanisms. + // By Bruce, 2007-06-01. + // + if(!priv->bDigMechanism && !priv->bCCKThMechanism) + return false; + + if(priv->CurrentOperaRate < 36) // Schedule Dig under all OFDM rates. By Bruce, 2007-06-01. + return false; + } else { + if(!priv->bDigMechanism) + return false; + + if(priv->CurrentOperaRate < 48) + return false; + } + return true; +} + + +// +// Description: +// Implementation of DIG for Zebra and Zebra2. +// +void DIG_Zebra(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + //PHAL_DATA_8187 pHalData = GetHalData8187(Adapter); + u16 CCKFalseAlarm, OFDMFalseAlarm; + u16 OfdmFA1, OfdmFA2; + int InitialGainStep = 7; // The number of initial gain stages. + int LowestGainStage = 4; // The capable lowest stage of performing dig workitem. + +// printk("---------> DIG_Zebra()\n"); + + //Read only 1 byte because of HW bug. This is a temporal modification. Joseph + // Modify by Isaiah 2006-06-27 + if(priv->card_8187_Bversion == VERSION_8187B_B) + { + CCKFalseAlarm = 0; + OFDMFalseAlarm = (u16)(priv->FalseAlarmRegValue); + OfdmFA1 = 0x01; + OfdmFA2 = priv->RegDigOfdmFaUpTh; + } + else + { + CCKFalseAlarm = (u16)(priv->FalseAlarmRegValue & 0x0000ffff); + OFDMFalseAlarm = (u16)((priv->FalseAlarmRegValue >> 16) & 0x0000ffff); + OfdmFA1 = 0x15; + //OfdmFA2 = 0xC00; + OfdmFA2 = ((u16)(priv->RegDigOfdmFaUpTh)) << 8; + } + +// printk("DIG**********CCK False Alarm: %#X \n",CCKFalseAlarm); +// printk("DIG**********OFDM False Alarm: %#X \n",OFDMFalseAlarm); + + + + // The number of initial gain steps is different, by Bruce, 2007-04-13. + if(priv->card_8187 == NIC_8187) { + if (priv->InitialGain == 0 ) //autoDIG + { + switch( priv->rf_chip) + { + case RF_ZEBRA: + priv->InitialGain = 5; // m74dBm; + break; + case RF_ZEBRA2: + priv->InitialGain = 4; // m78dBm; + break; + default: + priv->InitialGain = 5; // m74dBm; + break; + } + } + InitialGainStep = 7; + if(priv->InitialGain > 7) + priv->InitialGain = 5; + LowestGainStage = 4; + } else { + if (priv->InitialGain == 0 ) //autoDIG + { // Advised from SD3 DZ, by Bruce, 2007-06-05. + priv->InitialGain = 4; // In 87B, m74dBm means State 4 (m82dBm) + } + if(priv->card_8187_Bversion != VERSION_8187B_B) + { // Advised from SD3 DZ, by Bruce, 2007-06-05. + OfdmFA1 = 0x20; + } + InitialGainStep = 8; + LowestGainStage = priv->RegBModeGainStage; // Lowest gain stage. + } + + if (OFDMFalseAlarm > OfdmFA1) + { + if (OFDMFalseAlarm > OfdmFA2) + { + priv->DIG_NumberFallbackVote++; + if (priv->DIG_NumberFallbackVote >1) + { + //serious OFDM False Alarm, need fallback + // By Bruce, 2007-03-29. + // if (pHalData->InitialGain < 7) // In 87B, m66dBm means State 7 (m74dBm) + if (priv->InitialGain < InitialGainStep) + { + priv->InitialGain = (priv->InitialGain + 1); + //printk("DIG**********OFDM False Alarm: %#X, OfdmFA1: %#X, OfdmFA2: %#X\n", OFDMFalseAlarm, OfdmFA1, OfdmFA2); + //printk("DIG+++++++ fallback OFDM:%d \n", priv->InitialGain); + UpdateInitialGain(dev); // 2005.01.06, by rcnjko. + } + priv->DIG_NumberFallbackVote = 0; + priv->DIG_NumberUpgradeVote=0; + } + } + else + { + if (priv->DIG_NumberFallbackVote) + priv->DIG_NumberFallbackVote--; + } + priv->DIG_NumberUpgradeVote=0; + } + else //OFDM False Alarm < 0x15 + { + if (priv->DIG_NumberFallbackVote) + priv->DIG_NumberFallbackVote--; + priv->DIG_NumberUpgradeVote++; + + if (priv->DIG_NumberUpgradeVote>9) + { + if (priv->InitialGain > LowestGainStage) // In 87B, m78dBm means State 4 (m864dBm) + { + priv->InitialGain = (priv->InitialGain - 1); + //printk("DIG**********OFDM False Alarm: %#X, OfdmFA1: %#X, OfdmFA2: %#X\n", OFDMFalseAlarm, OfdmFA1, OfdmFA2); + //printk("DIG--------- Upgrade OFDM:%d \n", priv->InitialGain); + UpdateInitialGain(dev); // 2005.01.06, by rcnjko. + } + priv->DIG_NumberFallbackVote = 0; + priv->DIG_NumberUpgradeVote=0; + } + } + +// printk("DIG+++++++ OFDM:%d\n", priv->InitialGain); +// printk("<--------- DIG_Zebra()\n"); +} + +// +// Description: +// Dispatch DIG implementation according to RF. +// +void DynamicInitGain(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + switch(priv->rf_chip) + { + case RF_ZEBRA: + case RF_ZEBRA2: // [AnnieWorkaround] For Zebra2, 2005-08-01. + //case RF_ZEBRA4: + DIG_Zebra(dev); + break; + + default: + printk("DynamicInitGain(): unknown RFChipID(%d) !!!\n", priv->rf_chip); + break; + } +} + +// By Bruce, 2007-03-29. +// +// Description: +// Dispatch CCK Power Detection implementation according to RF. +// +void DynamicCCKThreshold(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + u16 CCK_Up_Th; + u16 CCK_Lw_Th; + u16 CCKFalseAlarm; + + printk("=====>DynamicCCKThreshold()\n"); + + CCK_Up_Th = priv->CCKUpperTh; + CCK_Lw_Th = priv->CCKLowerTh; + CCKFalseAlarm = (u16)((priv->FalseAlarmRegValue & 0x0000ffff) >> 8); // We only care about the higher byte. + printk("DynamicCCKThreshold(): CCK Upper Threshold: 0x%02X, Lower Threshold: 0x%02X, CCKFalseAlarmHighByte: 0x%02X\n", CCK_Up_Th, CCK_Lw_Th, CCKFalseAlarm); + + if(priv->StageCCKTh < 3 && CCKFalseAlarm >= CCK_Up_Th) + { + priv->StageCCKTh ++; + UpdateCCKThreshold(dev); + } + else if(priv->StageCCKTh > 0 && CCKFalseAlarm <= CCK_Lw_Th) + { + priv->StageCCKTh --; + UpdateCCKThreshold(dev); + } + + printk("<=====DynamicCCKThreshold()\n"); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_hw_dig_wq (struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work,struct delayed_work,work); + struct ieee80211_device *ieee = container_of(dwork,struct ieee80211_device,hw_dig_wq); + struct net_device *dev = ieee->dev; +#else +void rtl8180_hw_dig_wq(struct net_device *dev) +{ + // struct r8180_priv *priv = ieee80211_priv(dev); +#endif + struct r8180_priv *priv = ieee80211_priv(dev); + + // Read CCK and OFDM False Alarm. + if(priv->card_8187_Bversion == VERSION_8187B_B) { + // Read only 1 byte because of HW bug. This is a temporal modification. Joseph + // Modify by Isaiah 2006-06-27 + priv->FalseAlarmRegValue = (u32)read_nic_byte(dev, (OFDM_FALSE_ALARM+1)); + } else { + priv->FalseAlarmRegValue = read_nic_dword(dev, CCK_FALSE_ALARM); + } + + // Adjust Initial Gain dynamically. + if(priv->bDigMechanism) { + DynamicInitGain(dev); + } + + // + // Move from DynamicInitGain to be independent of the OFDM DIG mechanism, by Bruce, 2007-06-01. + // + if(priv->card_8187 == NIC_8187B) { + // By Bruce, 2007-03-29. + // Dynamically update CCK Power Detection Threshold. + if(priv->bCCKThMechanism) + { + DynamicCCKThreshold(dev); + } + } +} + +void SetTxPowerLevel8187(struct net_device *dev, short chan) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + switch(priv->rf_chip) + { + case RF_ZEBRA: + rtl8225_SetTXPowerLevel(dev,chan); + break; + + case RF_ZEBRA2: + //case RF_ZEBRA4: + rtl8225z2_SetTXPowerLevel(dev,chan); + break; + } +} + +// +// Description: +// Check if input power signal strength exceeds maximum input power threshold +// of current HW. +// If yes, we set our HW to high input power state: +// RX: always force TR switch to SW Tx mode to reduce input power. +// TX: turn off smaller Tx output power (see RtUsbCheckForHang). +// +// If no, we restore our HW to normal input power state: +/// RX: restore TR switch to HW controled mode. +// TX: restore TX output power (see RtUsbCheckForHang). +// +// TODO: +// 1. Tx power control shall not be done in Platform-dependent timer (e.g. RtUsbCheckForHang). +// 2. Allow these threshold adjustable by RF SD. +// +void DoRxHighPower(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + TR_SWITCH_STATE TrSwState; + u16 HiPwrUpperTh = 0; + u16 HiPwrLowerTh = 0; + u16 RSSIHiPwrUpperTh = 0; + u16 RSSIHiPwrLowerTh = 0; + + //87S remove TrSwitch mechanism + if((priv->card_8187 == NIC_8187B)||(priv->card_8187 == NIC_8187)) { + + //printk("----> DoRxHighPower()\n"); + + // + // Get current TR switch setting. + // + //Adapter->HalFunc.GetHwRegHandler(Adapter, HW_VAR_TR_SWITCH, (pu1Byte)(&TrSwState)); + TrSwState = priv->TrSwitchState; + + // + // Determine threshold according to RF type. + // + switch(priv->rf_chip) + { + case RF_ZEBRA: + HiPwrUpperTh = Z1_HIPWR_UPPER_TH; + HiPwrLowerTh = Z1_HIPWR_LOWER_TH; + printk("DoRxHighPower(): RF_ZEBRA, Upper Threshold: %d LOWER Threshold: %d\n", + HiPwrUpperTh, HiPwrLowerTh); + break; + + case RF_ZEBRA2: + if((priv->card_8187 == NIC_8187)) { + HiPwrUpperTh = Z2_HIPWR_UPPER_TH; + HiPwrLowerTh = Z2_HIPWR_LOWER_TH; + } else { + // By Bruce, 2007-04-11. + // HiPwrUpperTh = Z2_HIPWR_UPPER_TH; + // HiPwrLowerTh = Z2_HIPWR_LOWER_TH; + + HiPwrUpperTh = priv->Z2HiPwrUpperTh; + HiPwrLowerTh = priv->Z2HiPwrLowerTh; + HiPwrUpperTh = HiPwrUpperTh * 10; + HiPwrLowerTh = HiPwrLowerTh * 10; + + RSSIHiPwrUpperTh = priv->Z2RSSIHiPwrUpperTh; + RSSIHiPwrLowerTh = priv->Z2RSSIHiPwrLowerTh; + //printk("DoRxHighPower(): RF_ZEBRA2, Upper Threshold: %d LOWER Threshold: %d, RSSI Upper Th: %d, RSSI Lower Th: %d\n",HiPwrUpperTh, HiPwrLowerTh, RSSIHiPwrUpperTh, RSSIHiPwrLowerTh); + } + break; + + default: + printk("DoRxHighPower(): Unknown RFChipID(%d), UndecoratedSmoothedSS(%d), TrSwState(%d)!!!\n", + priv->rf_chip, priv->UndecoratedSmoothedSS, TrSwState); + return; + break; + } + + /*printk(">>>>>>>>>>Set TR switch to software control, UndecoratedSmoothedSS:%d, CurCCKRSSI = %d\n",\ + priv->UndecoratedSmoothedSS, priv->CurCCKRSSI); + */ + if((priv->card_8187 == NIC_8187)) { + // + // Perform Rx part High Power Mechanism by UndecoratedSmoothedSS. + // + if (priv->UndecoratedSmoothedSS > HiPwrUpperTh) + { // High input power state. + if( priv->TrSwitchState == TR_HW_CONTROLLED ) + { + /* printk(">>>>>>>>>>Set TR switch to software control, UndecoratedSmoothedSS:%d \n", \ + priv->UndecoratedSmoothedSS); + // printk(">>>>>>>>>> TR_SW_TX\n"); + */ + write_nic_byte(dev, RFPinsSelect, + (u8)(priv->wMacRegRfPinsSelect | TR_SW_MASK_8187 )); + write_nic_byte(dev, RFPinsOutput, + (u8)((priv->wMacRegRfPinsOutput&(~TR_SW_MASK_8187))|TR_SW_MASK_TX_8187)); + priv->TrSwitchState = TR_SW_TX; + priv->bToUpdateTxPwr = true; + } + } + else if (priv->UndecoratedSmoothedSS < HiPwrLowerTh) + { // Normal input power state. + if( priv->TrSwitchState == TR_SW_TX) + { + /* printk("<<<<<<<<<<UndecoratedSmoothedSS); + // printk("<<<<<<<<<< TR_HW_CONTROLLED\n"); + */ + write_nic_byte(dev, RFPinsOutput, (u8)(priv->wMacRegRfPinsOutput)); + write_nic_byte(dev, RFPinsSelect, (u8)(priv->wMacRegRfPinsSelect)); + priv->TrSwitchState = TR_HW_CONTROLLED; + priv->bToUpdateTxPwr = true; + } + } + }else { + /*printk("=====>TrSwState = %s\n", (TrSwState==TR_HW_CONTROLLED)?"TR_HW_CONTROLLED":"TR_SW_TX"); + //printk("UndecoratedSmoothedSS:%d, CurCCKRSSI = %d\n",priv->UndecoratedSmoothedSS, priv->CurCCKRSSI); */ + // Asked by SD3 DZ, by Bruce, 2007-04-12. + if(TrSwState == TR_HW_CONTROLLED) + { + if((priv->UndecoratedSmoothedSS > HiPwrUpperTh) || + (priv->bCurCCKPkt && (priv->CurCCKRSSI > RSSIHiPwrUpperTh))) + { + //printk("===============================> high power!\n"); + write_nic_byte(dev, RFPinsSelect, (u8)(priv->wMacRegRfPinsSelect|TR_SW_MASK_8187 )); + write_nic_byte(dev, RFPinsOutput, + (u8)((priv->wMacRegRfPinsOutput&(~TR_SW_MASK_8187))|TR_SW_MASK_TX_8187)); + priv->TrSwitchState = TR_SW_TX; + priv->bToUpdateTxPwr = true; + } + } + else + { + if((priv->UndecoratedSmoothedSS < HiPwrLowerTh) && + (!priv->bCurCCKPkt || priv->CurCCKRSSI < RSSIHiPwrLowerTh)) + { + write_nic_byte(dev, RFPinsOutput, (u8)(priv->wMacRegRfPinsOutput)); + write_nic_byte(dev, RFPinsSelect, (u8)(priv->wMacRegRfPinsSelect)); + priv->TrSwitchState = TR_HW_CONTROLLED; + priv->bToUpdateTxPwr = true; + } + } + //printk("<=======TrSwState = %s\n", (TrSwState==TR_HW_CONTROLLED)?"TR_HW_CONTROLLED":"TR_SW_TX"); + } + //printk("<---- DoRxHighPower()\n"); + } +} + + +// +// Description: +// Callback function of UpdateTxPowerWorkItem. +// Because of some event happend, e.g. CCX TPC, High Power Mechanism, +// We update Tx power of current channel again. +// +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_tx_pw_wq (struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work,struct delayed_work,work); + struct ieee80211_device *ieee = container_of(dwork,struct ieee80211_device,tx_pw_wq); + struct net_device *dev = ieee->dev; +#else +void rtl8180_tx_pw_wq(struct net_device *dev) +{ + // struct r8180_priv *priv = ieee80211_priv(dev); +#endif + + struct r8180_priv *priv = ieee80211_priv(dev); + + //printk("----> UpdateTxPowerWorkItemCallback()\n"); + + if(priv->bToUpdateTxPwr) + { + //printk("DoTxHighPower(): schedule UpdateTxPowerWorkItem......\n"); + priv->bToUpdateTxPwr = false; + SetTxPowerLevel8187(dev, priv->chan); + } + + DoRxHighPower(dev); + //printk("<---- UpdateTxPowerWorkItemCallback()\n"); +} + +// +// Description: +// Return TRUE if we shall perform High Power Mecahnism, FALSE otherwise. +// +bool CheckHighPower(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + + if(!priv->bRegHighPowerMechanism) + { + return false; + } + + if((ieee->state == IEEE80211_LINKED_SCANNING)||(ieee->state == IEEE80211_MESH_SCANNING)) + { + return false; + } + + return true; +} + +#ifdef SW_ANTE_DIVERSITY + +#define ANTENNA_DIVERSITY_TIMER_PERIOD 1000 // 1000 m + +void +SwAntennaDiversityRxOk8185( + struct net_device *dev, + u8 SignalStrength + ) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + //printk("+SwAntennaDiversityRxOk8185: RxSs: %d\n", SignalStrength); + + priv->AdRxOkCnt++; + + if( priv->AdRxSignalStrength != -1) + { + priv->AdRxSignalStrength = ((priv->AdRxSignalStrength*7) + (SignalStrength*3)) / 10; + } + else + { // Initialization case. + priv->AdRxSignalStrength = SignalStrength; + } + + //printk("====>pkt rcvd by %d\n", priv->LastRxPktAntenna); + if( priv->LastRxPktAntenna ) //Main antenna. + priv->AdMainAntennaRxOkCnt++; + else // Aux antenna. + priv->AdAuxAntennaRxOkCnt++; + //printk("-SwAntennaDiversityRxOk8185: AdRxOkCnt: %d AdRxSignalStrength: %d\n", priv->AdRxOkCnt, priv->AdRxSignalStrength); +} + +// +// Description: Change Antenna Switch. +// +bool +SetAntenna8185( + struct net_device *dev, + u8 u1bAntennaIndex + ) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + bool bAntennaSwitched = false; + +// printk("+SetAntenna8185(): Antenna is switching to: %d \n", u1bAntennaIndex); + + switch(u1bAntennaIndex) + { + case 0://main antenna + switch(priv->rf_chip) + { + case RF_ZEBRA: + case RF_ZEBRA2: + //case RF_ZEBRA4: + // Tx Antenna. + write_nic_byte(dev, ANTSEL, 0x03); // Config TX antenna. + + //PlatformEFIOWrite4Byte(Adapter, BBAddr, 0x01009b90); // Config CCK RX antenna. + //PlatformEFIOWrite4Byte(Adapter, BBAddr, 0x5c8D); // Config OFDM RX antenna. + + // Rx CCK . + write_nic_byte(dev, 0x7f, ((0x01009b90 & 0xff000000) >> 24)); + write_nic_byte(dev, 0x7e, ((0x01009b90 & 0x00ff0000) >> 16)); + write_nic_byte(dev, 0x7d, ((0x01009b90 & 0x0000ff00) >> 8)); + write_nic_byte(dev, 0x7c, ((0x01009b90 & 0x000000ff) >> 0)); + + // Rx OFDM. + write_nic_byte(dev, 0x7f, ((0x00005c8D & 0xff000000) >> 24)); + write_nic_byte(dev, 0x7e, ((0x00005c8D & 0x00ff0000) >> 16)); + write_nic_byte(dev, 0x7d, ((0x00005c8D & 0x0000ff00) >> 8)); + write_nic_byte(dev, 0x7c, ((0x00005c8D & 0x000000ff) >> 0)); + + bAntennaSwitched = true; + break; + + default: + printk("SetAntenna8185: unkown RFChipID(%d)\n", priv->rf_chip); + break; + } + break; + + case 1: + switch(priv->rf_chip) + { + case RF_ZEBRA: + case RF_ZEBRA2: + //case RF_ZEBRA4: + // Tx Antenna. + write_nic_byte(dev, ANTSEL, 0x00); // Config TX antenna. + + //PlatformEFIOWrite4Byte(Adapter, BBAddr, 0x0100bb90); // Config CCK RX antenna. + //PlatformEFIOWrite4Byte(Adapter, BBAddr, 0x548D); // Config OFDM RX antenna. + + // Rx CCK. + write_nic_byte(dev, 0x7f, ((0x0100bb90 & 0xff000000) >> 24)); + write_nic_byte(dev, 0x7e, ((0x0100bb90 & 0x00ff0000) >> 16)); + write_nic_byte(dev, 0x7d, ((0x0100bb90 & 0x0000ff00) >> 8)); + write_nic_byte(dev, 0x7c, ((0x0100bb90 & 0x000000ff) >> 0)); + + // Rx OFDM. + write_nic_byte(dev, 0x7f, ((0x0000548D & 0xff000000) >> 24)); + write_nic_byte(dev, 0x7e, ((0x0000548D & 0x00ff0000) >> 16)); + write_nic_byte(dev, 0x7d, ((0x0000548D & 0x0000ff00) >> 8)); + write_nic_byte(dev, 0x7c, ((0x0000548D & 0x000000ff) >> 0)); + + bAntennaSwitched = true; + break; + + default: + printk("SetAntenna8185: unkown RFChipID(%d)\n", priv->rf_chip); + break; + } + break; + + default: + printk("SetAntenna8185: unkown u1bAntennaIndex(%d)\n", u1bAntennaIndex); + break; + } + + if(bAntennaSwitched) + { + priv->CurrAntennaIndex = u1bAntennaIndex; + } + +// printk("-SetAntenna8185(): return (%#X)\n", bAntennaSwitched); + + return bAntennaSwitched; +} + +// +// Description: Toggle Antenna switch. +// +bool SwitchAntenna(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + bool bResult = false; + + if(priv->CurrAntennaIndex == 0) + { + bResult = SetAntenna8185(dev, 1); + if(priv->ieee80211->state == IEEE80211_LINKED) + printk("Switching to Aux antenna 1 \n"); + } + else + { + bResult = SetAntenna8185(dev, 0); + if(priv->ieee80211->state == IEEE80211_LINKED) + printk("Switching to Main antenna 0 \n"); + } + + return bResult; +} + +// +// Description: +// Engine of SW Antenna Diversity mechanism. +// Since 8187 has no Tx part information, +// this implementation is only dependend on Rx part information. +// +// 2006.04.17, by rcnjko. +// +void SwAntennaDiversity(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + //bool bSwCheckSS=false; + bool bSwCheckSS=true;//open the SignalStrength check if not switched by rx ok pkt. + +// printk("+SwAntennaDiversity(): CurrAntennaIndex: %d\n", priv->CurrAntennaIndex); + +//by amy 080312 + if(bSwCheckSS){ + priv->AdTickCount++; + + //printk("(1) AdTickCount: %d, AdCheckPeriod: %d\n", priv->AdTickCount, priv->AdCheckPeriod); + //printk("(2) AdRxSignalStrength: %ld, AdRxSsThreshold: %ld\n", priv->AdRxSignalStrength, priv->AdRxSsThreshold); + } +// priv->AdTickCount++;//-by amy 080312 + + // Case 1. No Link. + if(priv->ieee80211->state != IEEE80211_LINKED){ + //printk("SwAntennaDiversity(): Case 1. No Link.\n"); + + priv->bAdSwitchedChecking = false; + // I switch antenna here to prevent any one of antenna is broken before link established, 2006.04.18, by rcnjko.. + SwitchAntenna(dev); + } + // Case 2. Linked but no packet received. + else if(priv->AdRxOkCnt == 0){ + printk("SwAntennaDiversity(): Case 2. Linked but no packet received.\n"); + + priv->bAdSwitchedChecking = false; + SwitchAntenna(dev); + } + // Case 3. Evaluate last antenna switch action in case4. and undo it if necessary. + else if(priv->bAdSwitchedChecking == true){ + //printk("SwAntennaDiversity(): Case 3. Evaluate last antenna switch action.\n"); + + priv->bAdSwitchedChecking = false; + + // Adjust Rx signal strength threashold. + priv->AdRxSsThreshold = (priv->AdRxSignalStrength + priv->AdRxSsBeforeSwitched) / 2; + + priv->AdRxSsThreshold = (priv->AdRxSsThreshold > priv->AdMaxRxSsThreshold) ? + priv->AdMaxRxSsThreshold: priv->AdRxSsThreshold; + if(priv->AdRxSignalStrength < priv->AdRxSsBeforeSwitched){ + // Rx signal strength is not improved after we swtiched antenna. => Swich back. + printk("SwAntennaDiversity(): Rx Signal Strength is not improved, CurrRxSs: %ld, LastRxSs: %ld\n", priv->AdRxSignalStrength, priv->AdRxSsBeforeSwitched); + + //by amy 080312 + // Increase Antenna Diversity checking period due to bad decision. + priv->AdCheckPeriod *= 2; + //by amy 080312 + // + // Increase Antenna Diversity checking period. + if(priv->AdCheckPeriod > priv->AdMaxCheckPeriod) + priv->AdCheckPeriod = priv->AdMaxCheckPeriod; + + // Wrong deceision => switch back. + SwitchAntenna(dev); + }else{ // Rx Signal Strength is improved. + printk("SwAntennaDiversity(): Rx Signal Strength is improved, CurrRxSs: %ld, LastRxSs: %ld\n", priv->AdRxSignalStrength, priv->AdRxSsBeforeSwitched); + + // Reset Antenna Diversity checking period to its min value. + priv->AdCheckPeriod = priv->AdMinCheckPeriod; + } + + //printk("SwAntennaDiversity(): AdRxSsThreshold: %ld, AdCheckPeriod: %d\n", + // priv->AdRxSsThreshold, priv->AdCheckPeriod); + } + // Case 4. Evaluate if we shall switch antenna now. + // Cause Table Speed is very fast in TRC Dell Lab, we check it every time. + else// if(priv->AdTickCount >= priv->AdCheckPeriod)//-by amy 080312 + { + //printk("SwAntennaDiversity(): Case 4. Evaluate if we shall switch antenna now.\n"); + + priv->AdTickCount = 0; + + // + // We evaluate RxOk counts for each antenna first and than + // evaluate signal strength. + // The following operation can overcome the disability of CCA on both two antennas + // When signal strength was extremely low or high. + // 2008.01.30. + // + + // + // Evaluate RxOk count from each antenna if we shall switch default antenna now. + // Added by Roger, 2008.02.21. + + //{by amy 080312 + if((priv->AdMainAntennaRxOkCnt < priv->AdAuxAntennaRxOkCnt) && (priv->CurrAntennaIndex == 0)){ + // We set Main antenna as default but RxOk count was less than Aux ones. + + printk("SwAntennaDiversity(): Main antenna %d RxOK is poor, AdMainAntennaRxOkCnt: %d, AdAuxAntennaRxOkCnt: %d\n",priv->CurrAntennaIndex, priv->AdMainAntennaRxOkCnt, priv->AdAuxAntennaRxOkCnt); + + // Switch to Aux antenna. + SwitchAntenna(dev); + priv->bHWAdSwitched = true; + }else if((priv->AdAuxAntennaRxOkCnt < priv->AdMainAntennaRxOkCnt) && (priv->CurrAntennaIndex == 1)){ + // We set Aux antenna as default but RxOk count was less than Main ones. + + printk("SwAntennaDiversity(): Aux antenna %d RxOK is poor, AdMainAntennaRxOkCnt: %d, AdAuxAntennaRxOkCnt: %d\n",priv->CurrAntennaIndex, priv->AdMainAntennaRxOkCnt, priv->AdAuxAntennaRxOkCnt); + + // Switch to Main antenna. + SwitchAntenna(dev); + priv->bHWAdSwitched = true; + }else{// Default antenna is better. + + printk("SwAntennaDiversity(): Current Antenna %d is better., AdMainAntennaRxOkCnt: %d, AdAuxAntennaRxOkCnt: %d\n",priv->CurrAntennaIndex, priv->AdMainAntennaRxOkCnt, priv->AdAuxAntennaRxOkCnt); + + // Still need to check current signal strength. + priv->bHWAdSwitched = false; + } + // + // We evaluate Rx signal strength ONLY when default antenna + // didn't changed by HW evaluation. + // 2008.02.27. + // + // [TRC Dell Lab] SignalStrength is inaccuracy. Isaiah 2008-03-05 + // For example, Throughput of aux is better than main antenna(about 10M v.s 2M), + // but AdRxSignalStrength is less than main. + // Our guess is that main antenna have lower throughput and get many change + // to receive more CCK packets(ex.Beacon) which have stronger SignalStrength. + // + if( (!priv->bHWAdSwitched) && (bSwCheckSS)){ + //by amy 080312} + + // Evaluate Rx signal strength if we shall switch antenna now. + if(priv->AdRxSignalStrength < priv->AdRxSsThreshold){ + // Rx signal strength is weak => Switch Antenna. + printk("SwAntennaDiversity(): Rx Signal Strength is weak, CurrRxSs: %ld, RxSsThreshold: %ld\n", priv->AdRxSignalStrength, priv->AdRxSsThreshold); + + priv->AdRxSsBeforeSwitched = priv->AdRxSignalStrength; + priv->bAdSwitchedChecking = true; + + SwitchAntenna(dev); + }else{ // Rx signal strength is OK. + printk("SwAntennaDiversity(): Rx Signal Strength is OK, CurrRxSs: %ld, RxSsThreshold: %ld\n", priv->AdRxSignalStrength, priv->AdRxSsThreshold); + + priv->bAdSwitchedChecking = false; + // Increase Rx signal strength threashold if necessary. + if( (priv->AdRxSignalStrength > (priv->AdRxSsThreshold + 10)) && // Signal is much stronger than current threshold + priv->AdRxSsThreshold <= priv->AdMaxRxSsThreshold) // Current threhold is not yet reach upper limit. + { + priv->AdRxSsThreshold = (priv->AdRxSsThreshold + priv->AdRxSignalStrength) / 2; + priv->AdRxSsThreshold = (priv->AdRxSsThreshold > priv->AdMaxRxSsThreshold) ? + priv->AdMaxRxSsThreshold: priv->AdRxSsThreshold;//+by amy 080312 + } + + // Reduce Antenna Diversity checking period if possible. + if( priv->AdCheckPeriod > priv->AdMinCheckPeriod ) + { + priv->AdCheckPeriod /= 2; + } + } + } + } +//by amy 080312 + // Reset antenna diversity Rx related statistics. + priv->AdRxOkCnt = 0; + priv->AdMainAntennaRxOkCnt = 0; + priv->AdAuxAntennaRxOkCnt = 0; +//by amy 080312 + +// priv->AdRxOkCnt = 0;//-by amy 080312 + + //printk("-SwAntennaDiversity()\n"); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void SwAntennaWorkItemCallback(struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, SwAntennaWorkItem.work); + struct net_device *dev = ieee->dev; +#else +void SwAntennaWorkItemCallback(struct net_device *dev) +{ +#endif + //printk("==>%s \n", __func__); + SwAntennaDiversity(dev); +} + +// +// Description: Timer callback function of SW Antenna Diversity. +// +void SwAntennaDiversityTimerCallback(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + RT_RF_POWER_STATE rtState; + + //printk("+SwAntennaDiversityTimerCallback()\n"); + + // + // We do NOT need to switch antenna while RF is off. + // 2007.05.09, added by Roger. + // + rtState = priv->eRFPowerState; + do{ + if (rtState == eRfOff){ +// printk("SwAntennaDiversityTimer - RF is OFF.\n"); + break; + }else if (rtState == eRfSleep){ + // Don't access BB/RF under Disable PLL situation. + //RT_TRACE((COMP_RF|COMP_ANTENNA), DBG_LOUD, ("SwAntennaDiversityTimerCallback(): RF is Sleep => skip it\n")); + break; + } + + queue_work(priv->ieee80211->wq,(void *)&priv->ieee80211->SwAntennaWorkItem); + + }while(false); + + if(priv->up){ + //priv->SwAntennaDiversityTimer.expires = jiffies + MSECS(ANTENNA_DIVERSITY_TIMER_PERIOD); + //add_timer(&priv->SwAntennaDiversityTimer); + mod_timer(&priv->SwAntennaDiversityTimer, jiffies + MSECS(ANTENNA_DIVERSITY_TIMER_PERIOD)); + } + +} +#endif + + + diff --git a/drivers/net/wireless/rtl8187b/r8180_dm.h b/drivers/net/wireless/rtl8187b/r8180_dm.h new file mode 100644 index 0000000..2b7e671 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_dm.h @@ -0,0 +1,38 @@ +/* + Hardware dynamic mechanism for RTL8187B. +Notes: + This file is ported from RTL8187B Windows driver +*/ + +#ifndef R8180_DM_H +#define R8180_DM_H + +#include "r8187.h" + +bool CheckDig(struct net_device *dev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_hw_dig_wq (struct work_struct *work); +#else +void rtl8180_hw_dig_wq(struct net_device *dev); +#endif + +bool CheckHighPower(struct net_device *dev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_tx_pw_wq (struct work_struct *work); +#else +void rtl8180_tx_pw_wq(struct net_device *dev); +#endif + +//by lzm for antenna +#ifdef SW_ANTE_DIVERSITY +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void SwAntennaWorkItemCallback(struct work_struct *work); +#else +void SwAntennaWorkItemCallback(struct net_device *dev); +#endif +void SwAntennaDiversityRxOk8185(struct net_device *dev, u8 SignalStrength); +void SwAntennaDiversityTimerCallback(struct net_device *dev); +#endif +//by lzm for antenna + +#endif //R8180_PM_H diff --git a/drivers/net/wireless/rtl8187b/r8180_hw.h b/drivers/net/wireless/rtl8187b/r8180_hw.h new file mode 100644 index 0000000..c050fca --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_hw.h @@ -0,0 +1,788 @@ +/* + This is part of rtl8187 OpenSource driver. + Copyright (C) Andrea Merello 2004-2005 + Released under the terms of GPL (General Public Licence) + + Parts of this driver are based on the GPL part of the + official Realtek driver. + Parts of this driver are based on the rtl8180 driver skeleton + from Patric Schenke & Andres Salomon. + Parts of this driver are based on the Intel Pro Wireless + 2100 GPL driver. + + We want to tanks the Authors of those projects + and the Ndiswrapper project Authors. +*/ + +/* Mariusz Matuszek added full registers definition with Realtek's name */ + +/* this file contains register definitions for the rtl8187 MAC controller */ +#ifndef R8180_HW +#define R8180_HW + +typedef enum _RF_TYPE_8187{ + RF_TYPE_MIN, + RF_ZEBRA = 5, + RF_ZEBRA2, // added by Annie, 2005-08-01. + RF_TYPE_MAX, +}RF_TYPE_8187,*PRF_TYPE_8187; + +typedef enum _VERSION_8187{ + // RTL8187 + VERSION_8187_B, // B-cut + VERSION_8187_D, // D-cut + // RTL8187B + VERSION_8187B_B, // B-cut + VERSION_8187B_D, //D-cut //added 2007-9-14 + VERSION_8187B_E, //E-cut //added 2007-9-14 +}VERSION_8187,*PVERSION_8187; + +//by lzm for antenna +#ifdef SW_ANTE_DIVERSITY +#define RF_PARAM 0x19 +#define RF_PARAM_DIGPHY_SHIFT 0 +#define RF_PARAM_ANTBDEFAULT_SHIFT 1 +#define EEPROM_VERSION 0x3c +#define EEPROM_CONFIG2 0x18 +#define EEPROM_CS_THRESHOLD 0x2F +#define EEPROM_RF_PARAM 0x08 +//// BIT[8-9] is for SW Antenna Diversity. Only the value EEPROM_SW_AD_ENABLE means enable, other values are diable. +#define EEPROM_SW_AD_MASK 0x0300 +#define EEPROM_SW_AD_ENABLE 0x0100 +//// BIT[10-11] determine if Antenna 1 is the Default Antenna. Only the value EEPROM_DEF_ANT_1 means TRUE, other values are FALSE. +#define EEPROM_DEF_ANT_MASK 0x0C00 +#define EEPROM_DEF_ANT_1 0x0400 + +#define RCR_EnCS1 BIT29 // enable carrier sense method 1 +#define RCR_EnCS2 BIT30 // enable carrier sense method 2 +#endif +//by lzm for antenna + +#define RTL8187_RF_INDEX 0x8225 +#define RTL8187_REQT_READ 0xc0 +#define RTL8187_REQT_WRITE 0x40 +#define RTL8187_REQ_GET_REGS 0x05 +#define RTL8187_REQ_SET_REGS 0x05 + + + +#define MAX_TX_URB 5 +#define MAX_RX_URB 16 +#define RX_URB_SIZE 0x9C4 + + + + + +#define BB_ANTATTEN_CHAN14 0x0c +#define BB_ANTENNA_B 0x40 + +#define BB_HOST_BANG (1<<30) +#define BB_HOST_BANG_EN (1<<2) +#define BB_HOST_BANG_CLK (1<<1) +#define BB_HOST_BANG_RW (1<<3) +#define BB_HOST_BANG_DATA 1 + +#define ANAPARAM_TXDACOFF_SHIFT 27 +#define ANAPARAM_PWR0_MASK ((1<<30)|(1<<29)|(1<<28)) +#define ANAPARAM_PWR0_SHIFT 28 +#define ANAPARAM_PWR1_MASK ((1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22)|(1<<21)|(1<<20)) +#define ANAPARAM_PWR1_SHIFT 20 + +#define MAC0 0 +#define MAC1 1 +#define MAC2 2 +#define MAC3 3 +#define MAC4 4 +#define MAC5 5 + +#define RXFIFOCOUNT 0x10 +#define TXFIFOCOUNT 0x12 +#define BcnIntTime 0x74 +#define TALLY_SEL 0xfc +#define BQREQ 0x13 + +#define CMD 0x37 +#define CMD_RST_SHIFT 4 +#define CMD_RESERVED_MASK ((1<<1) | (1<<5) | (1<<6) | (1<<7)) +#define CMD_RX_ENABLE_SHIFT 3 +#define CMD_TX_ENABLE_SHIFT 2 + +#define EPROM_CMD 0x50 +#define EPROM_CMD_RESERVED_MASK ((1<<5)|(1<<4)) +#define EPROM_CMD_OPERATING_MODE_SHIFT 6 +#define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6)) +#define EPROM_CMD_CONFIG 0x3 +#define EPROM_CMD_NORMAL 0 +#define EPROM_CMD_LOAD 1 +#define EPROM_CMD_PROGRAM 2 +#define EPROM_CS_SHIFT 3 +#define EPROM_CK_SHIFT 2 +#define EPROM_W_SHIFT 1 +#define EPROM_R_SHIFT 0 +#define CONFIG2_DMA_POLLING_MODE_SHIFT 3 +#define INTA 0x3e +#define INTA_TXOVERFLOW (1<<15) +#define INTA_TIMEOUT (1<<14) +#define INTA_BEACONTIMEOUT (1<<13) +#define INTA_ATIM (1<<12) +#define INTA_BEACONDESCERR (1<<11) +#define INTA_BEACONDESCOK (1<<10) +#define INTA_HIPRIORITYDESCERR (1<<9) +#define INTA_HIPRIORITYDESCOK (1<<8) +#define INTA_NORMPRIORITYDESCERR (1<<7) +#define INTA_NORMPRIORITYDESCOK (1<<6) +#define INTA_RXOVERFLOW (1<<5) +#define INTA_RXDESCERR (1<<4) +#define INTA_LOWPRIORITYDESCERR (1<<3) +#define INTA_LOWPRIORITYDESCOK (1<<2) +#define INTA_RXCRCERR (1<<1) +#define INTA_RXOK (1) +#define INTA_MASK 0x3c +#define RXRING_ADDR 0xe4 // page 0 +#define PGSELECT 0x5e +#define PGSELECT_PG_SHIFT 0 +#define RX_CONF 0x44 +#define MAC_FILTER_MASK ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<5) | \ +(1<<12) | (1<<18) | (1<<19) | (1<<20) | (1<<21) | (1<<22) | (1<<23)) +#define RX_CHECK_BSSID_SHIFT 23 +#define ACCEPT_PWR_FRAME_SHIFT 22 +#define ACCEPT_MNG_FRAME_SHIFT 20 +#define ACCEPT_CTL_FRAME_SHIFT 19 +#define ACCEPT_DATA_FRAME_SHIFT 18 +#define ACCEPT_ICVERR_FRAME_SHIFT 12 +#define ACCEPT_CRCERR_FRAME_SHIFT 5 +#define ACCEPT_BCAST_FRAME_SHIFT 3 +#define ACCEPT_MCAST_FRAME_SHIFT 2 +#define ACCEPT_ALLMAC_FRAME_SHIFT 0 +#define ACCEPT_NICMAC_FRAME_SHIFT 1 +#define RX_FIFO_THRESHOLD_MASK ((1<<13) | (1<<14) | (1<<15)) +#define RX_FIFO_THRESHOLD_SHIFT 13 +#define RX_FIFO_THRESHOLD_128 3 +#define RX_FIFO_THRESHOLD_256 4 +#define RX_FIFO_THRESHOLD_512 5 +#define RX_FIFO_THRESHOLD_1024 6 +#define RX_FIFO_THRESHOLD_NONE 7 +#define RX_AUTORESETPHY_SHIFT 28 +#define EPROM_TYPE_SHIFT 6 +#define TX_CONF 0x40 +#define TX_CONF_HEADER_AUTOICREMENT_SHIFT 30 +#define TX_LOOPBACK_SHIFT 17 +#define TX_LOOPBACK_MAC 1 +#define TX_LOOPBACK_BASEBAND 2 +#define TX_LOOPBACK_NONE 0 +#define TX_LOOPBACK_CONTINUE 3 +#define TX_LOOPBACK_MASK ((1<<17)|(1<<18)) +#define TX_LRLRETRY_SHIFT 0 +#define R8180_MAX_RETRY 255 +#define TX_SRLRETRY_SHIFT 8 +#define TX_NOICV_SHIFT 19 +#define TX_NOCRC_SHIFT 16 +#define TX_DMA_POLLING 0xd9 +#define TX_DMA_POLLING_BEACON_SHIFT 7 +#define TX_DMA_POLLING_HIPRIORITY_SHIFT 6 +#define TX_DMA_POLLING_NORMPRIORITY_SHIFT 5 +#define TX_DMA_POLLING_LOWPRIORITY_SHIFT 4 +#define TX_DMA_STOP_BEACON_SHIFT 3 +#define TX_DMA_STOP_HIPRIORITY_SHIFT 2 +#define TX_DMA_STOP_NORMPRIORITY_SHIFT 1 +#define TX_DMA_STOP_LOWPRIORITY_SHIFT 0 +#define TX_NORMPRIORITY_RING_ADDR 0x24 +#define TX_HIGHPRIORITY_RING_ADDR 0x28 +#define TX_LOWPRIORITY_RING_ADDR 0x20 +#define MAX_RX_DMA_MASK ((1<<8) | (1<<9) | (1<<10)) +#define MAX_RX_DMA_2048 7 +#define MAX_RX_DMA_1024 6 +#define MAX_RX_DMA_SHIFT 10 +#define INT_TIMEOUT 0x48 +#define CONFIG3_CLKRUN_SHIFT 2 +#define CONFIG3_ANAPARAM_W_SHIFT 6 +#define ANAPARAM 0x54 +#define BEACON_INTERVAL 0x70 +#define BEACON_INTERVAL_MASK ((1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)| \ +(1<<6)|(1<<7)|(1<<8)|(1<<9)) +#define ATIM_MASK ((1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)| \ +(1<<8)|(1<<9)) +#define ATIM 0x72 +#define EPROM_CS_SHIFT 3 +#define EPROM_CK_SHIFT 2 +#define PHY_DELAY 0x78 +#define PHY_CONFIG 0x80 +#define PHY_ADR 0x7c +#define PHY_READ 0x7e +#define CARRIER_SENSE_COUNTER 0x79 //byte +#define SECURITY 0x5f +#define SECURITY_WEP_TX_ENABLE_SHIFT 1 +#define SECURITY_WEP_RX_ENABLE_SHIFT 0 +#define SECURITY_ENCRYP_104 1 +#define SECURITY_ENCRYP_SHIFT 4 +#define SECURITY_ENCRYP_MASK ((1<<4)|(1<<5)) +#define KEY0 0x90 +#define CONFIG2_ANTENNA_SHIFT 6 +#define TX_BEACON_RING_ADDR 0x4c +#define CONFIG0_WEP40_SHIFT 7 +#define CONFIG0_WEP104_SHIFT 6 +#define AGCRESET_SHIFT 5 + + + +/* + * Operational registers offsets in PCI (I/O) space. + * RealTek names are used. + */ + +#define IDR0 0x0000 +#define IDR1 0x0001 +#define IDR2 0x0002 +#define IDR3 0x0003 +#define IDR4 0x0004 +#define IDR5 0x0005 + +/* 0x0006 - 0x0007 - reserved */ + +#define MAR0 0x0008 +#define MAR1 0x0009 +#define MAR2 0x000A +#define MAR3 0x000B +#define MAR4 0x000C +#define MAR5 0x000D +#define MAR6 0x000E +#define MAR7 0x000F + +/* 0x0010 - 0x0017 - reserved */ + +#define TSFTR 0x0018 +#define TSFTR_END 0x001F + +#define TLPDA 0x0020 +#define TLPDA_END 0x0023 +#define TNPDA 0x0024 +#define TNPDA_END 0x0027 +#define THPDA 0x0028 +#define THPDA_END 0x002B + +#define BRSR_8187 0x002C +#define BRSR_8187_END 0x002D +#define BRSR_8187B 0x0034 +#define BRSR_8187B_END 0x0035 + +#define BSSID 0x002E +#define BSSID_END 0x0033 + +/* 0x0034 - 0x0034 - reserved */ + +/* 0x0038 - 0x003B - reserved */ + +#define IMR 0x003C +#define IMR_END 0x003D + +#define ISR 0x003E +#define ISR_END 0x003F + +#define TCR 0x0040 +#define TCR_END 0x0043 + +#define RCR 0x0044 +#define RCR_END 0x0047 + +#define TimerInt 0x0048 +#define TimerInt_END 0x004B + +#define TBDA 0x004C +#define TBDA_END 0x004F + +#define CR9346 0x0050 + +#define CONFIG0 0x0051 +#define CONFIG1 0x0052 +#define CONFIG2 0x0053 + +#define ANA_PARAM 0x0054 +#define ANA_PARAM_END 0x0x0057 + +#define MSR 0x0058 + +#define CONFIG3 0x0059 +#define CONFIG4 0x005A + +#define TESTR 0x005B + +/* 0x005C - 0x005D - reserved */ +#define TFPC_AC 0x005C +#define PSR 0x005E + +#define SCR 0x005F + +/* 0x0060 - 0x006F - reserved */ +#define ANA_PARAM2 0x0060 +#define ANA_PARAM2_END 0x0063 + +#define BcnIntv 0x0070 +#define BcnItv_END 0x0071 + +#define AtimWnd 0x0072 +#define AtimWnd_END 0x0073 + +#define BintrItv 0x0074 +#define BintrItv_END 0x0075 + +#define AtimtrItv 0x0076 +#define AtimtrItv_END 0x0077 + +#define PhyDelay 0x0078 + +//#define CRCount 0x0079 + +#define AckTimeOutReg 0x79 // ACK timeout register, in unit of 4 us. +/* 0x007A - 0x007B - reserved */ +#define BBAddr 0x007C + + +#define PhyAddr 0x007C +#define PhyDataW 0x007D +#define PhyDataR 0x007E +#define RF_Ready 0x007F + +#define PhyCFG 0x0080 +#define PhyCFG_END 0x0083 + +/* following are for rtl8185 */ +#define RFPinsOutput 0x80 +#define RFPinsEnable 0x82 +#define RF_TIMING 0x8c +#define RFPinsSelect 0x84 +#define ANAPARAM2 0x60 +#define RF_PARA 0x88 +#define RFPinsInput 0x86 +#define GP_ENABLE 0x90 +#define GPIO 0x91 +#define HSSI_PARA 0x94 // HSS Parameter +#define SW_CONTROL_GPIO 0x400 +#define CCK_TXAGC 0x9d +#define OFDM_TXAGC 0x9e +#define ANTSEL 0x9f +#define TXAGC_CTL_PER_PACKET_ANT_SEL 0x02 +#define WPA_CONFIG 0xb0 +#define TX_AGC_CTL 0x9c +#define TX_AGC_CTL_PER_PACKET_TXAGC 0x01 +#define TX_AGC_CTL_PERPACKET_GAIN_SHIFT 0 +#define TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT 1 +#define TX_AGC_CTL_FEEDBACK_ANT 2 +#define RESP_RATE 0x34 +#define SIFS 0xb4 +#define DIFS 0xb5 +#define EIFS_8187 0x35 +#define EIFS_8187B 0x2D +#define SLOT 0xb6 +#define CW_VAL 0xbd +#define CW_CONF 0xbc +#define CW_CONF_PERPACKET_RETRY_LIMIT 0x02 +#define CW_CONF_PERPACKET_CW 0x01 +#define CW_CONF_PERPACKET_RETRY_SHIFT 1 +#define CW_CONF_PERPACKET_CW_SHIFT 0 +#define MAX_RESP_RATE_SHIFT 4 +#define MIN_RESP_RATE_SHIFT 0 +#define RATE_FALLBACK 0xbe +#define RATE_FALLBACK_CTL_ENABLE 0x80 +#define RATE_FALLBACK_CTL_AUTO_STEP0 0x00 + +#define ARFR 0x1E0 // Auto Rate Fallback Register (0x1e0 ~ 0x1e2) +#define RMS 0x1EC // Rx Max Pacetk Size (0x1ec[0:12]) + +/* + * 0x0084 - 0x00D3 is selected to page 1 when PSEn bit (bit0, PSR) + * is set to 1 + */ + +#define Wakeup0 0x0084 +#define Wakeup0_END 0x008B + +#define Wakeup1 0x008C +#define Wakeup1_END 0x0093 + +#define Wakeup2LD 0x0094 +#define Wakeup2LD_END 0x009B +#define Wakeup2HD 0x009C +#define Wakeup2HD_END 0x00A3 + +#define Wakeup3LD 0x00A4 +#define Wakeup3LD_END 0x00AB +#define Wakeup3HD 0x00AC +#define Wakeup3HD_END 0x00B3 + +#define Wakeup4LD 0x00B4 +#define Wakeup4LD_END 0x00BB +#define Wakeup4HD 0x00BC +#define Wakeup4HD_END 0x00C3 + +#define CRC0 0x00C4 +#define CRC0_END 0x00C5 +#define CRC1 0x00C6 +#define CRC1_END 0x00C7 +#define CRC2 0x00C8 +#define CRC2_END 0x00C9 +#define CRC3 0x00CA +#define CRC3_END 0x00CB +#define CRC4 0x00CC +#define CRC4_END 0x00CD + +/* 0x00CE - 0x00D3 - reserved */ + + + +/* + * 0x0084 - 0x00D3 is selected to page 0 when PSEn bit (bit0, PSR) + * is set to 0 + */ + +/* 0x0084 - 0x008F - reserved */ + +#define DK0 0x0090 +#define DK0_END 0x009F +#define DK1 0x00A0 +#define DK1_END 0x00AF +#define DK2 0x00B0 +#define DK2_END 0x00BF +#define DK3 0x00C0 +#define DK3_END 0x00CF + +#define GPO 0x90 +#define GPE 0x91 +#define GPI 0x92 + +#define RFTiming 0x008C +#define ACM_CONTROL 0x00BF // ACM Control Registe +#define INT_MIG 0x00E2 // Interrupt Migration (0xE2 ~ 0xE3) +#define TID_AC_MAP 0x00E8 // TID to AC Mapping Register + +#define AC_VO_PARAM 0x00F0 // AC_VO Parameters Record +#define AC_VI_PARAM 0x00F4 // AC_VI Parameters Record +#define AC_BE_PARAM 0x00F8 // AC_BE Parameters Record +#define AC_BK_PARAM 0x00FC // AC_BK Parameters Record + +/* 0x00D0 - 0x00D3 - reserved */ +#define CCK_FALSE_ALARM 0x00D0 +#define OFDM_FALSE_ALARM 0x00D2 + + +/* 0x00D4 - 0x00D7 - reserved */ + +#define CONFIG5 0x00D8 + +#define TPPoll 0x00D9 + +/* 0x00DA - 0x00DB - reserved */ + +#define CWR 0x00DC +#define CWR_END 0x00DD + +#define RetryCTR 0x00DE + +/* 0x00DF - 0x00E3 - reserved */ + +#define RDSAR 0x00E4 +#define RDSAR_END 0x00E7 + +/* 0x00E8 - 0x00EF - reserved */ +#define ANA_PARAM3 0x00EE + +#define FER 0x00F0 +#define FER_END 0x00F3 + +#define FEMR 0x1D4 // Function Event Mask register (0xf4 ~ 0xf7) +//#define FEMR 0x00F4 +#define FEMR_END 0x00F7 + +#define FPSR 0x00F8 +#define FPSR_END 0x00FB + +#define FFER 0x00FC +#define FFER_END 0x00FF + +/* + * 0x0000 - 0x00ff is selected to page 0 when PSEn bit (bit0, PSR) + * is set to 2 + */ +#define RFSW_CTRL 0x272 // 0x272-0x273. + + + +//---------------------------------------------------------------------------- +// 8187B AC_XX_PARAM bits +//---------------------------------------------------------------------------- +#define AC_PARAM_TXOP_LIMIT_OFFSET 16 +#define AC_PARAM_ECW_MAX_OFFSET 12 +#define AC_PARAM_ECW_MIN_OFFSET 8 +#define AC_PARAM_AIFS_OFFSET 0 + +//---------------------------------------------------------------------------- +// 8187B ACM_CONTROL bits (Offset 0xBF, 1 Byte) +//---------------------------------------------------------------------------- +#define VOQ_ACM_EN (0x01 << 7) //BIT7 +#define VIQ_ACM_EN (0x01 << 6) //BIT6 +#define BEQ_ACM_EN (0x01 << 5) //BIT5 +#define ACM_HW_EN (0x01 << 4) //BIT4 +#define TXOPSEL (0x01 << 3) //BIT3 +#define VOQ_ACM_CTL (0x01 << 2) //BIT2 // Set to 1 when AC_VO used time reaches or exceeds the admitted time +#define VIQ_ACM_CTL (0x01 << 1) //BIT1 // Set to 1 when AC_VI used time reaches or exceeds the admitted time +#define BEQ_ACM_CTL (0x01 << 0) //BIT0 // Set to 1 when AC_BE used time reaches or exceeds the admitted time + +//---------------------------------------------------------------------------- +// 8187B RF pins related setting (offset 0xFF80-0xFF87,) +//---------------------------------------------------------------------------- +#define TR_SW_MASK_TX_8187 BIT5 +#define TR_SW_MASK_RX_8187 BIT6 +#define TR_SW_MASK_8187 (TR_SW_MASK_TX_8187 | TR_SW_MASK_RX_8187) + +/* + * Bitmasks for specific register functions. + * Names are derived from the register name and function name. + * + * _[] + * + * this leads to some awkward names... + */ + +#define BRSR_BPLCP ((1<< 8)) +#define BRSR_MBR ((1<< 1)|(1<< 0)) +#define BRSR_MBR_8185 ((1<< 11)|(1<< 10)|(1<< 9)|(1<< 8)|(1<< 7)|(1<< 6)|(1<< 5)|(1<< 4)|(1<< 3)|(1<< 2)|(1<< 1)|(1<< 0)) +#define BRSR_MBR0 ((1<< 0)) +#define BRSR_MBR1 ((1<< 1)) + +#define CR_RST ((1<< 4)) +#define CR_RE ((1<< 3)) +#define CR_TE ((1<< 2)) +#define CR_MulRW ((1<< 0)) + +#define IMR_TXFOVW ((1<<15)) +#define IMR_TimeOut ((1<<14)) +#define IMR_BcnInt ((1<<13)) +#define IMR_ATIMInt ((1<<12)) +#define IMR_TBDER ((1<<11)) +#define IMR_TBDOK ((1<<10)) +#define IMR_THPDER ((1<< 9)) +#define IMR_THPDOK ((1<< 8)) +#define IMR_TNPDER ((1<< 7)) +#define IMR_TNPDOK ((1<< 6)) +#define IMR_RXFOVW ((1<< 5)) +#define IMR_RDU ((1<< 4)) +#define IMR_TLPDER ((1<< 3)) +#define IMR_TLPDOK ((1<< 2)) +#define IMR_RER ((1<< 1)) +#define IMR_ROK ((1<< 0)) + +#define ISR_TXFOVW ((1<<15)) +#define ISR_TimeOut ((1<<14)) +#define ISR_BcnInt ((1<<13)) +#define ISR_ATIMInt ((1<<12)) +#define ISR_TBDER ((1<<11)) +#define ISR_TBDOK ((1<<10)) +#define ISR_THPDER ((1<< 9)) +#define ISR_THPDOK ((1<< 8)) +#define ISR_TNPDER ((1<< 7)) +#define ISR_TNPDOK ((1<< 6)) +#define ISR_RXFOVW ((1<< 5)) +#define ISR_RDU ((1<< 4)) +#define ISR_TLPDER ((1<< 3)) +#define ISR_TLPDOK ((1<< 2)) +#define ISR_RER ((1<< 1)) +#define ISR_ROK ((1<< 0)) + +#define HW_VERID_R8180_F 3 +#define HW_VERID_R8180_ABCD 2 +#define HW_VERID_R8185_ABC 4 +#define HW_VERID_R8185_D 5 + +#define TCR_DurProcMode ((1<<30)) +#define TCR_DISReqQsize ((1<<28)) +#define TCR_HWVERID_MASK ((1<<27)|(1<<26)|(1<<25)) +#define TCR_HWVERID_SHIFT 25 +#define TCR_SWPLCPLEN ((1<<24)) +#define TCR_PLCP_LEN TCR_SAT // rtl8180 +#define TCR_MXDMA_MASK ((1<<23)|(1<<22)|(1<<21)) +#define TCR_MXDMA_1024 6 +#define TCR_MXDMA_2048 7 +#define TCR_MXDMA_SHIFT 21 +#define TCR_DISCW ((1<<20)) +#define TCR_ICV ((1<<19)) +#define TCR_LBK ((1<<18)|(1<<17)) +#define TCR_LBK1 ((1<<18)) +#define TCR_LBK0 ((1<<17)) +#define TCR_CRC ((1<<16)) +#define TCR_SRL_MASK ((1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)) +#define TCR_LRL_MASK ((1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)) +#define TCR_PROBE_NOTIMESTAMP_SHIFT 29 //rtl8185 + +#define RCR_ONLYERLPKT ((1<<31)) +#define RCR_CS_SHIFT 29 +#define RCR_CS_MASK ((1<<30) | (1<<29)) +#define RCR_ENMARP ((1<<28)) +#define RCR_CBSSID ((1<<23)) +#define RCR_APWRMGT ((1<<22)) +#define RCR_ADD3 ((1<<21)) +#define RCR_AMF ((1<<20)) +#define RCR_ACF ((1<<19)) +#define RCR_ADF ((1<<18)) +#define RCR_RXFTH ((1<<15)|(1<<14)|(1<<13)) +#define RCR_RXFTH2 ((1<<15)) +#define RCR_RXFTH1 ((1<<14)) +#define RCR_RXFTH0 ((1<<13)) +#define RCR_AICV ((1<<12)) +#define RCR_MXDMA ((1<<10)|(1<< 9)|(1<< 8)) +#define RCR_MXDMA2 ((1<<10)) +#define RCR_MXDMA1 ((1<< 9)) +#define RCR_MXDMA0 ((1<< 8)) +#define RCR_9356SEL ((1<< 6)) +#define RCR_ACRC32 ((1<< 5)) +#define RCR_AB ((1<< 3)) +#define RCR_AM ((1<< 2)) +#define RCR_APM ((1<< 1)) +#define RCR_AAP ((1<< 0)) + +#define CR9346_EEM ((1<<7)|(1<<6)) +#define CR9346_EEM1 ((1<<7)) +#define CR9346_EEM0 ((1<<6)) +#define CR9346_EECS ((1<<3)) +#define CR9346_EESK ((1<<2)) +#define CR9346_EED1 ((1<<1)) +#define CR9346_EED0 ((1<<0)) + +#define CONFIG0_WEP104 ((1<<6)) +#define CONFIG0_LEDGPO_En ((1<<4)) +#define CONFIG0_Aux_Status ((1<<3)) +#define CONFIG0_GL ((1<<1)|(1<<0)) +#define CONFIG0_GL1 ((1<<1)) +#define CONFIG0_GL0 ((1<<0)) + +#define CONFIG1_LEDS ((1<<7)|(1<<6)) +#define CONFIG1_LEDS1 ((1<<7)) +#define CONFIG1_LEDS0 ((1<<6)) +#define CONFIG1_LWACT ((1<<4)) +#define CONFIG1_MEMMAP ((1<<3)) +#define CONFIG1_IOMAP ((1<<2)) +#define CONFIG1_VPD ((1<<1)) +#define CONFIG1_PMEn ((1<<0)) + +#define CONFIG2_LCK ((1<<7)) +#define CONFIG2_ANT ((1<<6)) +#define CONFIG2_DPS ((1<<3)) +#define CONFIG2_PAPE_sign ((1<<2)) +#define CONFIG2_PAPE_time ((1<<1)|(1<<0)) +#define CONFIG2_PAPE_time1 ((1<<1)) +#define CONFIG2_PAPE_time0 ((1<<0)) + +#define CONFIG3_GNTSel ((1<<7)) +#define CONFIG3_PARM_En ((1<<6)) +#define CONFIG3_Magic ((1<<5)) +#define CONFIG3_CardB_En ((1<<3)) +#define CONFIG3_CLKRUN_En ((1<<2)) +#define CONFIG3_FuncRegEn ((1<<1)) +#define CONFIG3_FBtbEn ((1<<0)) + +#define CONFIG4_VCOPDN ((1<<7)) +#define CONFIG4_PWROFF ((1<<6)) +#define CONFIG4_PWRMGT ((1<<5)) +#define CONFIG4_LWPME ((1<<4)) +#define CONFIG4_LWPTN ((1<<2)) +#define CONFIG4_RFTYPE ((1<<1)|(1<<0)) +#define CONFIG4_RFTYPE1 ((1<<1)) +#define CONFIG4_RFTYPE0 ((1<<0)) + +#define CONFIG5_TX_FIFO_OK ((1<<7)) +#define CONFIG5_RX_FIFO_OK ((1<<6)) +#define CONFIG5_CALON ((1<<5)) +#define CONFIG5_EACPI ((1<<2)) +#define CONFIG5_LANWake ((1<<1)) +#define CONFIG5_PME_STS ((1<<0)) + +#define MSR_LINK_MASK ((1<<2)|(1<<3)) +#define MSR_LINK_MANAGED 2 +#define MSR_LINK_NONE 0 +#define MSR_LINK_SHIFT 2 +#define MSR_LINK_ADHOC 1 +#define MSR_LINK_MASTER 3 +#define MSR_LINK_ENEDCA (1<<4) + +#define PSR_GPO ((1<<7)) +#define PSR_GPI ((1<<6)) +#define PSR_LEDGPO1 ((1<<5)) +#define PSR_LEDGPO0 ((1<<4)) +#define PSR_UWF ((1<<1)) +#define PSR_PSEn ((1<<0)) + +#define SCR_KM ((1<<5)|(1<<4)) +#define SCR_KM1 ((1<<5)) +#define SCR_KM0 ((1<<4)) +#define SCR_TXSECON ((1<<1)) +#define SCR_RXSECON ((1<<0)) + +#define BcnItv_BcnItv (0x01FF) + +#define AtimWnd_AtimWnd (0x01FF) + +#define BintrItv_BintrItv (0x01FF) + +#define AtimtrItv_AtimtrItv (0x01FF) + +#define PhyDelay_PhyDelay ((1<<2)|(1<<1)|(1<<0)) + +#define TPPoll_BQ ((1<<7)) +#define TPPoll_HPQ ((1<<6)) +#define TPPoll_NPQ ((1<<5)) +#define TPPoll_LPQ ((1<<4)) +#define TPPoll_SBQ ((1<<3)) +#define TPPoll_SHPQ ((1<<2)) +#define TPPoll_SNPQ ((1<<1)) +#define TPPoll_SLPQ ((1<<0)) + +#define CWR_CW (0x01FF) + +#define FER_INTR ((1<<15)) +#define FER_GWAKE ((1<< 4)) + +#define FEMR_INTR ((1<<15)) +#define FEMR_WKUP ((1<<14)) +#define FEMR_GWAKE ((1<< 4)) + +#define FPSR_INTR ((1<<15)) +#define FPSR_GWAKE ((1<< 4)) + +#define FFER_INTR ((1<<15)) +#define FFER_GWAKE ((1<< 4)) + + +//---------------------------------------------------------------------------- +// 818xB AnaParm & AnaParm2 Register +//---------------------------------------------------------------------------- +/* +#ifdef RTL8185B_FPGA +#define ANAPARM_FPGA_ON 0xa0000b59 +//#define ANAPARM_FPGA_OFF +#define ANAPARM2_FPGA_ON 0x860dec11 +//#define ANAPARM2_FPGA_OFF +#else //ASIC +*/ +#define ANAPARM_ASIC_ON 0x45090658 +//#define ANAPARM_ASIC_OFF +#define ANAPARM2_ASIC_ON 0x727f3f52 +//#define ANAPARM2_ASIC_OFF +//#endif +//by amy for power save +#define RF_CHANGE_BY_SW BIT31 +#define RF_CHANGE_BY_HW BIT30 +#define RF_CHANGE_BY_PS BIT29 +#define RF_CHANGE_BY_IPS BIT28 +#define ANAPARM_ASIC_ON 0x45090658 +#define ANAPARM2_ASIC_ON 0x727f3f52 + +#define ANAPARM_ON ANAPARM_ASIC_ON +#define ANAPARM2_ON ANAPARM2_ASIC_ON +#define TFPC 0x5C // Tx FIFO Packet Count for BK, BE, VI, VO queues (2 bytes) +#define Config4_PowerOff BIT6 // Turn ON/Off RF Power(RFMD) +#define ANAPARM_OFF 0x51480658 +#define ANAPARM2_OFF 0x72003f70 +//by amy for power save + +#define MAX_DOZE_WAITING_TIMES_87B 500 + +#endif diff --git a/drivers/net/wireless/rtl8187b/r8180_pm.c b/drivers/net/wireless/rtl8187b/r8180_pm.c new file mode 100644 index 0000000..90a0bd9 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_pm.c @@ -0,0 +1,97 @@ +/* + Power management interface routines. + Written by Mariusz Matuszek. + This code is currently just a placeholder for later work and + does not do anything useful. + + This is part of rtl8180 OpenSource driver. + Copyright (C) Andrea Merello 2004 + Released under the terms of GPL (General Public Licence) +*/ + +#ifdef CONFIG_RTL8180_PM + + +#include "r8180_hw.h" +#include "r8180_pm.h" +#include "r8187.h" +int rtl8180_save_state (struct pci_dev *dev, u32 state) +{ + printk(KERN_NOTICE "r8180 save state call (state %u).\n", state); + return(-EAGAIN); +} + +//netif_running is set to 0 before system call rtl8180_close, +//netif_running is set to 1 before system call rtl8180_open, +//if open success it will not change, or it change to 0; +int rtl8187_suspend (struct usb_interface *intf, pm_message_t state) +{ + struct r8180_priv *priv; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct net_device *dev = usb_get_intfdata(intf); +#else + //struct net_device *dev = (struct net_device *)ptr; +#endif + + printk("====>%s \n", __func__); + priv=ieee80211_priv(dev); + + if(dev) { + /* save the old rfkill state and then power off it */ + priv->eInactivePowerState = priv->eRFPowerState; + /* power off the wifi by default */ + r8187b_wifi_change_rfkill_state(dev, eRfOff); + + if (!netif_running(dev)) { + //printk(KERN_WARNING "UI or other close dev before suspend, go out suspend function\n"); + return 0; + } + + dev->netdev_ops->ndo_stop(dev); + netif_device_detach(dev); + } + return 0; +} + + +int rtl8187_resume (struct usb_interface *intf) +{ + struct r8180_priv *priv; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct net_device *dev = usb_get_intfdata(intf); +#else + //struct net_device *dev = (struct net_device *)ptr; +#endif + + printk("====>%s \n", __func__); + priv=ieee80211_priv(dev); + + if(dev) { + /* resume the old rfkill state */ + r8187b_wifi_change_rfkill_state(dev, priv->eInactivePowerState); + + if (!netif_running(dev)){ + //printk(KERN_WARNING "UI or other close dev before suspend, go out resume function\n"); + return 0; + } + + netif_device_attach(dev); + dev->netdev_ops->ndo_open(dev); + } + + return 0; +} + + +int rtl8180_enable_wake (struct pci_dev *dev, u32 state, int enable) +{ + + //printk(KERN_NOTICE "r8180 enable wake call (state %u, enable %d).\n", + // state, enable); + return 0; + //return(-EAGAIN); +} + + + +#endif //CONFIG_RTL8180_PM diff --git a/drivers/net/wireless/rtl8187b/r8180_pm.h b/drivers/net/wireless/rtl8187b/r8180_pm.h new file mode 100644 index 0000000..efcba1d --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_pm.h @@ -0,0 +1,28 @@ +/* + Power management interface routines. + Written by Mariusz Matuszek. + This code is currently just a placeholder for later work and + does not do anything useful. + + This is part of rtl8180 OpenSource driver. + Copyright (C) Andrea Merello 2004 + Released under the terms of GPL (General Public Licence) + +*/ + +#ifdef CONFIG_RTL8180_PM + +#ifndef R8180_PM_H +#define R8180_PM_H + +#include +#include + +int rtl8180_save_state (struct pci_dev *dev, u32 state); +int rtl8187_suspend (struct usb_interface *intf,pm_message_t state); +int rtl8187_resume (struct usb_interface *intf); +int rtl8180_enable_wake (struct pci_dev *dev, u32 state, int enable); + +#endif //R8180_PM_H + +#endif // CONFIG_RTL8180_PM diff --git a/drivers/net/wireless/rtl8187b/r8180_rtl8225.c b/drivers/net/wireless/rtl8187b/r8180_rtl8225.c new file mode 100644 index 0000000..e53d4cd --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_rtl8225.c @@ -0,0 +1,1007 @@ +/* + This is part of the rtl8180-sa2400 driver + released under the GPL (See file COPYING for details). + Copyright (c) 2005 Andrea Merello + + This files contains programming code for the rtl8225 + radio frontend. + + *Many* thanks to Realtek Corp. for their great support! + +*/ + + + +#include "r8180_hw.h" +#include "r8180_rtl8225.h" +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + +#define USE_8051_3WIRE 1 + +u8 rtl8225_threshold[]={ + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, +}; + +u8 rtl8225_gain[]={ + 0x23,0x88,0x7c,0xa5,// -82dbm + 0x23,0x88,0x7c,0xb5,// -82dbm + 0x23,0x88,0x7c,0xc5,// -82dbm + 0x33,0x80,0x79,0xc5,// -78dbm + 0x43,0x78,0x76,0xc5,// -74dbm + 0x53,0x60,0x73,0xc5,// -70dbm + 0x63,0x58,0x70,0xc5,// -66dbm +}; + +u16 rtl8225bcd_rxgain[]={ + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb + +}; + + + +u8 rtl8225_tx_gain_cck_ofdm[]={ + 0x02,0x06,0x0e,0x1e,0x3e,0x7e +}; + + +u8 rtl8225_tx_power_ofdm[]={ + 0x80,0x90,0xa2,0xb5,0xcb,0xe4 +}; + + +u8 rtl8225_tx_power_cck_ch14[]={ + 0x18,0x17,0x15,0x0c,0x00,0x00,0x00,0x00, + 0x1b,0x1a,0x17,0x0e,0x00,0x00,0x00,0x00, + 0x1f,0x1e,0x1a,0x0f,0x00,0x00,0x00,0x00, + 0x22,0x21,0x1d,0x11,0x00,0x00,0x00,0x00, + 0x26,0x25,0x21,0x13,0x00,0x00,0x00,0x00, + 0x2b,0x2a,0x25,0x15,0x00,0x00,0x00,0x00 +}; + + +u8 rtl8225_tx_power_cck[]={ + 0x18,0x17,0x15,0x11,0x0c,0x08,0x04,0x02, + 0x1b,0x1a,0x17,0x13,0x0e,0x09,0x04,0x02, + 0x1f,0x1e,0x1a,0x15,0x10,0x0a,0x05,0x02, + 0x22,0x21,0x1d,0x18,0x11,0x0b,0x06,0x02, + 0x26,0x25,0x21,0x1b,0x14,0x0d,0x06,0x03, + 0x2b,0x2a,0x25,0x1e,0x16,0x0e,0x07,0x03 +}; + +u8 rtl8225_agc[]={ + 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96, + 0x95,0x94,0x93,0x92,0x91,0x90,0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86, + 0x85,0x84,0x83,0x82,0x81,0x80,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36, + 0x35,0x34,0x33,0x32,0x31,0x30,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26, + 0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16, + 0x15,0x14,0x13,0x12,0x11,0x10,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06, + 0x05,0x04,0x03,0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +}; + +u32 rtl8225_chan[] = { + 0, //dummy channel 0 + 0x085c, //1 + 0x08dc, //2 + 0x095c, //3 + 0x09dc, //4 + 0x0a5c, //5 + 0x0adc, //6 + 0x0b5c, //7 + 0x0bdc, //8 + 0x0c5c, //9 + 0x0cdc, //10 + 0x0d5c, //11 + 0x0ddc, //12 + 0x0e5c, //13 + //0x0f5c, //14 + 0x0f72, // 14 +}; + +void rtl8225_set_gain(struct net_device *dev, short gain) +{ + write_phy_ofdm(dev, 0x0d, rtl8225_gain[gain * 4]); + write_phy_ofdm(dev, 0x1b, rtl8225_gain[gain * 4 + 2]); + write_phy_ofdm(dev, 0x1d, rtl8225_gain[gain * 4 + 3]); + write_phy_ofdm(dev, 0x23, rtl8225_gain[gain * 4 + 1]); + +} + + +void write_rtl8225(struct net_device *dev, u8 adr, u16 data) +{ +//in windows the delays in this function was del from 85 to 87, +//here we mod to sleep, or The CPU occupany is too hight. LZM 31/10/2008 + +#ifdef USE_8051_3WIRE + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + //u8 bit; + //u16 wReg80, wReg82, wReg84; + u16 wReg80, wReg84; + + wReg80 = read_nic_word(dev, RFPinsOutput); + wReg80 &= 0xfff3; +// wReg82 = read_nic_word(dev, RFPinsEnable); + wReg84 = read_nic_word(dev, RFPinsSelect); + // 3-wire should be controled by HW when we finish SW 3-wire programming. 2005.08.10, by rcnjko. + //wReg84 &= 0xfff0; + wReg84 &= 0xfff8; //modified by david according to windows segment code. + + // We must set SW enabled before terminating HW 3-wire, 2005.07.29, by rcnjko. +// write_nic_word(dev, RFPinsEnable, (wReg82|0x0007)); // Set To Output Enable + write_nic_word(dev, RFPinsSelect, (wReg84|0x0007)); // Set To SW Switch +// force_pci_posting(dev); +// udelay(10); // + + write_nic_word(dev, 0x80, (BB_HOST_BANG_EN|wReg80)); // Set SI_EN (RFLE) +// force_pci_posting(dev); +// udelay(2); + //twreg.struc.enableB = 0; + write_nic_word(dev, 0x80, (wReg80)); // Clear SI_EN (RFLE) +// force_pci_posting(dev); +// udelay(10); + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + adr, 0x8225, &data, 2, HZ / 2); + + // write_nic_word(dev, 0x80, (BB_HOST_BANG_EN|wReg80)); +// force_pci_posting(dev); +// udelay(10); + + write_nic_word(dev, 0x80, (wReg80|0x0004)); + write_nic_word(dev, 0x84, (wReg84|0x0000));// Set To SW Switch + + if(priv->card_type == USB) + ;// msleep(2); + else + ; // rtl8185_rf_pins_enable(dev); + +#else + int i; + u16 out,select; + u8 bit; + u32 bangdata = (data << 4) | (adr & 0xf); + struct r8180_priv *priv = ieee80211_priv(dev); + + out = read_nic_word(dev, RFPinsOutput) & 0xfff3; + + write_nic_word(dev,RFPinsEnable, + (read_nic_word(dev,RFPinsEnable) | 0x7)); + + select = read_nic_word(dev, RFPinsSelect); + + write_nic_word(dev, RFPinsSelect, select | 0x7 | + ((priv->card_type == USB) ? 0 : SW_CONTROL_GPIO)); + +// force_pci_posting(dev); +// udelay(10); + + write_nic_word(dev, RFPinsOutput, out | BB_HOST_BANG_EN );//| 0x1fff); + +// force_pci_posting(dev); +// udelay(2); + + write_nic_word(dev, RFPinsOutput, out); + +// force_pci_posting(dev); +// udelay(10); + + + for(i=15; i>=0;i--){ + + bit = (bangdata & (1<> i; + + write_nic_word(dev, RFPinsOutput, bit | out); + + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + + i--; + bit = (bangdata & (1<> i; + + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + + write_nic_word(dev, RFPinsOutput, bit | out); + + } + + write_nic_word(dev, RFPinsOutput, out | BB_HOST_BANG_EN); + +// force_pci_posting(dev); +// udelay(10); + + write_nic_word(dev, RFPinsOutput, out | + ((priv->card_type == USB) ? 4 : BB_HOST_BANG_EN)); + + write_nic_word(dev, RFPinsSelect, select | + ((priv->card_type == USB) ? 0 : SW_CONTROL_GPIO)); + + if(priv->card_type == USB) + ;// msleep(2); + else +// rtl8185_rf_pins_enable(dev); +#endif +} + + +void write_rtl8225_patch(struct net_device *dev, u8 adr, u16 data) +{ + + int i; + u16 out,select; + u8 bit; + u32 bangdata = (data << 4) | (adr & 0xf); + struct r8180_priv *priv = ieee80211_priv(dev); + + out = read_nic_word(dev, RFPinsOutput) & 0xfff3; + + write_nic_word(dev,RFPinsEnable, + (read_nic_word(dev,RFPinsEnable) | 0x7)); + + select = read_nic_word(dev, RFPinsSelect); + + write_nic_word(dev, RFPinsSelect, select | 0x7 | + ((priv->card_type == USB) ? 0 : SW_CONTROL_GPIO)); + + force_pci_posting(dev); + udelay(10); + + write_nic_word(dev, RFPinsOutput, out | BB_HOST_BANG_EN );//| 0x1fff); + + force_pci_posting(dev); + udelay(2); + + write_nic_word(dev, RFPinsOutput, out); + + force_pci_posting(dev); + udelay(10); + + for(i=15; i>=0;i--){ + + bit = (bangdata & (1<> i; + + write_nic_word(dev, RFPinsOutput, bit | out); + + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + + i--; + bit = (bangdata & (1<> i; + + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + write_nic_word(dev, RFPinsOutput, bit | out | BB_HOST_BANG_CLK); + + write_nic_word(dev, RFPinsOutput, bit | out); + + } + + write_nic_word(dev, RFPinsOutput, out | BB_HOST_BANG_EN); + + force_pci_posting(dev); + udelay(10); + + write_nic_word(dev, RFPinsOutput, out | + ((priv->card_type == USB) ? 4 : BB_HOST_BANG_EN)); + + write_nic_word(dev, RFPinsSelect, select | + ((priv->card_type == USB) ? 0 : SW_CONTROL_GPIO)); + + if(priv->card_type == USB) + mdelay(2); + else + rtl8185_rf_pins_enable(dev); + +} + +void rtl8225_rf_close(struct net_device *dev) +{ + write_rtl8225(dev, 0x4, 0x1f); + + force_pci_posting(dev); + mdelay(1); + + rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_OFF); + rtl8185_set_anaparam2(dev, RTL8225_ANAPARAM2_OFF); +} + +#ifdef ENABLE_DOT11D +// +// Description: +// Map dBm into Tx power index according to +// current HW model, for example, RF and PA, and +// current wireless mode. +// +s8 +DbmToTxPwrIdx( + struct r8180_priv *priv, + WIRELESS_MODE WirelessMode, + s32 PowerInDbm + ) +{ + bool bUseDefault = true; + s8 TxPwrIdx = 0; + +#ifdef CONFIG_RTL818X_S + // + // 071011, SD3 SY: + // OFDM Power in dBm = Index * 0.5 + 0 + // CCK Power in dBm = Index * 0.25 + 13 + // + if(priv->card_8185 >= VERSION_8187S_B) + { + s32 tmp = 0; + + if(WirelessMode == WIRELESS_MODE_G) + { + bUseDefault = false; + tmp = (2 * PowerInDbm); + + if(tmp < 0) + TxPwrIdx = 0; + else if(tmp > 40) // 40 means 20 dBm. + TxPwrIdx = 40; + else + TxPwrIdx = (s8)tmp; + } + else if(WirelessMode == WIRELESS_MODE_B) + { + bUseDefault = false; + tmp = (4 * PowerInDbm) - 52; + + if(tmp < 0) + TxPwrIdx = 0; + else if(tmp > 28) // 28 means 20 dBm. + TxPwrIdx = 28; + else + TxPwrIdx = (s8)tmp; + } + } +#endif + + // + // TRUE if we want to use a default implementation. + // We shall set it to FALSE when we have exact translation formular + // for target IC. 070622, by rcnjko. + // + if(bUseDefault) + { + if(PowerInDbm < 0) + TxPwrIdx = 0; + else if(PowerInDbm > 35) + TxPwrIdx = 35; + else + TxPwrIdx = (u8)PowerInDbm; + } + + return TxPwrIdx; +} +#endif + + +short rtl8225_rf_set_sens(struct net_device *dev, short sens) +{ + if (sens <0 || sens > 6) return -1; + + if(sens > 4) + write_rtl8225(dev, 0x0c, 0x850); + else + write_rtl8225(dev, 0x0c, 0x50); + + sens= 6-sens; + rtl8225_set_gain(dev, sens); + + write_phy_cck(dev, 0x41, rtl8225_threshold[sens]); + return 0; + +} + +void rtl8225_SetTXPowerLevel(struct net_device *dev, short ch) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + int GainIdx; + int GainSetting; + int i; + u8 power; + u8 *cck_power_table; + u8 max_cck_power_level; + u8 max_ofdm_power_level; + u8 min_ofdm_power_level; + u8 cck_power_level = 0xff & priv->chtxpwr[ch]; + u8 ofdm_power_level = 0xff & priv->chtxpwr_ofdm[ch]; + +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(priv->ieee80211) && + IS_DOT11D_STATE_DONE(priv->ieee80211) ) + { + //PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(priv->ieee80211); + u8 MaxTxPwrInDbm = DOT11D_GetMaxTxPwrInDbm(priv->ieee80211, ch); + u8 CckMaxPwrIdx = DbmToTxPwrIdx(priv, WIRELESS_MODE_B, MaxTxPwrInDbm); + u8 OfdmMaxPwrIdx = DbmToTxPwrIdx(priv, WIRELESS_MODE_G, MaxTxPwrInDbm); + + //printk("Max Tx Power dBm (%d) => CCK Tx power index : %d, OFDM Tx power index: %d\n", MaxTxPwrInDbm, CckMaxPwrIdx, OfdmMaxPwrIdx); + + //printk("EEPROM channel(%d) => CCK Tx power index: %d, OFDM Tx power index: %d\n", + // ch, cck_power_level, ofdm_power_level); + + if(cck_power_level > CckMaxPwrIdx) + cck_power_level = CckMaxPwrIdx; + if(ofdm_power_level > OfdmMaxPwrIdx) + ofdm_power_level = OfdmMaxPwrIdx; + } + + //priv->CurrentCckTxPwrIdx = cck_power_level; + //priv->CurrentOfdmTxPwrIdx = ofdm_power_level; +#endif + + + if(priv->card_type == USB){ + max_cck_power_level = 11; + max_ofdm_power_level = 25; // 12 -> 25 + min_ofdm_power_level = 10; + }else{ + max_cck_power_level = 35; + max_ofdm_power_level = 35; + min_ofdm_power_level = 0; + } + if( priv->TrSwitchState == TR_SW_TX ) + { + printk("SetTxPowerLevel8187(): Origianl OFDM Tx power level %d\n", ofdm_power_level); + ofdm_power_level -= GetTxOfdmHighPowerBias(dev); + cck_power_level -= GetTxCckHighPowerBias(dev); + printk("SetTxPowerLevel8187(): Adjusted OFDM Tx power level %d for we are in High Power state\n", + ofdm_power_level); + printk("SetTxPowerLevel8187(): Adjusted CCK Tx power level %d for we are in High Power state\n", + cck_power_level); + } + + + + /* CCK power setting */ + if(cck_power_level > max_cck_power_level) + cck_power_level = max_cck_power_level; + GainIdx=cck_power_level % 6; + GainSetting=cck_power_level / 6; + + if(ch == 14) + cck_power_table = rtl8225_tx_power_cck_ch14; + else + cck_power_table = rtl8225_tx_power_cck; + +// if(priv->card_8185 == 1 && priv->card_8185_Bversion ){ + /*Ver B*/ +// write_nic_byte(dev, TX_GAIN_CCK, rtl8225_tx_gain_cck_ofdm[GainSetting]); +// }else{ + /*Ver C - D */ + write_nic_byte(dev, CCK_TXAGC, rtl8225_tx_gain_cck_ofdm[GainSetting]>>1); +// } + + for(i=0;i<8;i++){ + + power = cck_power_table[GainIdx * 8 + i]; + write_phy_cck(dev, 0x44 + i, power); + } + + /* FIXME Is this delay really needeed ? */ + force_pci_posting(dev); + mdelay(1); + + /* OFDM power setting */ +// Old: +// if(ofdm_power_level > max_ofdm_power_level) +// ofdm_power_level = 35; +// ofdm_power_level += min_ofdm_power_level; +// Latest: + if(ofdm_power_level > (max_ofdm_power_level - min_ofdm_power_level)) + ofdm_power_level = max_ofdm_power_level; + else + ofdm_power_level += min_ofdm_power_level; + if(ofdm_power_level > 35) + ofdm_power_level = 35; +// + + GainIdx=ofdm_power_level % 6; + GainSetting=ofdm_power_level / 6; +#if 1 +// if(priv->card_type == USB){ + rtl8185_set_anaparam2(dev,RTL8225_ANAPARAM2_ON); + + write_phy_ofdm(dev,2,0x42); + write_phy_ofdm(dev,6,0); + write_phy_ofdm(dev,8,0); +// } +#endif +// if(priv->card_8185 == 1 && priv->card_8185_Bversion){ +// /*Ver B*/ +// write_nic_byte(dev, TX_GAIN_OFDM, rtl8225_tx_gain_cck_ofdm[GainSetting]); +// }else{ + /*Ver C - D */ + write_nic_byte(dev, OFDM_TXAGC, rtl8225_tx_gain_cck_ofdm[GainSetting]>>1); +// } + + + power = rtl8225_tx_power_ofdm[GainIdx]; + + write_phy_ofdm(dev, 0x5, power); + write_phy_ofdm(dev, 0x7, power); + + force_pci_posting(dev); + mdelay(1); + //write_nic_byte(dev, TX_AGC_CONTROL,4); +} + +void rtl8225_rf_set_chan(struct net_device *dev, short ch) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + short gset = (priv->ieee80211->state == IEEE80211_LINKED && + ieee80211_is_54g(priv->ieee80211->current_network)) || + priv->ieee80211->iw_mode == IW_MODE_MONITOR; + int eifs_addr; + + if(NIC_8187 == priv->card_8187) { + eifs_addr = EIFS_8187; + } else { + eifs_addr = EIFS_8187B; + } + +#ifdef ENABLE_DOT11D + if(!IsLegalChannel(priv->ieee80211, ch) ) + { + printk("channel(%d). is invalide\n", ch); + return; + } +#endif + + rtl8225_SetTXPowerLevel(dev, ch); + + write_rtl8225(dev, 0x7, rtl8225_chan[ch]); + + force_pci_posting(dev); + mdelay(10); + + write_nic_byte(dev,SIFS,0x22);// SIFS: 0x22 + + if(gset) + write_nic_byte(dev,DIFS,20); //DIFS: 20 + else + write_nic_byte(dev,DIFS,0x24); //DIFS: 36 + + if(priv->ieee80211->state == IEEE80211_LINKED && + ieee80211_is_shortslot(priv->ieee80211->current_network)) + write_nic_byte(dev,SLOT,0x9); //SLOT: 9 + + else + write_nic_byte(dev,SLOT,0x14); //SLOT: 20 (0x14) + + + if(gset){ + write_nic_byte(dev,eifs_addr,91 - 20); // EIFS: 91 (0x5B) + write_nic_byte(dev,CW_VAL,0x73); //CW VALUE: 0x37 + //DMESG("using G net params"); + }else{ + write_nic_byte(dev,eifs_addr,91 - 0x24); // EIFS: 91 (0x5B) + write_nic_byte(dev,CW_VAL,0xa5); //CW VALUE: 0x37 + //DMESG("using B net params"); + } + + +} + +void rtl8225_host_pci_init(struct net_device *dev) +{ + write_nic_word(dev, RFPinsOutput, 0x480); + + rtl8185_rf_pins_enable(dev); + + //if(priv->card_8185 == 2 && priv->enable_gpio0 ) /* version D */ + //write_nic_word(dev, RFPinsSelect, 0x88); + //else + write_nic_word(dev, RFPinsSelect, 0x88 | SW_CONTROL_GPIO); /* 0x488 | SW_CONTROL_GPIO */ + + write_nic_byte(dev, GP_ENABLE, 0); + + force_pci_posting(dev); + mdelay(200); + + write_nic_word(dev, GP_ENABLE, 0xff & (~(1<<6))); /* bit 6 is for RF on/off detection */ + + +} + +void rtl8225_host_usb_init(struct net_device *dev) +{ + write_nic_byte(dev,RFPinsSelect+1,0); + + write_nic_byte(dev,GPIO,0); + + write_nic_byte_E(dev,0x53,read_nic_byte_E(dev,0x53) | (1<<7)); + + write_nic_byte(dev,RFPinsSelect+1,4); + + write_nic_byte(dev,GPIO,0x20); + + write_nic_byte(dev,GP_ENABLE,0); + + + /* Config BB & RF */ + write_nic_word(dev, RFPinsOutput, 0x80); + + write_nic_word(dev, RFPinsSelect, 0x80); + + write_nic_word(dev, RFPinsEnable, 0x80); + + + mdelay(100); + + mdelay(1000); + +} + +void rtl8225_rf_init(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int i; + short channel = 1; + u16 brsr; + int brsr_addr; + + if(NIC_8187 == priv->card_8187) { + brsr_addr = BRSR_8187; + } else { + brsr_addr = BRSR_8187B; + } + + + priv->chan = channel; + + rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_ON); + + + if(priv->card_type == USB) + rtl8225_host_usb_init(dev); + else + rtl8225_host_pci_init(dev); + + write_nic_dword(dev, RF_TIMING, 0x000a8008); + + //brsr = read_nic_word(dev, BRSR); + brsr = read_nic_word(dev, brsr_addr); + + //write_nic_word(dev, BRSR, 0xffff); + write_nic_word(dev, brsr_addr, 0xffff); + + write_nic_dword(dev, RF_PARA, 0x100044); + + #if 1 //0->1 + rtl8180_set_mode(dev, EPROM_CMD_CONFIG); + write_nic_byte(dev, CONFIG3, 0x44); + rtl8180_set_mode(dev, EPROM_CMD_NORMAL); + #endif + + if(priv->card_type == USB){ + rtl8185_rf_pins_enable(dev); + + mdelay(1000); + } + + write_rtl8225(dev, 0x0, 0x67); mdelay(1); + + + write_rtl8225(dev, 0x1, 0xfe0); mdelay(1); + + write_rtl8225(dev, 0x2, 0x44d); mdelay(1); + + write_rtl8225(dev, 0x3, 0x441); mdelay(1); + + if(priv->card_type == USB) + write_rtl8225(dev, 0x4, 0x486); + else + write_rtl8225(dev, 0x4, 0x8be); + + mdelay(1); + + + /* version B & C */ + + if(priv->card_type == USB) + write_rtl8225(dev, 0x5, 0xbc0); + else if(priv->card_type == MINIPCI) + write_rtl8225(dev, 0x5, 0xbc0 + 3 +(6<<3)); + else + write_rtl8225(dev, 0x5, 0xbc0 + (6<<3)); + + mdelay(1); +// } + + write_rtl8225(dev, 0x6, 0xae6); mdelay(1); + + write_rtl8225(dev, 0x7, ((priv->card_type == USB)? 0x82a : rtl8225_chan[channel])); mdelay(1); + + write_rtl8225(dev, 0x8, 0x1f); mdelay(1); + + write_rtl8225(dev, 0x9, 0x334); mdelay(1); + + write_rtl8225(dev, 0xa, 0xfd4); mdelay(1); + + write_rtl8225(dev, 0xb, 0x391); mdelay(1); + + write_rtl8225(dev, 0xc, 0x50); mdelay(1); + + + write_rtl8225(dev, 0xd, 0x6db); mdelay(1); + + write_rtl8225(dev, 0xe, 0x29); mdelay(1); + + write_rtl8225(dev, 0xf, 0x914); + + if(priv->card_type == USB){ + //force_pci_posting(dev); + mdelay(100); + } + + write_rtl8225(dev, 0x2, 0xc4d); + + if(priv->card_type == USB){ + // force_pci_posting(dev); + mdelay(200); + + write_rtl8225(dev, 0x2, 0x44d); + + // force_pci_posting(dev); + mdelay(100); + + }//End of if(priv->card_type == USB) + /* FIXME!! rtl8187 we have to check if calibrarion + * is successful and eventually cal. again (repeat + * the two write on reg 2) + */ + force_pci_posting(dev); + + mdelay(100); //200 for 8187 + + //if(priv->card_type != USB) /* maybe not needed even for 8185 */ +// write_rtl8225(dev, 0x7, rtl8225_chan[channel]); + + write_rtl8225(dev, 0x0, 0x127); + + for(i=0;i<95;i++){ + write_rtl8225(dev, 0x1, (u8)(i+1)); + + /* version B & C & D*/ + + write_rtl8225(dev, 0x2, rtl8225bcd_rxgain[i]); + } + + write_rtl8225(dev, 0x0, 0x27); + + +// //if(priv->card_type != USB){ +// write_rtl8225(dev, 0x2, 0x44d); +// write_rtl8225(dev, 0x7, rtl8225_chan[channel]); +// write_rtl8225(dev, 0x2, 0x47d); +// +// force_pci_posting(dev); +// mdelay(100); +// +// write_rtl8225(dev, 0x2, 0x44d); +// //} + + write_rtl8225(dev, 0x0, 0x22f); + + if(priv->card_type != USB) + rtl8185_rf_pins_enable(dev); + + for(i=0;i<128;i++){ + write_phy_ofdm(dev, 0xb, rtl8225_agc[i]); + + mdelay(1); + write_phy_ofdm(dev, 0xa, (u8)i+ 0x80); + + mdelay(1); + } + + force_pci_posting(dev); + mdelay(1); + + write_phy_ofdm(dev, 0x0, 0x1); mdelay(1); + write_phy_ofdm(dev, 0x1, 0x2); mdelay(1); + write_phy_ofdm(dev, 0x2, ((priv->card_type == USB)? 0x42 : 0x62)); mdelay(1); + write_phy_ofdm(dev, 0x3, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x4, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x5, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x6, 0x40); mdelay(1); + write_phy_ofdm(dev, 0x7, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x8, 0x40); mdelay(1); + write_phy_ofdm(dev, 0x9, 0xfe); mdelay(1); + + /* ver C & D */ + write_phy_ofdm(dev, 0xa, 0x9); mdelay(1); + + //write_phy_ofdm(dev, 0x18, 0xef); + // } + //} + write_phy_ofdm(dev, 0xb, 0x80); mdelay(1); + + write_phy_ofdm(dev, 0xc, 0x1);mdelay(1); + + + //if(priv->card_type != USB) + //write_phy_ofdm(dev, 0xd, 0x33); // <> + + write_phy_ofdm(dev, 0xe, 0xd3);mdelay(1); + + write_phy_ofdm(dev, 0xf, 0x38);mdelay(1); +/*ver D & 8187*/ +// } + +// if(priv->card_8185 == 1 && priv->card_8185_Bversion) +// write_phy_ofdm(dev, 0x10, 0x04);/*ver B*/ +// else + write_phy_ofdm(dev, 0x10, 0x84);mdelay(1); +/*ver C & D & 8187*/ + + write_phy_ofdm(dev, 0x11, 0x06);mdelay(1); +/*agc resp time 700*/ + + +// if(priv->card_8185 == 2){ + /* Ver D & 8187*/ + write_phy_ofdm(dev, 0x12, 0x20);mdelay(1); + + write_phy_ofdm(dev, 0x13, 0x20);mdelay(1); + + write_phy_ofdm(dev, 0x14, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x15, 0x40); mdelay(1); + write_phy_ofdm(dev, 0x16, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x17, 0x40); mdelay(1); + +// if (priv->card_type == USB) +// write_phy_ofdm(dev, 0x18, 0xef); + + write_phy_ofdm(dev, 0x18, 0xef);mdelay(1); + + + write_phy_ofdm(dev, 0x19, 0x19); mdelay(1); + write_phy_ofdm(dev, 0x1a, 0x20); mdelay(1); + +// if (priv->card_type != USB){ +// if(priv->card_8185 == 1 && priv->card_8185_Bversion) +// write_phy_ofdm(dev, 0x1b, 0x66); /* Ver B */ +// else + write_phy_ofdm(dev, 0x1b, 0x76);mdelay(1); + /* Ver C & D */ //FIXME:MAYBE not needed +// } + + write_phy_ofdm(dev, 0x1c, 0x4);mdelay(1); + + /*ver D & 8187*/ + write_phy_ofdm(dev, 0x1e, 0x95);mdelay(1); + + write_phy_ofdm(dev, 0x1f, 0x75); mdelay(1); + +// } + + write_phy_ofdm(dev, 0x20, 0x1f);mdelay(1); + + write_phy_ofdm(dev, 0x21, 0x27);mdelay(1); + + write_phy_ofdm(dev, 0x22, 0x16);mdelay(1); + +// if(priv->card_type != USB) + //write_phy_ofdm(dev, 0x23, 0x43); //FIXME maybe not needed // <> + + write_phy_ofdm(dev, 0x24, 0x46); mdelay(1); + write_phy_ofdm(dev, 0x25, 0x20); mdelay(1); + write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); + write_phy_ofdm(dev, 0x27, 0x88); mdelay(1); +/* Ver C & D & 8187*/ + + // <> Set init. gain to m74dBm. + + rtl8225_set_gain(dev,4); + /*write_phy_ofdm(dev, 0x0d, 0x43); mdelay(1); + write_phy_ofdm(dev, 0x1b, 0x76); mdelay(1); + write_phy_ofdm(dev, 0x1d, 0xc5); mdelay(1); + write_phy_ofdm(dev, 0x23, 0x78); mdelay(1); +*/ + //if(priv->card_type == USB); + // rtl8225_set_gain_usb(dev, 1); /* FIXME this '2' is random */ + + write_phy_cck(dev, 0x0, 0x98); mdelay(1); + write_phy_cck(dev, 0x3, 0x20); mdelay(1); + write_phy_cck(dev, 0x4, 0x7e); mdelay(1); + write_phy_cck(dev, 0x5, 0x12); mdelay(1); + write_phy_cck(dev, 0x6, 0xfc); mdelay(1); + write_phy_cck(dev, 0x7, 0x78);mdelay(1); + /* Ver C & D & 8187*/ + + write_phy_cck(dev, 0x8, 0x2e);mdelay(1); + + write_phy_cck(dev, 0x10, ((priv->card_type == USB) ? 0x9b: 0x93)); mdelay(1); + write_phy_cck(dev, 0x11, 0x88); mdelay(1); + write_phy_cck(dev, 0x12, 0x47); mdelay(1); + write_phy_cck(dev, 0x13, 0xd0); /* Ver C & D & 8187*/ + + write_phy_cck(dev, 0x19, 0x0); + write_phy_cck(dev, 0x1a, 0xa0); + write_phy_cck(dev, 0x1b, 0x8); + write_phy_cck(dev, 0x40, 0x86); /* CCK Carrier Sense Threshold */ + + write_phy_cck(dev, 0x41, 0x8d);mdelay(1); + + + write_phy_cck(dev, 0x42, 0x15); mdelay(1); + write_phy_cck(dev, 0x43, 0x18); mdelay(1); + write_phy_cck(dev, 0x44, 0x1f); mdelay(1); + write_phy_cck(dev, 0x45, 0x1e); mdelay(1); + write_phy_cck(dev, 0x46, 0x1a); mdelay(1); + write_phy_cck(dev, 0x47, 0x15); mdelay(1); + write_phy_cck(dev, 0x48, 0x10); mdelay(1); + write_phy_cck(dev, 0x49, 0xa); mdelay(1); + write_phy_cck(dev, 0x4a, 0x5); mdelay(1); + write_phy_cck(dev, 0x4b, 0x2); mdelay(1); + write_phy_cck(dev, 0x4c, 0x5);mdelay(1); + + + write_nic_byte(dev, 0x5b, 0x0d); mdelay(1); + + + +// <> +// // TESTR 0xb 8187 +// write_phy_cck(dev, 0x10, 0x93);// & 0xfb); +// +// //if(priv->card_type != USB){ +// write_phy_ofdm(dev, 0x2, 0x62); +// write_phy_ofdm(dev, 0x6, 0x0); +// write_phy_ofdm(dev, 0x8, 0x0); +// //} + + rtl8225_SetTXPowerLevel(dev, channel); + + write_phy_cck(dev, 0x10, 0x9b); mdelay(1); /* Rx ant A, 0xdb for B */ + write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); /* Rx ant A, 0x10 for B */ + + rtl8185_tx_antenna(dev, 0x3); /* TX ant A, 0x0 for B */ + + /* switch to high-speed 3-wire + * last digit. 2 for both cck and ofdm + */ + if(priv->card_type == USB) + write_nic_dword(dev, 0x94, 0x3dc00002); + else{ + write_nic_dword(dev, 0x94, 0x15c00002); + rtl8185_rf_pins_enable(dev); + } + +// if(priv->card_type != USB) +// rtl8225_set_gain(dev, 4); /* FIXME this '1' is random */ // <> +// rtl8225_set_mode(dev, 1); /* FIXME start in B mode */ // <> +// +// /* make sure is waken up! */ +// write_rtl8225(dev,0x4, 0x9ff); +// rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_ON); +// rtl8185_set_anaparam2(dev, RTL8225_ANAPARAM2_ON); + + rtl8225_rf_set_chan(dev, priv->chan); + + //write_nic_word(dev,BRSR,brsr); + +} diff --git a/drivers/net/wireless/rtl8187b/r8180_rtl8225.h b/drivers/net/wireless/rtl8187b/r8180_rtl8225.h new file mode 100644 index 0000000..138e0d0 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_rtl8225.h @@ -0,0 +1,77 @@ +/* + This is part of the rtl8180-sa2400 driver + released under the GPL (See file COPYING for details). + Copyright (c) 2005 Andrea Merello + + This files contains programming code for the rtl8225 + radio frontend. + + *Many* thanks to Realtek Corp. for their great support! + +*/ + +#ifndef RTL8225H +#define RTL8225H + +#include "r8187.h" + +#define RTL8225_ANAPARAM_ON 0xa0000a59 + +// FIXME: OFF ANAPARAM MIGHT BE WRONG! +#define RTL8225_ANAPARAM_OFF 0xa00beb59 +#define RTL8225_ANAPARAM2_OFF 0x840dec11 + +#define RTL8225_ANAPARAM2_ON 0x860c7312 + +void rtl8225_rf_init(struct net_device *dev); +void rtl8225z2_rf_init(struct net_device *dev); +void rtl8225z2_rf_set_chan(struct net_device *dev, short ch); +short rtl8225_is_V_z2(struct net_device *dev); +void rtl8225_rf_set_chan(struct net_device *dev,short ch); +void rtl8225_rf_close(struct net_device *dev); +short rtl8225_rf_set_sens(struct net_device *dev, short sens); +void rtl8225_host_pci_init(struct net_device *dev); +void rtl8225_host_usb_init(struct net_device *dev); +void write_rtl8225(struct net_device *dev, u8 adr, u16 data); +void rtl8225z2_rf_set_mode(struct net_device *dev) ; +void rtl8185_rf_pins_enable(struct net_device *dev); +void rtl8180_set_mode(struct net_device *dev,int mode); +void UpdateInitialGain(struct net_device *dev); +void UpdateCCKThreshold(struct net_device *dev); +void rtl8225_SetTXPowerLevel(struct net_device *dev, short ch); +void rtl8225z2_SetTXPowerLevel(struct net_device *dev, short ch); + +#define RTL8225_RF_MAX_SENS 6 +#define RTL8225_RF_DEF_SENS 4 + +extern inline char GetTxOfdmHighPowerBias(struct net_device *dev) +{ + // + // We should always adjust our Tx Power for 8187 and 8187B. + // It was ever recommended not to adjust Tx Power of 8187B with Atheros AP + // for throughput by David, but now we found it is not the issue to impact + // the Atheros's problem and also no adjustion for Tx Power will cause "low" + // throughput. By Bruce, 2007-07-03. + // + return 10; +} + +// +// Description: +// Return Tx power level to minus if we are in high power state. +// +// Note: +// Adjust it according to RF if required. +// +extern inline char GetTxCckHighPowerBias(struct net_device *dev) +{ + return 7; +} + + + +extern u8 rtl8225_agc[]; + +extern u32 rtl8225_chan[]; + +#endif diff --git a/drivers/net/wireless/rtl8187b/r8180_rtl8225z2.c b/drivers/net/wireless/rtl8187b/r8180_rtl8225z2.c new file mode 100644 index 0000000..b661ab5 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_rtl8225z2.c @@ -0,0 +1,2092 @@ +/* + This is part of the rtl8180-sa2400 driver + released under the GPL (See file COPYING for details). + Copyright (c) 2005 Andrea Merello + + This files contains programming code for the rtl8225 + radio frontend. + + *Many* thanks to Realtek Corp. for their great support! + +*/ + + + +#include "r8180_hw.h" +#include "r8180_rtl8225.h" +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + +//2005.11.16 +u8 rtl8225z2_threshold[]={ + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, +}; + +// 0xd 0x19 0x1b 0x21 +u8 rtl8225z2_gain_bg[]={ + 0x23, 0x15, 0xa5, // -82-1dbm + 0x23, 0x15, 0xb5, // -82-2dbm + 0x23, 0x15, 0xc5, // -82-3dbm + 0x33, 0x15, 0xc5, // -78dbm + 0x43, 0x15, 0xc5, // -74dbm + 0x53, 0x15, 0xc5, // -70dbm + 0x63, 0x15, 0xc5, // -66dbm +}; + +u8 rtl8225z2_gain_a[]={ + 0x13,0x27,0x5a,//,0x37,// -82dbm + 0x23,0x23,0x58,//,0x37,// -82dbm + 0x33,0x1f,0x56,//,0x37,// -82dbm + 0x43,0x1b,0x54,//,0x37,// -78dbm + 0x53,0x17,0x51,//,0x37,// -74dbm + 0x63,0x24,0x4f,//,0x37,// -70dbm + 0x73,0x0f,0x4c,//,0x37,// -66dbm +}; +static u32 MAC_REG_TABLE[][3]={ + {0xf0, 0x32, 0000}, {0xf1, 0x32, 0000}, {0xf2, 0x00, 0000}, {0xf3, 0x00, 0000}, + {0xf4, 0x32, 0000}, {0xf5, 0x43, 0000}, {0xf6, 0x00, 0000}, {0xf7, 0x00, 0000}, + {0xf8, 0x46, 0000}, {0xf9, 0xa4, 0000}, {0xfa, 0x00, 0000}, {0xfb, 0x00, 0000}, + {0xfc, 0x96, 0000}, {0xfd, 0xa4, 0000}, {0xfe, 0x00, 0000}, {0xff, 0x00, 0000}, + + {0x58, 0x4b, 0001}, {0x59, 0x00, 0001}, {0x5a, 0x4b, 0001}, {0x5b, 0x00, 0001}, + {0x60, 0x4b, 0001}, {0x61, 0x09, 0001}, {0x62, 0x4b, 0001}, {0x63, 0x09, 0001}, + {0xce, 0x0f, 0001}, {0xcf, 0x00, 0001}, {0xe0, 0xff, 0001}, {0xe1, 0x0f, 0001}, + {0xe2, 0x00, 0001}, {0xf0, 0x4e, 0001}, {0xf1, 0x01, 0001}, {0xf2, 0x02, 0001}, + {0xf3, 0x03, 0001}, {0xf4, 0x04, 0001}, {0xf5, 0x05, 0001}, {0xf6, 0x06, 0001}, + {0xf7, 0x07, 0001}, {0xf8, 0x08, 0001}, + + {0x4e, 0x00, 0002}, {0x0c, 0x04, 0002}, {0x21, 0x61, 0002}, {0x22, 0x68, 0002}, + {0x23, 0x6f, 0002}, {0x24, 0x76, 0002}, {0x25, 0x7d, 0002}, {0x26, 0x84, 0002}, + {0x27, 0x8d, 0002}, {0x4d, 0x08, 0002}, {0x50, 0x05, 0002}, {0x51, 0xf5, 0002}, + {0x52, 0x04, 0002}, {0x53, 0xa0, 0002}, {0x54, 0x1f, 0002}, {0x55, 0x23, 0002}, + {0x56, 0x45, 0002}, {0x57, 0x67, 0002}, {0x58, 0x08, 0002}, {0x59, 0x08, 0002}, + {0x5a, 0x08, 0002}, {0x5b, 0x08, 0002}, {0x60, 0x08, 0002}, {0x61, 0x08, 0002}, + {0x62, 0x08, 0002}, {0x63, 0x08, 0002}, {0x64, 0xcf, 0002}, {0x72, 0x56, 0002}, + {0x73, 0x9a, 0002}, + + {0x34, 0xf0, 0000}, {0x35, 0x0f, 0000}, {0x5b, 0x40, 0000}, {0x84, 0x88, 0000}, + {0x85, 0x24, 0000}, {0x88, 0x54, 0000}, {0x8b, 0xb8, 0000}, {0x8c, 0x07, 0000}, + {0x8d, 0x00, 0000}, {0x94, 0x1b, 0000}, {0x95, 0x12, 0000}, {0x96, 0x00, 0000}, + {0x97, 0x06, 0000}, {0x9d, 0x1a, 0000}, {0x9f, 0x10, 0000}, {0xb4, 0x22, 0000}, + {0xbe, 0x80, 0000}, {0xdb, 0x00, 0000}, {0xee, 0x00, 0000}, {0x91, 0x01, 0000}, + //lzm mode 0x91 form 0x03->0x01 open GPIO BIT1, + //because Polling methord will rurn off Radio + //the first time when read GPI(0x92). + //because after 0x91:bit1 form 1->0, there will + //be time for 0x92:bit1 form 0->1 + + {0x4c, 0x00, 0002}, {0x9f, 0x00, 0003}, {0x8c, 0x01, 0000}, {0x8d, 0x10, 0000}, + {0x8e, 0x08, 0000}, {0x8f, 0x00, 0000} +}; + +static u8 ZEBRA_AGC[]={ + 0, + 0x5e,0x5e,0x5e,0x5e,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4d,0x4b,0x49,0x47, + 0x45,0x43,0x41,0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2d,0x2b,0x29,0x27, + 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0d,0x0b,0x09,0x07, + 0x05,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x19,0x19,0x19,0x019,0x19,0x19,0x19,0x19,0x19,0x20,0x21,0x22,0x23,0x24,0x25,0x26, + 0x26,0x27,0x27,0x28,0x28,0x29,0x2a,0x2a,0x2a,0x2b,0x2b,0x2b,0x2c,0x2c,0x2c,0x2d, + 0x2d,0x2d,0x2d,0x2e,0x2e,0x2e,0x2e,0x2f,0x2f,0x2f,0x30,0x30,0x31,0x31,0x31,0x31, + 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31 +}; + +static u32 ZEBRA_RF_RX_GAIN_TABLE[]={ + 0, + 0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,0x0408,0x0409, + 0x040a,0x040b,0x0502,0x0503,0x0504,0x0505,0x0540,0x0541, + 0x0542,0x0543,0x0544,0x0545,0x0580,0x0581,0x0582,0x0583, + 0x0584,0x0585,0x0588,0x0589,0x058a,0x058b,0x0643,0x0644, + 0x0645,0x0680,0x0681,0x0682,0x0683,0x0684,0x0685,0x0688, + 0x0689,0x068a,0x068b,0x068c,0x0742,0x0743,0x0744,0x0745, + 0x0780,0x0781,0x0782,0x0783,0x0784,0x0785,0x0788,0x0789, + 0x078a,0x078b,0x078c,0x078d,0x0790,0x0791,0x0792,0x0793, + 0x0794,0x0795,0x0798,0x0799,0x079a,0x079b,0x079c,0x079d, + 0x07a0,0x07a1,0x07a2,0x07a3,0x07a4,0x07a5,0x07a8,0x07a9, + 0x03aa,0x03ab,0x03ac,0x03ad,0x03b0,0x03b1,0x03b2,0x03b3, + 0x03b4,0x03b5,0x03b8,0x03b9,0x03ba,0x03bb,0x03bb +}; + +// Use the new SD3 given param, by shien chang, 2006.07.14 + +static u8 OFDM_CONFIG[]={ + // 0x00 + 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, + + // 0x10 + 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, + 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, + + // 0x20 + 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f, + 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, + + // 0x30 + 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, + 0x6d, 0x3c, 0xfb, 0x07//0xc7 + }; + +//2005.11.16, +u8 ZEBRA2_CCK_OFDM_GAIN_SETTING[]={ + 0x00,0x01,0x02,0x03,0x04,0x05, + 0x06,0x07,0x08,0x09,0x0a,0x0b, + 0x0c,0x0d,0x0e,0x0f,0x10,0x11, + 0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d, + 0x1e,0x1f,0x20,0x21,0x22,0x23, +}; +//- +u16 rtl8225z2_rxgain[]={ + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb + +}; + + +/* + from 0 to 0x23 +u8 rtl8225_tx_gain_cck_ofdm[]={ + 0x02,0x06,0x0e,0x1e,0x3e,0x7e +}; +*/ + +//- +u8 rtl8225z2_tx_power_ofdm[]={ + 0x42,0x00,0x40,0x00,0x40 +}; + + +//- +u8 rtl8225z2_tx_power_cck_ch14[]={ + 0x36,0x35,0x2e,0x1b,0x00,0x00,0x00,0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, +}; + + +//- +u8 rtl8225z2_tx_power_cck[]={ + 0x36,0x35,0x2e,0x25,0x1c,0x12,0x09,0x04, + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 +}; + +#ifdef ENABLE_DOT11D +// +// Description: +// Map dBm into Tx power index according to +// current HW model, for example, RF and PA, and +// current wireless mode. +// +s8 +rtl8187B_DbmToTxPwrIdx( + struct r8180_priv *priv, + WIRELESS_MODE WirelessMode, + s32 PowerInDbm + ) +{ + bool bUseDefault = true; + s8 TxPwrIdx = 0; + +#ifdef CONFIG_RTL818X_S + // + // 071011, SD3 SY: + // OFDM Power in dBm = Index * 0.5 + 0 + // CCK Power in dBm = Index * 0.25 + 13 + // + if(priv->card_8185 >= VERSION_8187S_B) + { + s32 tmp = 0; + + if(WirelessMode == WIRELESS_MODE_G) + { + bUseDefault = false; + tmp = (2 * PowerInDbm); + + if(tmp < 0) + TxPwrIdx = 0; + else if(tmp > 40) // 40 means 20 dBm. + TxPwrIdx = 40; + else + TxPwrIdx = (s8)tmp; + } + else if(WirelessMode == WIRELESS_MODE_B) + { + bUseDefault = false; + tmp = (4 * PowerInDbm) - 52; + + if(tmp < 0) + TxPwrIdx = 0; + else if(tmp > 28) // 28 means 20 dBm. + TxPwrIdx = 28; + else + TxPwrIdx = (s8)tmp; + } + } +#endif + + // + // TRUE if we want to use a default implementation. + // We shall set it to FALSE when we have exact translation formular + // for target IC. 070622, by rcnjko. + // + if(bUseDefault) + { + if(PowerInDbm < 0) + TxPwrIdx = 0; + else if(PowerInDbm > 35) + TxPwrIdx = 35; + else + TxPwrIdx = (u8)PowerInDbm; + } + + return TxPwrIdx; +} +#endif + + +void rtl8225z2_set_gain(struct net_device *dev, short gain) +{ + u8* rtl8225_gain; + struct r8180_priv *priv = ieee80211_priv(dev); + + u8 mode = priv->ieee80211->mode; + + if(mode == IEEE_B || mode == IEEE_G) + rtl8225_gain = rtl8225z2_gain_bg; + else + rtl8225_gain = rtl8225z2_gain_a; + + //write_phy_ofdm(dev, 0x0d, rtl8225_gain[gain * 3]); + //write_phy_ofdm(dev, 0x19, rtl8225_gain[gain * 3 + 1]); + //write_phy_ofdm(dev, 0x1b, rtl8225_gain[gain * 3 + 2]); + //2005.11.17, by ch-hsu + write_phy_ofdm(dev, 0x0b, rtl8225_gain[gain * 3]); + write_phy_ofdm(dev, 0x1b, rtl8225_gain[gain * 3 + 1]); + write_phy_ofdm(dev, 0x1d, rtl8225_gain[gain * 3 + 2]); + write_phy_ofdm(dev, 0x21, 0x37); + +} + +u32 read_rtl8225(struct net_device *dev, u8 adr) +{ + u32 data2Write = ((u32)(adr & 0x1f)) << 27; + u32 dataRead; + u32 mask; + u16 oval,oval2,oval3,tmp; +// ThreeWireReg twreg; +// ThreeWireReg tdata; + int i; + short bit, rw; + + u8 wLength = 6; + u8 rLength = 12; + u8 low2high = 0; + + oval = read_nic_word(dev, RFPinsOutput); + oval2 = read_nic_word(dev, RFPinsEnable); + oval3 = read_nic_word(dev, RFPinsSelect); + write_nic_word(dev, RFPinsEnable, (oval2|0xf)); + write_nic_word(dev, RFPinsSelect, (oval3|0xf)); + + dataRead = 0; + + oval &= ~0xf; + + write_nic_word(dev, RFPinsOutput, oval | BB_HOST_BANG_EN ); udelay(4); + + write_nic_word(dev, RFPinsOutput, oval ); udelay(5); + + rw = 0; + + mask = (low2high) ? 0x01 : (((u32)0x01)<<(32-1)); + for(i = 0; i < wLength/2; i++) + { + bit = ((data2Write&mask) != 0) ? 1 : 0; + write_nic_word(dev, RFPinsOutput, bit|oval | rw); udelay(1); + + write_nic_word(dev, RFPinsOutput, bit|oval | BB_HOST_BANG_CLK | rw); udelay(2); + write_nic_word(dev, RFPinsOutput, bit|oval | BB_HOST_BANG_CLK | rw); udelay(2); + + mask = (low2high) ? (mask<<1): (mask>>1); + + if(i == 2) + { + rw = BB_HOST_BANG_RW; + write_nic_word(dev, RFPinsOutput, bit|oval | BB_HOST_BANG_CLK | rw); udelay(2); + write_nic_word(dev, RFPinsOutput, bit|oval | rw); udelay(2); + break; + } + + bit = ((data2Write&mask) != 0) ? 1: 0; + + write_nic_word(dev, RFPinsOutput, oval|bit|rw| BB_HOST_BANG_CLK); udelay(2); + write_nic_word(dev, RFPinsOutput, oval|bit|rw| BB_HOST_BANG_CLK); udelay(2); + + write_nic_word(dev, RFPinsOutput, oval| bit |rw); udelay(1); + + mask = (low2high) ? (mask<<1) : (mask>>1); + } + + //twreg.struc.clk = 0; + //twreg.struc.data = 0; + write_nic_word(dev, RFPinsOutput, rw|oval); udelay(2); + mask = (low2high) ? 0x01 : (((u32)0x01) << (12-1)); + + // We must set data pin to HW controled, otherwise RF can't driver it and + // value RF register won't be able to read back properly. 2006.06.13, by rcnjko. + write_nic_word(dev, RFPinsEnable,((oval2|0xe) & (~0x01))); + + for(i = 0; i < rLength; i++) + { + write_nic_word(dev, RFPinsOutput, rw|oval); udelay(1); + + write_nic_word(dev, RFPinsOutput, rw|oval|BB_HOST_BANG_CLK); udelay(2); + write_nic_word(dev, RFPinsOutput, rw|oval|BB_HOST_BANG_CLK); udelay(2); + write_nic_word(dev, RFPinsOutput, rw|oval|BB_HOST_BANG_CLK); udelay(2); + tmp = read_nic_word(dev, RFPinsInput); + + dataRead |= (tmp & BB_HOST_BANG_CLK ? mask : 0); + + write_nic_word(dev, RFPinsOutput, (rw|oval)); udelay(2); + + mask = (low2high) ? (mask<<1) : (mask>>1); + } + + write_nic_word(dev, RFPinsOutput, BB_HOST_BANG_EN|BB_HOST_BANG_RW|oval); udelay(2); + + write_nic_word(dev, RFPinsEnable, oval2); + write_nic_word(dev, RFPinsSelect, oval3); // Set To SW Switch + write_nic_word(dev, RFPinsOutput, 0x3a0); + + return dataRead; + +} +short rtl8225_is_V_z2(struct net_device *dev) +{ + short vz2 = 1; + //set VCO-PDN pin +// printk("%s()\n", __FUNCTION__); + write_nic_word(dev, RFPinsOutput, 0x0080); + write_nic_word(dev, RFPinsSelect, 0x0080); + write_nic_word(dev, RFPinsEnable, 0x0080); + + //lzm mod for up take too long time 20081201 + //mdelay(100); + //mdelay(1000); + + /* sw to reg pg 1 */ + write_rtl8225(dev, 0, 0x1b7); + /* reg 8 pg 1 = 23*/ + if( read_rtl8225(dev, 8) != 0x588) + vz2 = 0; + + else /* reg 9 pg 1 = 24 */ + if( read_rtl8225(dev, 9) != 0x700) + vz2 = 0; + + /* sw back to pg 0 */ + write_rtl8225(dev, 0, 0xb7); + + return vz2; + +} + +void rtl8225z2_SetTXPowerLevel(struct net_device *dev, short ch) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + +// int GainIdx; +// int GainSetting; + int i; + u8 power; + u8 *cck_power_table; + u8 max_cck_power_level; + u8 min_cck_power_level; + u8 max_ofdm_power_level; + u8 min_ofdm_power_level; + s8 cck_power_level = 0xff & priv->chtxpwr[ch]; + s8 ofdm_power_level = 0xff & priv->chtxpwr_ofdm[ch]; + u8 hw_version = priv->card_8187_Bversion; + +#ifdef ENABLE_DOT11D + if(IS_DOT11D_ENABLE(priv->ieee80211) && + IS_DOT11D_STATE_DONE(priv->ieee80211) ) + { + //PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(priv->ieee80211); + u8 MaxTxPwrInDbm = DOT11D_GetMaxTxPwrInDbm(priv->ieee80211, ch); + u8 CckMaxPwrIdx = rtl8187B_DbmToTxPwrIdx(priv, WIRELESS_MODE_B, MaxTxPwrInDbm); + u8 OfdmMaxPwrIdx = rtl8187B_DbmToTxPwrIdx(priv, WIRELESS_MODE_G, MaxTxPwrInDbm); + + //printk("Max Tx Power dBm (%d) => CCK Tx power index : %d, OFDM Tx power index: %d\n", MaxTxPwrInDbm, CckMaxPwrIdx, OfdmMaxPwrIdx); + + //printk("EEPROM channel(%d) => CCK Tx power index: %d, OFDM Tx power index: %d\n", + // ch, cck_power_level, ofdm_power_level); + + if(cck_power_level > CckMaxPwrIdx) + cck_power_level = CckMaxPwrIdx; + if(ofdm_power_level > OfdmMaxPwrIdx) + ofdm_power_level = OfdmMaxPwrIdx; + } + + //priv->CurrentCckTxPwrIdx = cck_power_level; + //priv->CurrentOfdmTxPwrIdx = ofdm_power_level; +#endif + + if (NIC_8187B == priv->card_8187) + { + if (hw_version == VERSION_8187B_B) + { + min_cck_power_level = 0; + max_cck_power_level = 15; + min_ofdm_power_level = 2; + max_ofdm_power_level = 17; + }else + { + min_cck_power_level = 7; + max_cck_power_level = 22; + min_ofdm_power_level = 10; + max_ofdm_power_level = 25; + } + + if( priv->TrSwitchState == TR_SW_TX ) + { + //printk("SetTxPowerLevel8187(): Origianl OFDM Tx power level %d, adjust value = %d\n", ofdm_power_level,GetTxOfdmHighPowerBias(dev)); + ofdm_power_level -= GetTxOfdmHighPowerBias(dev); + cck_power_level -= GetTxCckHighPowerBias(dev); + //printk("SetTxPowerLevel8187(): Adjusted OFDM Tx power level %d for we are in High Power state\n", + // ofdm_power_level); + //printk("SetTxPowerLevel8187(): Adjusted CCK Tx power level %d for we are in High Power state\n", + // cck_power_level); + } + /* CCK power setting */ + if(cck_power_level > (max_cck_power_level -min_cck_power_level)) + cck_power_level = max_cck_power_level; + else + cck_power_level += min_cck_power_level; + cck_power_level += priv->cck_txpwr_base; + + if(cck_power_level > 35) + cck_power_level = 35; + if(cck_power_level < 0) + cck_power_level = 0; + + if(ch == 14) + cck_power_table = rtl8225z2_tx_power_cck_ch14; + else + cck_power_table = rtl8225z2_tx_power_cck; + if (hw_version == VERSION_8187B_B) + { + if (cck_power_level <= 6){ + } + else if (cck_power_level <=11){ + cck_power_table += 8; + } + else{ + cck_power_table += (8*2); + } + }else{ + if (cck_power_level<=5){ + }else if(cck_power_level<=11){ + cck_power_table += 8; + }else if(cck_power_level <= 17){ + cck_power_table += 8*2; + }else{ + cck_power_table += 8*3; + } + } + + + + for(i=0;i<8;i++){ + + power = cck_power_table[i]; + write_phy_cck(dev, 0x44 + i, power); + } + + //write_nic_byte(dev, TX_GAIN_CCK, power); + //2005.11.17, + write_nic_byte(dev, CCK_TXAGC, (ZEBRA2_CCK_OFDM_GAIN_SETTING[cck_power_level]*2)); + +// force_pci_posting(dev); +// msleep(1); +//in windows the delay was del from 85 to 87, +//here we mod to sleep, or The CPU occupany is too hight. LZM 31/10/2008 + + /* OFDM power setting */ + // Old: + // if(ofdm_power_level > max_ofdm_power_level) + // ofdm_power_level = 35; + // ofdm_power_level += min_ofdm_power_level; + // Latest: + if(ofdm_power_level > (max_ofdm_power_level - min_ofdm_power_level)) + ofdm_power_level = max_ofdm_power_level; + else + ofdm_power_level += min_ofdm_power_level; + + ofdm_power_level += priv->ofdm_txpwr_base; + + if(ofdm_power_level > 35) + ofdm_power_level = 35; + + if(ofdm_power_level < 0) + ofdm_power_level = 0; + write_nic_byte(dev, OFDM_TXAGC, ZEBRA2_CCK_OFDM_GAIN_SETTING[ofdm_power_level]*2); + + if (hw_version == VERSION_8187B_B) + { + if(ofdm_power_level<=11){ + write_phy_ofdm(dev, 0x87, 0x60); + write_phy_ofdm(dev, 0x89, 0x60); + } + else{ + write_phy_ofdm(dev, 0x87, 0x5c); + write_phy_ofdm(dev, 0x89, 0x5c); + } + }else{ + if(ofdm_power_level<=11){ + write_phy_ofdm(dev, 0x87, 0x5c); + write_phy_ofdm(dev, 0x89, 0x5c); + } + if(ofdm_power_level<=17){ + write_phy_ofdm(dev, 0x87, 0x54); + write_phy_ofdm(dev, 0x89, 0x54); + } + else{ + write_phy_ofdm(dev, 0x87, 0x50); + write_phy_ofdm(dev, 0x89, 0x50); + } + } +// force_pci_posting(dev); +// msleep(1); +//in windows the delay was del from 85 to 87, +//and here we mod to sleep, or The CPU occupany is too hight. LZM 31/10/2008 + }else if(NIC_8187 == priv->card_8187) { + min_cck_power_level = 0; + max_cck_power_level = 15; + min_ofdm_power_level = 10; + max_ofdm_power_level = 25; + if(cck_power_level > (max_cck_power_level -min_cck_power_level)) + cck_power_level = max_cck_power_level; + else + cck_power_level += min_cck_power_level; + cck_power_level += priv->cck_txpwr_base; + + if(cck_power_level > 35) + cck_power_level = 35; + + if(ch == 14) + cck_power_table = rtl8225z2_tx_power_cck_ch14; + else + cck_power_table = rtl8225z2_tx_power_cck; + for(i=0;i<8;i++){ + power = cck_power_table[i]; + write_phy_cck(dev, 0x44 + i, power); + } + + //write_nic_byte(dev, TX_GAIN_CCK, power); + //2005.11.17, + write_nic_byte(dev, CCK_TXAGC, ZEBRA2_CCK_OFDM_GAIN_SETTING[cck_power_level]); + +// force_pci_posting(dev); +// msleep(1); +//in windows the delay was del from 85 to 87, +//and here we mod to sleep, or The CPU occupany is too hight. LZM 31/10/2008 + if(ofdm_power_level > (max_ofdm_power_level - min_ofdm_power_level)) + ofdm_power_level = max_ofdm_power_level; + else + ofdm_power_level += min_ofdm_power_level; + + ofdm_power_level += priv->ofdm_txpwr_base; + + if(ofdm_power_level > 35) + ofdm_power_level = 35; + write_nic_byte(dev, OFDM_TXAGC, ZEBRA2_CCK_OFDM_GAIN_SETTING[ofdm_power_level]); + + rtl8185_set_anaparam2(dev,RTL8225_ANAPARAM2_ON); + + write_phy_ofdm(dev,2,0x42); + write_phy_ofdm(dev,5,0); + write_phy_ofdm(dev,6,0x40); + write_phy_ofdm(dev,7,0); + write_phy_ofdm(dev,8,0x40); + } + +} + +void rtl8225z2_rf_set_chan(struct net_device *dev, short ch) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + short gset = (priv->ieee80211->state == IEEE80211_LINKED && + ieee80211_is_54g(priv->ieee80211->current_network)) || + priv->ieee80211->iw_mode == IW_MODE_MONITOR; + int eifs_addr; + + down(&priv->set_chan_sem); + + if(NIC_8187 == priv->card_8187) { + eifs_addr = EIFS_8187; + } else { + eifs_addr = EIFS_8187B; + } + +#ifdef ENABLE_DOT11D + if(!IsLegalChannel(priv->ieee80211, ch) ) + { + printk("channel(%d). is invalide\n", ch); + up(&priv->set_chan_sem); + return; + } +#endif + //87B not do it FIXME + rtl8225z2_SetTXPowerLevel(dev, ch); + + //write_nic_byte(dev,0x7,(u8)rtl8225_chan[ch]); + write_rtl8225(dev, 0x7, rtl8225_chan[ch]); + + force_pci_posting(dev); + //mdelay(10); +//in windows the delay was del from 85 to 87, +//and here we mod to sleep, or The CPU occupany is too hight. LZM 31/10/2008 + if(NIC_8187 == priv->card_8187){ + write_nic_byte(dev,SIFS,0x22);// SIFS: 0x22 + + if(gset) + write_nic_byte(dev,DIFS,20); //DIFS: 20 + else + write_nic_byte(dev,DIFS,0x24); //DIFS: 36 + + if(priv->ieee80211->state == IEEE80211_LINKED && + ieee80211_is_shortslot(priv->ieee80211->current_network)) + write_nic_byte(dev,SLOT,0x9); //SLOT: 9 + + else + write_nic_byte(dev,SLOT,0x14); //SLOT: 20 (0x14) + + + if(gset){ + write_nic_byte(dev,eifs_addr,91 - 20); // EIFS: 91 (0x5B) + write_nic_byte(dev,CW_VAL,0x73); //CW VALUE: 0x37 + //DMESG("using G net params"); + }else{ + write_nic_byte(dev,eifs_addr,91 - 0x24); // EIFS: 91 (0x5B) + write_nic_byte(dev,CW_VAL,0xa5); //CW VALUE: 0x37 + //DMESG("using B net params"); + } + } + + else { +#ifdef THOMAS_TURBO + if(priv->ieee80211->current_network.Turbo_Enable && priv->ieee80211->iw_mode == IW_MODE_INFRA){ + write_nic_word(dev,AC_VO_PARAM,0x5114); + write_nic_word(dev,AC_VI_PARAM,0x5114); + write_nic_word(dev,AC_BE_PARAM,0x5114); + write_nic_word(dev,AC_BK_PARAM,0x5114); + } else { + write_nic_word(dev,AC_VO_PARAM,0x731c); + write_nic_word(dev,AC_VI_PARAM,0x731c); + write_nic_word(dev,AC_BE_PARAM,0x731c); + write_nic_word(dev,AC_BK_PARAM,0x731c); + } +#endif + } + + up(&priv->set_chan_sem); +} +void +MacConfig_87BASIC_HardCode(struct net_device *dev) +{ + //============================================================================ + // MACREG.TXT + //============================================================================ + int nLinesRead = 0; + u32 u4bRegOffset, u4bRegValue, u4bPageIndex; + int i; + + nLinesRead=(sizeof(MAC_REG_TABLE)/3)/4; + + for(i = 0; i < nLinesRead; i++) + { + u4bRegOffset=MAC_REG_TABLE[i][0]; + u4bRegValue=MAC_REG_TABLE[i][1]; + u4bPageIndex=MAC_REG_TABLE[i][2]; + + u4bRegOffset|= (u4bPageIndex << 8); + + write_nic_byte(dev, u4bRegOffset, (u8)u4bRegValue); + } + //============================================================================ +} + +static void MacConfig_87BASIC(struct net_device *dev) +{ + MacConfig_87BASIC_HardCode(dev); + + //============================================================================ + + // Follow TID_AC_MAP of WMac. + //PlatformEFIOWrite2Byte(dev, TID_AC_MAP, 0xfa50); + write_nic_word(dev, TID_AC_MAP, 0xfa50); + + // Interrupt Migration, Jong suggested we use set 0x0000 first, 2005.12.14, by rcnjko. + write_nic_word(dev, INT_MIG, 0x0000); + + // Prevent TPC to cause CRC error. Added by Annie, 2006-06-10. + write_nic_dword(dev, 0x1F0, 0x00000000); + write_nic_dword(dev, 0x1F4, 0x00000000); + write_nic_byte(dev, 0x1F8, 0x00); + + // For WiFi 5.2.2.5 Atheros AP performance. Added by Annie, 2006-06-12. + // PlatformIOWrite4Byte(dev, RFTiming, 0x0008e00f); + // Asked for by SD3 CM Lin, 2006.06.27, by rcnjko. + write_nic_dword(dev, RFTiming, 0x00004001); + +#ifdef TODO + // Asked for by Victor, for 87B B-cut Rx FIFO overflow bug, 2006.06.27, by rcnjko. + if(dev->NdisUsbDev.CardInfo.USBIsHigh == FALSE) + { + PlatformEFIOWrite1Byte(dev, 0x24E, 0x01); + } +#endif +} + + +// +// Description: +// Initialize RFE and read Zebra2 version code. +// +// 2005-08-01, by Annie. +// +void +SetupRFEInitialTiming(struct net_device* dev) +{ + //u32 data8, data9; + struct r8180_priv *priv = ieee80211_priv(dev); + + // setup initial timing for RFE + // Set VCO-PDN pin. + write_nic_word(dev, RFPinsOutput, 0x0480); + write_nic_word(dev, RFPinsSelect, 0x2488); + write_nic_word(dev, RFPinsEnable, 0x1FFF); + + mdelay(100); + // Steven recommends: delay 1 sec for setting RF 1.8V. by Annie, 2005-04-28. + mdelay(1000); + + // + // TODO: Read Zebra version code if necessary. + // + priv->rf_chip = RF_ZEBRA2; +} + + +void ZEBRA_Config_87BASIC_HardCode(struct net_device* dev) +{ + u32 i; + u32 addr,data; + u32 u4bRegOffset, u4bRegValue; + + + //============================================================================= + // RADIOCFG.TXT + //============================================================================= + write_rtl8225(dev, 0x00, 0x00b7); mdelay(1); + write_rtl8225(dev, 0x01, 0x0ee0); mdelay(1); + write_rtl8225(dev, 0x02, 0x044d); mdelay(1); + write_rtl8225(dev, 0x03, 0x0441); mdelay(1); + write_rtl8225(dev, 0x04, 0x08c3); mdelay(1); + write_rtl8225(dev, 0x05, 0x0c72); mdelay(1); + write_rtl8225(dev, 0x06, 0x00e6); mdelay(1); + write_rtl8225(dev, 0x07, 0x082a); mdelay(1); + write_rtl8225(dev, 0x08, 0x003f); mdelay(1); + write_rtl8225(dev, 0x09, 0x0335); mdelay(1); + write_rtl8225(dev, 0x0a, 0x09d4); mdelay(1); + write_rtl8225(dev, 0x0b, 0x07bb); mdelay(1); + write_rtl8225(dev, 0x0c, 0x0850); mdelay(1); + write_rtl8225(dev, 0x0d, 0x0cdf); mdelay(1); + write_rtl8225(dev, 0x0e, 0x002b); mdelay(1); + write_rtl8225(dev, 0x0f, 0x0114); mdelay(1); + + write_rtl8225(dev, 0x00, 0x01b7); mdelay(1); + + + for(i=1;i<=95;i++) + { + write_rtl8225(dev, 0x01, i);mdelay(1); + write_rtl8225(dev, 0x02, ZEBRA_RF_RX_GAIN_TABLE[i]); mdelay(1); + //DbgPrint("RF - 0x%x = 0x%x\n", i, ZEBRA_RF_RX_GAIN_TABLE[i]); + } + + write_rtl8225(dev, 0x03, 0x0080); mdelay(1); // write reg 18 + write_rtl8225(dev, 0x05, 0x0004); mdelay(1); // write reg 20 + write_rtl8225(dev, 0x00, 0x00b7); mdelay(1); // switch to reg0-reg15 + //lzm mod for up take too long time 20081201 +#ifdef THOMAS_BEACON + msleep(1000);// Deay 1 sec. //0xfd + //msleep(1000);// Deay 1 sec. //0xfd + //msleep(1000);// Deay 1 sec. //0xfd + msleep(400);// Deay 1 sec. //0xfd +#else + + mdelay(1000); + //mdelay(1000); + //mdelay(1000); + mdelay(400); +#endif + write_rtl8225(dev, 0x02, 0x0c4d); mdelay(1); + //lzm mod for up take too long time 20081201 + //mdelay(1000); + //mdelay(1000); + msleep(100);// Deay 100 ms. //0xfe + msleep(100);// Deay 100 ms. //0xfe + write_rtl8225(dev, 0x02, 0x044d); mdelay(1); + write_rtl8225(dev, 0x00, 0x02bf); mdelay(1); //0x002f disable 6us corner change, 06f--> enable + + //============================================================================= + + //============================================================================= + // CCKCONF.TXT + //============================================================================= + /* + u4bRegOffset=0x41; + u4bRegValue=0xc8; + + //DbgPrint("\nCCK- 0x%x = 0x%x\n", u4bRegOffset, u4bRegValue); + WriteBB(dev, (0x01000080 | (u4bRegOffset & 0x7f) | ((u4bRegValue & 0xff) << 8))); + */ + + + //============================================================================= + + //============================================================================= + // Follow WMAC RTL8225_Config() + //============================================================================= +// // +// // enable EEM0 and EEM1 in 9346CR +// PlatformEFIOWrite1Byte(dev, CR9346, PlatformEFIORead1Byte(dev, CR9346)|0xc0); +// // enable PARM_En in Config3 +// PlatformEFIOWrite1Byte(dev, CONFIG3, PlatformEFIORead1Byte(dev, CONFIG3)|0x40); +// +// PlatformEFIOWrite4Byte(dev, AnaParm2, ANAPARM2_ASIC_ON); //0x727f3f52 +// PlatformEFIOWrite4Byte(dev, AnaParm, ANAPARM_ASIC_ON); //0x45090658 + + // power control + write_nic_byte(dev, CCK_TXAGC, 0x03); + write_nic_byte(dev, OFDM_TXAGC, 0x07); + write_nic_byte(dev, ANTSEL, 0x03); + +// // disable PARM_En in Config3 +// PlatformEFIOWrite1Byte(dev, CONFIG3, PlatformEFIORead1Byte(dev, CONFIG3)&0xbf); +// // disable EEM0 and EEM1 in 9346CR +// PlatformEFIOWrite1Byte(dev, CR9346, PlatformEFIORead1Byte(dev, CR9346)&0x3f); + //============================================================================= + + //============================================================================= + // AGC.txt + //============================================================================= + //write_nic_dword( dev, PhyAddr, 0x00001280); // Annie, 2006-05-05 + //write_phy_ofdm( dev, 0x00, 0x12); // David, 2006-08-01 + write_phy_ofdm( dev, 0x80, 0x12); // David, 2006-08-09 + + for (i=0; i<128; i++) + { + //DbgPrint("AGC - [%x+1] = 0x%x\n", i, ZEBRA_AGC[i+1]); + + data = ZEBRA_AGC[i+1]; + data = data << 8; + data = data | 0x0000008F; + + addr = i + 0x80; //enable writing AGC table + addr = addr << 8; + addr = addr | 0x0000008E; + + write_phy_ofdm(dev,data&0x7f,(data>>8)&0xff); + write_phy_ofdm(dev,addr&0x7f,(addr>>8)&0xff); + write_phy_ofdm(dev,0x0E,0x00); + } + + //write_nic_dword(dev, PhyAddr, 0x00001080); // Annie, 2006-05-05 + //write_phy_ofdm( dev, 0x00, 0x10); // David, 2006-08-01 + write_phy_ofdm( dev, 0x80, 0x10); // David, 2006-08-09 + + //============================================================================= + + //============================================================================= + // OFDMCONF.TXT + //============================================================================= + + for(i=0; i<60; i++) + { + u4bRegOffset=i; + u4bRegValue=OFDM_CONFIG[i]; + //u4bRegValue=OFDM_CONFIG3m82[i]; + + // write_nic_dword(dev,PhyAddr,(0x00000080 | (u4bRegOffset & 0x7f) | ((u4bRegValue & 0xff) << 8))); + write_phy_ofdm(dev,i,u4bRegValue); + } + + + //============================================================================= +} + +void ZEBRA_Config_87BASIC(struct net_device *dev) +{ + ZEBRA_Config_87BASIC_HardCode(dev); +} +//by amy for DIG +// +// Description: +// Update initial gain into PHY. +// +void +UpdateCCKThreshold( + struct net_device *dev + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + // Update CCK Power Detection(0x41) value. + switch(priv->StageCCKTh) + { + case 0: +// printk("Update CCK Stage 0: 88 \n"); + write_phy_cck(dev, 0xc1, 0x88);mdelay(1); + break; + + case 1: +// printk("Update CCK Stage 1: 98 \n"); + write_phy_cck(dev, 0xc1, 0x98);mdelay(1); + break; + + case 2: +// printk("Update CCK Stage 2: C8 \n"); + write_phy_cck(dev, 0xc1, 0xC8);mdelay(1); + break; + + case 3: +// printk("Update CCK Stage 3: D8 \n"); + write_phy_cck(dev, 0xc1, 0xD8);mdelay(1); + break; + + default: +// printk("Update CCK Stage %d ERROR!\n", pHalData->StageCCKTh); + break; + } +} +// +// Description: +// Update initial gain into PHY. +// +void +UpdateInitialGain( + struct net_device *dev + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + //u8 u1Tmp=0; + + //printk("UpdateInitialGain(): InitialGain: %d RFChipID: %d\n", priv->InitialGain, priv->rf_chip); + + switch(priv->rf_chip) + { + case RF_ZEBRA: + case RF_ZEBRA2: + + // + // Note: + // Whenever we update this gain table, we should be careful about those who call it. + // Functions which call UpdateInitialGain as follows are important: + // (1)StaRateAdaptive87B + // (2)DIG_Zebra + // (3)ActSetWirelessMode8187 (when the wireless mode is "B" mode, we set the + // OFDM[0x17] = 0x26 to improve the Rx sensitivity). + // By Bruce, 2007-06-01. + // + + // + // SD3 C.M. Lin Initial Gain Table, by Bruce, 2007-06-01. + // + switch(priv->InitialGain) + { + case 1: //m861dBm +// DMESG("RTL8187 + 8225 Initial Gain State 1: -82 dBm "); + write_phy_ofdm(dev, 0x97, 0x26); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0x86); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfa); mdelay(1); + break; + + case 2: //m862dBm +// DMESG("RTL8187 + 8225 Initial Gain State 2: -78 dBm "); + write_phy_ofdm(dev, 0x97, 0x36); mdelay(1);// Revise 0x26 to 0x36, by Roger, 2007.05.03. + write_phy_ofdm(dev, 0xa4, 0x86); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfa); mdelay(1); + break; + + case 3: //m863dBm +// DMESG("RTL8187 + 8225 Initial Gain State 3: -78 dBm "); + write_phy_ofdm(dev, 0x97, 0x36); mdelay(1);// Revise 0x26 to 0x36, by Roger, 2007.05.03. + write_phy_ofdm(dev, 0xa4, 0x86); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfb); mdelay(1); + break; + + case 4: //m864dBm +// DMESG("RTL8187 + 8225 Initial Gain State 4: -74 dBm "); + write_phy_ofdm(dev, 0x97, 0x46); mdelay(1);// Revise 0x26 to 0x36, by Roger, 2007.05.03. + write_phy_ofdm(dev, 0xa4, 0x86); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfb); mdelay(1); + break; + + case 5: //m82dBm +// DMESG("RTL8187 + 8225 Initial Gain State 5: -74 dBm "); + write_phy_ofdm(dev, 0x97, 0x46); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0x96); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfb); mdelay(1); + break; + + case 6: //m78dBm +// DMESG("RTL8187 + 8225 Initial Gain State 6: -70 dBm "); + write_phy_ofdm(dev, 0x97, 0x56); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0x96); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfc); mdelay(1); + break; + + case 7: //m74dBm +// DMESG("RTL8187 + 8225 Initial Gain State 7: -70 dBm "); + write_phy_ofdm(dev, 0x97, 0x56); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0xa6); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfc); mdelay(1); + break; + + // By Bruce, 2007-03-29. + case 8: + write_phy_ofdm(dev, 0x97, 0x66); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0xb6); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfc); mdelay(1); + break; + + default: //MP +// DMESG("RTL8187 + 8225 Initial Gain State: -82 dBm (default), InitialGain(%d)", priv->InitialGain); + write_phy_ofdm(dev, 0x97, 0x26); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0x86); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfa); mdelay(1); + break; + } + break; + + default: + break; + } +} +//by amy for DIG +void PhyConfig8187(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 btConfig4; + + btConfig4 = read_nic_byte(dev, CONFIG4); + priv->RFProgType = (btConfig4 & 0x03); + + + + switch(priv->rf_chip) + { + case RF_ZEBRA2: + ZEBRA_Config_87BASIC(dev); + break; + } + if(priv->bDigMechanism) + { + if(priv->InitialGain == 0) + priv->InitialGain = 4; + //DMESG("DIG is enabled, set default initial gain index to %d", priv->InitialGain); + } + + // By Bruce, 2007-03-29. + UpdateCCKThreshold(dev); + // Update initial gain after PhyConfig comleted, asked for by SD3 CMLin. + UpdateInitialGain(dev); + return ; +} + +u8 GetSupportedWirelessMode8187(struct net_device* dev) +{ + u8 btSupportedWirelessMode; + struct r8180_priv *priv = ieee80211_priv(dev); + + btSupportedWirelessMode = 0; + + switch(priv->rf_chip) + { + case RF_ZEBRA: + case RF_ZEBRA2: + btSupportedWirelessMode = (WIRELESS_MODE_B | WIRELESS_MODE_G); + break; + default: + btSupportedWirelessMode = WIRELESS_MODE_B; + break; + } + return btSupportedWirelessMode; +} + +void ActUpdateChannelAccessSetting(struct net_device *dev, + int WirelessMode, + PCHANNEL_ACCESS_SETTING ChnlAccessSetting) +{ + AC_CODING eACI; + AC_PARAM AcParam; +#ifdef TODO + PSTA_QOS pStaQos = Adapter->MgntInfo.pStaQos; +#endif + //bool bFollowLegacySetting = false; + + + switch( WirelessMode ) + { + case WIRELESS_MODE_A: + ChnlAccessSetting->SIFS_Timer = 0x22; + ChnlAccessSetting->DIFS_Timer = 34; // 34 = 16 + 2*9. 2006.06.07, by rcnjko. + ChnlAccessSetting->SlotTimeTimer = 9; + ChnlAccessSetting->EIFS_Timer = 23; + ChnlAccessSetting->CWminIndex = 4; + ChnlAccessSetting->CWmaxIndex = 10; + break; + + case WIRELESS_MODE_B: + ChnlAccessSetting->SIFS_Timer = 0x22; + ChnlAccessSetting->DIFS_Timer = 50; // 50 = 10 + 2*20. 2006.06.07, by rcnjko. + ChnlAccessSetting->SlotTimeTimer = 20; + ChnlAccessSetting->EIFS_Timer = 91; + ChnlAccessSetting->CWminIndex = 5; + ChnlAccessSetting->CWmaxIndex = 10; + break; + + case WIRELESS_MODE_G: + // + // + // TODO: We still don't know how to set up these registers, just follow WMAC to + // verify 8185B FPAG. + // + // + // Jong said CWmin/CWmax register are not functional in 8185B, + // so we shall fill channel access realted register into AC parameter registers, + // even in nQBss. + // + ChnlAccessSetting->SIFS_Timer = 0x22; // Suggested by Jong, 2005.12.08. + ChnlAccessSetting->SlotTimeTimer = 9; // 2006.06.07, by rcnjko. + ChnlAccessSetting->DIFS_Timer = 28; // 28 = 10 + 2*9. 2006.06.07, by rcnjko. + ChnlAccessSetting->EIFS_Timer = 0x5B; // Suggested by wcchu, it is the default value of EIFS register, 2005.12.08. +#ifdef TODO + switch (Adapter->NdisUsbDev.CWinMaxMin) +#else + switch (2) +#endif + { + case 0:// 0: [max:7 min:1 ] + ChnlAccessSetting->CWminIndex = 1; + ChnlAccessSetting->CWmaxIndex = 7; + break; + case 1:// 1: [max:7 min:2 ] + ChnlAccessSetting->CWminIndex = 2; + ChnlAccessSetting->CWmaxIndex = 7; + break; + case 2:// 2: [max:7 min:3 ] + ChnlAccessSetting->CWminIndex = 3; + ChnlAccessSetting->CWmaxIndex = 7; + break; + case 3:// 3: [max:9 min:1 ] + ChnlAccessSetting->CWminIndex = 1; + ChnlAccessSetting->CWmaxIndex = 9; + break; + case 4:// 4: [max:9 min:2 ] + ChnlAccessSetting->CWminIndex = 2; + ChnlAccessSetting->CWmaxIndex = 9; + break; + case 5:// 5: [max:9 min:3 ] + ChnlAccessSetting->CWminIndex = 3; + ChnlAccessSetting->CWmaxIndex = 9; + break; + case 6:// 6: [max:A min:5 ] + ChnlAccessSetting->CWminIndex = 5; + ChnlAccessSetting->CWmaxIndex = 10; + break; + case 7:// 7: [max:A min:4 ] + ChnlAccessSetting->CWminIndex = 4; + ChnlAccessSetting->CWmaxIndex = 10; + break; + + default: + ChnlAccessSetting->CWminIndex = 1; + ChnlAccessSetting->CWmaxIndex = 7; + break; + } +#ifdef TODO + if( Adapter->MgntInfo.OpMode == RT_OP_MODE_IBSS) + { + ChnlAccessSetting->CWminIndex= 4; + ChnlAccessSetting->CWmaxIndex= 10; + } +#endif + break; + } + + + write_nic_byte(dev, SIFS, ChnlAccessSetting->SIFS_Timer); +//{ update slot time related by david, 2006-7-21 + write_nic_byte(dev, SLOT, ChnlAccessSetting->SlotTimeTimer); // Rewrited from directly use PlatformEFIOWrite1Byte(), by Annie, 2006-03-29. +#ifdef TODO + if(pStaQos->CurrentQosMode > QOS_DISABLE) + { + for(eACI = 0; eACI < AC_MAX; eACI++) + { + Adapter->HalFunc.SetHwRegHandler(Adapter, HW_VAR_AC_PARAM, \ + (pu1Byte)(&(pStaQos->WMMParamEle.AcParam[eACI])) ); + } + } + else +#endif + { + u8 u1bAIFS = aSifsTime + (2 * ChnlAccessSetting->SlotTimeTimer ); + + write_nic_byte(dev, AC_VO_PARAM, u1bAIFS); + write_nic_byte(dev, AC_VI_PARAM, u1bAIFS); + write_nic_byte(dev, AC_BE_PARAM, u1bAIFS); + write_nic_byte(dev, AC_BK_PARAM, u1bAIFS); + } +//} + + write_nic_byte(dev, EIFS_8187B, ChnlAccessSetting->EIFS_Timer); + write_nic_byte(dev, AckTimeOutReg, 0x5B); // Suggested by wcchu, it is the default value of EIFS register, 2005.12.08. +#ifdef TODO + // Update ECWmin/ECWmax, AIFS, TXOP Limit of each AC to the value defined by SPEC. + if( pStaQos->CurrentQosMode > QOS_DISABLE ) + { // QoS mode. + if(pStaQos->QBssWirelessMode == WirelessMode) + { + // Follow AC Parameters of the QBSS. + for(eACI = 0; eACI < AC_MAX; eACI++) + { + Adapter->HalFunc.SetHwRegHandler(Adapter, HW_VAR_AC_PARAM, (pu1Byte)(&(pStaQos->WMMParamEle.AcParam[eACI])) ); + } + } + else + { + // Follow Default WMM AC Parameters. + bFollowLegacySetting = TRUE; + } + } + else + { // Legacy 802.11. + bFollowLegacySetting = TRUE; + } + + if(bFollowLegacySetting) +#endif + if(true) + { + // + // Follow 802.11 seeting to AC parameter, all AC shall use the same parameter. + // 2005.12.01, by rcnjko. + // + AcParam.longData = 0; + AcParam.f.AciAifsn.f.AIFSN = 2; // Follow 802.11 DIFS. + AcParam.f.AciAifsn.f.ACM = 0; + AcParam.f.Ecw.f.ECWmin = ChnlAccessSetting->CWminIndex; // Follow 802.11 CWmin. + AcParam.f.Ecw.f.ECWmax = ChnlAccessSetting->CWmaxIndex; // Follow 802.11 CWmax. + AcParam.f.TXOPLimit = 0; + for(eACI = 0; eACI < AC_MAX; eACI++) + { + AcParam.f.AciAifsn.f.ACI = (u8)eACI; + { + PAC_PARAM pAcParam = (PAC_PARAM)(&AcParam); + AC_CODING eACI; + u8 u1bAIFS; + u32 u4bAcParam; + + // Retrive paramters to udpate. + eACI = pAcParam->f.AciAifsn.f.ACI; + u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN * ChnlAccessSetting->SlotTimeTimer + aSifsTime; + u4bAcParam = ( (((u32)(pAcParam->f.TXOPLimit)) << AC_PARAM_TXOP_LIMIT_OFFSET) | + (((u32)(pAcParam->f.Ecw.f.ECWmax)) << AC_PARAM_ECW_MAX_OFFSET) | + (((u32)(pAcParam->f.Ecw.f.ECWmin)) << AC_PARAM_ECW_MIN_OFFSET) | + (((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET)); + + switch(eACI) + { + case AC1_BK: + write_nic_dword(dev, AC_BK_PARAM, u4bAcParam); + break; + + case AC0_BE: + write_nic_dword(dev, AC_BE_PARAM, u4bAcParam); + break; + + case AC2_VI: + write_nic_dword(dev, AC_VI_PARAM, u4bAcParam); + break; + + case AC3_VO: + write_nic_dword(dev, AC_VO_PARAM, u4bAcParam); + break; + + default: + printk(KERN_WARNING "SetHwReg8185(): invalid ACI: %d !\n", eACI); + break; + } + + // Cehck ACM bit. + // If it is set, immediately set ACM control bit to downgrading AC for passing WMM testplan. Annie, 2005-12-13. + //write_nic_byte(dev, ACM_CONTROL, pAcParam->f.AciAifsn); + { + PACI_AIFSN pAciAifsn = (PACI_AIFSN)(&pAcParam->f.AciAifsn); + AC_CODING eACI = pAciAifsn->f.ACI; + + //modified Joseph + //for 8187B AsynIORead issue +#ifdef TODO + u8 AcmCtrl = pHalData->AcmControl; +#else + u8 AcmCtrl = 0; +#endif + if( pAciAifsn->f.ACM ) + { // ACM bit is 1. + switch(eACI) + { + case AC0_BE: + AcmCtrl |= (BEQ_ACM_EN|BEQ_ACM_CTL|ACM_HW_EN); // or 0x21 + break; + + case AC2_VI: + AcmCtrl |= (VIQ_ACM_EN|VIQ_ACM_CTL|ACM_HW_EN); // or 0x42 + break; + + case AC3_VO: + AcmCtrl |= (VOQ_ACM_EN|VOQ_ACM_CTL|ACM_HW_EN); // or 0x84 + break; + + default: + printk(KERN_WARNING "SetHwReg8185(): [HW_VAR_ACM_CTRL] ACM set\ + failed: eACI is %d\n", eACI ); + break; + } + } + else + { // ACM bit is 0. + switch(eACI) + { + case AC0_BE: + AcmCtrl &= ( (~BEQ_ACM_EN) & (~BEQ_ACM_CTL) & (~ACM_HW_EN) ); // and 0xDE + break; + + case AC2_VI: + AcmCtrl &= ( (~VIQ_ACM_EN) & (~VIQ_ACM_CTL) & (~ACM_HW_EN) ); // and 0xBD + break; + + case AC3_VO: + AcmCtrl &= ( (~VOQ_ACM_EN) & (~VOQ_ACM_CTL) & (~ACM_HW_EN) ); // and 0x7B + break; + + default: + break; + } + } + + //printk(KERN_WARNING "SetHwReg8185(): [HW_VAR_ACM_CTRL] Write 0x%X\n", AcmCtrl); + +#ifdef TO_DO + pHalData->AcmControl = AcmCtrl; +#endif + write_nic_byte(dev, ACM_CONTROL, AcmCtrl); + } + } + } + } +} + +void ActSetWirelessMode8187(struct net_device* dev, u8 btWirelessMode) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + //PMGNT_INFO pMgntInfo = &(pAdapter->MgntInfo); + u8 btSupportedWirelessMode = GetSupportedWirelessMode8187(dev); + + if( (btWirelessMode & btSupportedWirelessMode) == 0 ) + { // Don't switch to unsupported wireless mode, 2006.02.15, by rcnjko. + printk(KERN_WARNING "ActSetWirelessMode8187(): WirelessMode(%d) is not supported (%d)!\n", + btWirelessMode, btSupportedWirelessMode); + return; + } + + // 1. Assign wireless mode to swtich if necessary. + if( (btWirelessMode == WIRELESS_MODE_AUTO) || + (btWirelessMode & btSupportedWirelessMode) == 0 ) + { + if((btSupportedWirelessMode & WIRELESS_MODE_A)) + { + btWirelessMode = WIRELESS_MODE_A; + } + else if((btSupportedWirelessMode & WIRELESS_MODE_G)) + { + btWirelessMode = WIRELESS_MODE_G; + } + else if((btSupportedWirelessMode & WIRELESS_MODE_B)) + { + btWirelessMode = WIRELESS_MODE_B; + } + else + { + printk(KERN_WARNING "MptActSetWirelessMode8187(): No valid wireless mode supported, \ + btSupportedWirelessMode(%x)!!!\n", btSupportedWirelessMode); + btWirelessMode = WIRELESS_MODE_B; + } + } + + // 2. Swtich band. + switch(priv->rf_chip) + { + case RF_ZEBRA: + case RF_ZEBRA2: + { + // Update current wireless mode if we swtich to specified band successfully. + ieee->mode = (WIRELESS_MODE)btWirelessMode; + } + break; + + default: + printk(KERN_WARNING "MptActSetWirelessMode8187(): unsupported RF: 0x%X !!!\n", priv->rf_chip); + break; + } + + // 4. Change related setting. +#if 0 + if( ieee->mode == WIRELESS_MODE_A ){ + DMESG("WIRELESS_MODE_A"); + } + else if(ieee->mode == WIRELESS_MODE_B ){ + DMESG("WIRELESS_MODE_B"); + } + else if( ieee->mode == WIRELESS_MODE_G ){ + DMESG("WIRELESS_MODE_G"); + } +#endif + ActUpdateChannelAccessSetting(dev, ieee->mode, &priv->ChannelAccessSetting ); +//by amy 0305 +#ifdef TODO + if(ieee->mode == WIRELESS_MODE_B && priv->InitialGain > pHalData->RegBModeGainStage) + { + pHalData->InitialGain = pHalData->RegBModeGainStage; // B mode, OFDM[0x17] = 26. + RT_TRACE(COMP_INIT | COMP_DIG, DBG_LOUD, ("ActSetWirelessMode8187(): update init_gain to index %d for B mode\n",pHalData->InitialGain)); + PlatformScheduleWorkItem( &(pHalData->UpdateDigWorkItem) ); + } +// pAdapter->MgntInfo.dot11CurrentWirelessMode = pHalData->CurrentWirelessMode; +// MgntSetRegdot11OperationalRateSet( pAdapter ); +#endif +//by amy 0305 +} + + +void +InitializeExtraRegsOn8185(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + //RTL8185_TODO: Determine Retrylimit, TxAGC, AutoRateFallback control. + bool bUNIVERSAL_CONTROL_RL = false; // Enable per-packet tx retry, 2005.03.31, by rcnjko. + bool bUNIVERSAL_CONTROL_AGC = true;//false; + bool bUNIVERSAL_CONTROL_ANT = true;//false; + bool bAUTO_RATE_FALLBACK_CTL = true; + u8 val8; + + // Set up ACK rate. + // Suggested by wcchu, 2005.08.25, by rcnjko. + // 1. Initialize (MinRR, MaxRR) to (6,24) for A/G. + // 2. MUST Set RR before BRSR. + // 3. CCK must be basic rate. + if((ieee->mode == IEEE_G)||(ieee->mode == IEEE_A)) + { + write_nic_word(dev, BRSR_8187B, 0x0fff); + } + else + { + write_nic_word(dev, BRSR_8187B, 0x000f); + } + + + // Retry limit + val8 = read_nic_byte(dev, CW_CONF); + if(bUNIVERSAL_CONTROL_RL) + { + val8 &= (~CW_CONF_PERPACKET_RETRY_LIMIT); + } + else + { + val8 |= CW_CONF_PERPACKET_RETRY_LIMIT; + } + + write_nic_byte(dev, CW_CONF, val8); + + // Tx AGC + val8 = read_nic_byte(dev, TX_AGC_CTL); + if(bUNIVERSAL_CONTROL_AGC) + { + val8 &= (~TX_AGC_CTL_PER_PACKET_TXAGC); + write_nic_byte(dev, CCK_TXAGC, 128); + write_nic_byte(dev, OFDM_TXAGC, 128); + } + else + { + val8 |= TX_AGC_CTL_PER_PACKET_TXAGC; + } + write_nic_byte(dev, TX_AGC_CTL, val8); + + // Tx Antenna including Feedback control + val8 = read_nic_byte(dev, TX_AGC_CTL); + + if(bUNIVERSAL_CONTROL_ANT) + { + write_nic_byte(dev, ANTSEL, 0x00); + val8 &= (~TXAGC_CTL_PER_PACKET_ANT_SEL); + } + else + { + val8 |= TXAGC_CTL_PER_PACKET_ANT_SEL; + } + write_nic_byte(dev, TX_AGC_CTL, val8); + + // Auto Rate fallback control + val8 = read_nic_byte(dev, RATE_FALLBACK); + if( bAUTO_RATE_FALLBACK_CTL ) + { + val8 |= RATE_FALLBACK_CTL_ENABLE | RATE_FALLBACK_CTL_AUTO_STEP0; + + // We shall set up the ARFR according to user's setting. + write_nic_word(dev, ARFR, 0x0fff); // set 1M ~ 54M + } + else + { + val8 &= (~RATE_FALLBACK_CTL_ENABLE); + } + write_nic_byte(dev, RATE_FALLBACK, val8); + +} +/////////////////////////// +void rtl8225z2_rf_init(struct net_device *dev) +{ + + struct r8180_priv *priv = ieee80211_priv(dev); + if (NIC_8187B == priv->card_8187){ + struct ieee80211_device *ieee = priv->ieee80211; + u8 InitWirelessMode; + u8 SupportedWirelessMode; + bool bInvalidWirelessMode = false; + InitializeExtraRegsOn8185(dev); + + write_nic_byte(dev, MSR, read_nic_byte(dev,MSR) & 0xf3); // default network type to 'No Link' + //{to avoid tx stall + write_nic_byte(dev, MSR, read_nic_byte(dev, MSR)|MSR_LINK_ENEDCA);//should always set ENDCA bit + write_nic_byte(dev, ACM_CONTROL, priv->AcmControl); + + write_nic_word(dev, BcnIntv, 100); + write_nic_word(dev, AtimWnd, 2); + write_nic_word(dev, FEMR, 0xFFFF); + //LED TYPE + { + write_nic_byte(dev, CONFIG1,((read_nic_byte(dev, CONFIG1)&0x3f)|0x80)); //turn on bit 5:Clkrun_mode + } + write_nic_byte(dev, CR9346, 0x0); // disable config register write + + //{ add some info here + write_nic_dword(dev, MAC0, ((u32*)dev->dev_addr)[0]); + write_nic_word(dev, MAC4, ((u32*)dev->dev_addr)[1] & 0xffff ); + + write_nic_byte(dev, WPA_CONFIG, 0); + //} + + MacConfig_87BASIC(dev); + + // Override the RFSW_CTRL (MAC offset 0x272-0x273), 2006.06.07, by rcnjko. + write_nic_word(dev, RFSW_CTRL, 0x569a); +#ifdef JOHN_TKIP + { + void CamResetAllEntry(struct net_device *dev); + void EnableHWSecurityConfig8187(struct net_device *dev); + CamResetAllEntry(dev); + EnableHWSecurityConfig8187(dev); + write_nic_word(dev, AESMSK_FC, AESMSK_FC_DEFAULT); mdelay(1); + write_nic_word(dev, AESMSK_SC, AESMSK_SC_DEFAULT); mdelay(1); + write_nic_word(dev, AESMSK_QC, AESMSK_QC_DEFAULT); mdelay(1); + } +#endif + //----------------------------------------------------------------------------- + // Set up PHY related. + //----------------------------------------------------------------------------- + // Enable Config3.PARAM_En to revise AnaaParm. + write_nic_byte(dev, CR9346, 0xC0); + write_nic_byte(dev, CONFIG3, read_nic_byte(dev,CONFIG3)|CONFIG3_PARM_En); + write_nic_byte(dev, CR9346, 0x0); + + // Initialize RFE and read Zebra2 version code. Added by Annie, 2005-08-01. + SetupRFEInitialTiming(dev); + // PHY config. + PhyConfig8187(dev); + + // We assume RegWirelessMode has already been initialized before, + // however, we has to validate the wireless mode here and provide a reasonble + // initialized value if necessary. 2005.01.13, by rcnjko. + SupportedWirelessMode = GetSupportedWirelessMode8187(dev); + + if((ieee->mode != WIRELESS_MODE_B) && + (ieee->mode != WIRELESS_MODE_G) && + (ieee->mode != WIRELESS_MODE_A) && + (ieee->mode != WIRELESS_MODE_AUTO)) + { // It should be one of B, G, A, or AUTO. + bInvalidWirelessMode = true; + } + else + { // One of B, G, A, or AUTO. + // Check if the wireless mode is supported by RF. + if( (ieee->mode != WIRELESS_MODE_AUTO) && + (ieee->mode & SupportedWirelessMode) == 0 ) + { + bInvalidWirelessMode = true; + } + } + + if(bInvalidWirelessMode || ieee->mode==WIRELESS_MODE_AUTO) + { // Auto or other invalid value. + // Assigne a wireless mode to initialize. + if((SupportedWirelessMode & WIRELESS_MODE_A)) + { + InitWirelessMode = WIRELESS_MODE_A; + } + else if((SupportedWirelessMode & WIRELESS_MODE_G)) + { + + InitWirelessMode = WIRELESS_MODE_G; + } + else if((SupportedWirelessMode & WIRELESS_MODE_B)) + { + + InitWirelessMode = WIRELESS_MODE_B; + } + else + { + printk(KERN_WARNING + "InitializeAdapter8187(): No valid wireless mode supported, SupportedWirelessMode(%x)!!!\n", + SupportedWirelessMode); + InitWirelessMode = WIRELESS_MODE_B; + } + + // Initialize RegWirelessMode if it is not a valid one. + if(bInvalidWirelessMode) + { + ieee->mode = (WIRELESS_MODE)InitWirelessMode; + } + } + else + { // One of B, G, A. + InitWirelessMode = ieee->mode; + } + ActSetWirelessMode8187(dev, (u8)(InitWirelessMode)); + {//added for init gain + write_phy_ofdm(dev, 0x97, 0x46); mdelay(1); + write_phy_ofdm(dev, 0xa4, 0xb6); mdelay(1); + write_phy_ofdm(dev, 0x85, 0xfc); mdelay(1); + write_phy_cck(dev, 0xc1, 0x88); mdelay(1); + } + + } + else{ + int i; + short channel = 1; + u16 brsr; + u32 data,addr; + + priv->chan = channel; + + rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_ON); + + if(priv->card_type == USB) + rtl8225_host_usb_init(dev); + else + rtl8225_host_pci_init(dev); + + write_nic_dword(dev, RF_TIMING, 0x000a8008); + + brsr = read_nic_word(dev, BRSR_8187); + + write_nic_word(dev, BRSR_8187, 0xffff); + + + write_nic_dword(dev, RF_PARA, 0x100044); + + #if 1 //0->1 + rtl8180_set_mode(dev, EPROM_CMD_CONFIG); + write_nic_byte(dev, CONFIG3, 0x44); + rtl8180_set_mode(dev, EPROM_CMD_NORMAL); + #endif + + + rtl8185_rf_pins_enable(dev); + + // mdelay(1000); + + write_rtl8225(dev, 0x0, 0x2bf); mdelay(1); + + + write_rtl8225(dev, 0x1, 0xee0); mdelay(1); + + write_rtl8225(dev, 0x2, 0x44d); mdelay(1); + + write_rtl8225(dev, 0x3, 0x441); mdelay(1); + + + write_rtl8225(dev, 0x4, 0x8c3);mdelay(1); + + + + write_rtl8225(dev, 0x5, 0xc72);mdelay(1); + // } + + write_rtl8225(dev, 0x6, 0xe6); mdelay(1); + + write_rtl8225(dev, 0x7, ((priv->card_type == USB)? 0x82a : rtl8225_chan[channel])); mdelay(1); + + write_rtl8225(dev, 0x8, 0x3f); mdelay(1); + + write_rtl8225(dev, 0x9, 0x335); mdelay(1); + + write_rtl8225(dev, 0xa, 0x9d4); mdelay(1); + + write_rtl8225(dev, 0xb, 0x7bb); mdelay(1); + + write_rtl8225(dev, 0xc, 0x850); mdelay(1); + + + write_rtl8225(dev, 0xd, 0xcdf); mdelay(1); + + write_rtl8225(dev, 0xe, 0x2b); mdelay(1); + + write_rtl8225(dev, 0xf, 0x114); + + + mdelay(100); + + + //if(priv->card_type != USB) /* maybe not needed even for 8185 */ + // write_rtl8225(dev, 0x7, rtl8225_chan[channel]); + + write_rtl8225(dev, 0x0, 0x1b7); + + for(i=0;i<95;i++){ + write_rtl8225(dev, 0x1, (u8)(i+1)); + /* version B & C & D*/ + write_rtl8225(dev, 0x2, rtl8225z2_rxgain[i]); + } + //write_rtl8225(dev, 0x3, 0x80); + write_rtl8225(dev, 0x3, 0x2); + write_rtl8225(dev, 0x5, 0x4); + + write_rtl8225(dev, 0x0, 0xb7); + + write_rtl8225(dev, 0x2, 0xc4d); + + if(priv->card_type == USB){ + // force_pci_posting(dev); + mdelay(200); + + write_rtl8225(dev, 0x2, 0x44d); + + // force_pci_posting(dev); + mdelay(200); + + }//End of if(priv->card_type == USB) + /* FIXME!! rtl8187 we have to check if calibrarion + * is successful and eventually cal. again (repeat + * the two write on reg 2) + */ + // Check for calibration status, 2005.11.17, + data = read_rtl8225(dev, 6); + if (!(data&0x00000080)) + { + write_rtl8225(dev, 0x02, 0x0c4d); + force_pci_posting(dev); mdelay(200); + write_rtl8225(dev, 0x02, 0x044d); + force_pci_posting(dev); mdelay(100); + data = read_rtl8225(dev, 6); + if (!(data&0x00000080)) + { + DMESGW("RF Calibration Failed!!!!\n"); + } + } + //force_pci_posting(dev); + + mdelay(200); //200 for 8187 + + + // //if(priv->card_type != USB){ + // write_rtl8225(dev, 0x2, 0x44d); + // write_rtl8225(dev, 0x7, rtl8225_chan[channel]); + // write_rtl8225(dev, 0x2, 0x47d); + // + // force_pci_posting(dev); + // mdelay(100); + // + // write_rtl8225(dev, 0x2, 0x44d); + // //} + + write_rtl8225(dev, 0x0, 0x2bf); + + if(priv->card_type != USB) + rtl8185_rf_pins_enable(dev); + //set up ZEBRA AGC table, 2005.11.17, + for(i=0;i<128;i++){ + data = rtl8225_agc[i]; + + addr = i + 0x80; //enable writing AGC table + write_phy_ofdm(dev, 0xb, data); + + mdelay(1); + write_phy_ofdm(dev, 0xa, addr); + + mdelay(1); + } + + force_pci_posting(dev); + mdelay(1); + + write_phy_ofdm(dev, 0x0, 0x1); mdelay(1); + write_phy_ofdm(dev, 0x1, 0x2); mdelay(1); + write_phy_ofdm(dev, 0x2, ((priv->card_type == USB)? 0x42 : 0x62)); mdelay(1); + write_phy_ofdm(dev, 0x3, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x4, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x5, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x6, 0x40); mdelay(1); + write_phy_ofdm(dev, 0x7, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x8, 0x40); mdelay(1); + write_phy_ofdm(dev, 0x9, 0xfe); mdelay(1); + + write_phy_ofdm(dev, 0xa, 0x8); mdelay(1); + + //write_phy_ofdm(dev, 0x18, 0xef); + // } + //} + write_phy_ofdm(dev, 0xb, 0x80); mdelay(1); + + write_phy_ofdm(dev, 0xc, 0x1);mdelay(1); + + + //if(priv->card_type != USB) + write_phy_ofdm(dev, 0xd, 0x43); + + write_phy_ofdm(dev, 0xe, 0xd3);mdelay(1); + + write_phy_ofdm(dev, 0xf, 0x38);mdelay(1); + /*ver D & 8187*/ + // } + + // if(priv->card_8185 == 1 && priv->card_8185_Bversion) + // write_phy_ofdm(dev, 0x10, 0x04);/*ver B*/ + // else + write_phy_ofdm(dev, 0x10, 0x84);mdelay(1); + /*ver C & D & 8187*/ + + write_phy_ofdm(dev, 0x11, 0x07);mdelay(1); + /*agc resp time 700*/ + + + // if(priv->card_8185 == 2){ + /* Ver D & 8187*/ + write_phy_ofdm(dev, 0x12, 0x20);mdelay(1); + + write_phy_ofdm(dev, 0x13, 0x20);mdelay(1); + + write_phy_ofdm(dev, 0x14, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x15, 0x40); mdelay(1); + write_phy_ofdm(dev, 0x16, 0x0); mdelay(1); + write_phy_ofdm(dev, 0x17, 0x40); mdelay(1); + + // if (priv->card_type == USB) + // write_phy_ofdm(dev, 0x18, 0xef); + + write_phy_ofdm(dev, 0x18, 0xef);mdelay(1); + + + write_phy_ofdm(dev, 0x19, 0x19); mdelay(1); + write_phy_ofdm(dev, 0x1a, 0x20); mdelay(1); + write_phy_ofdm(dev, 0x1b, 0x15);mdelay(1); + + write_phy_ofdm(dev, 0x1c, 0x4);mdelay(1); + + write_phy_ofdm(dev, 0x1d, 0xc5);mdelay(1); //2005.11.17, + + write_phy_ofdm(dev, 0x1e, 0x95);mdelay(1); + + write_phy_ofdm(dev, 0x1f, 0x75); mdelay(1); + + // } + + write_phy_ofdm(dev, 0x20, 0x1f);mdelay(1); + + write_phy_ofdm(dev, 0x21, 0x17);mdelay(1); + + write_phy_ofdm(dev, 0x22, 0x16);mdelay(1); + + // if(priv->card_type != USB) + write_phy_ofdm(dev, 0x23, 0x80);mdelay(1); //FIXME maybe not needed // <> + + write_phy_ofdm(dev, 0x24, 0x46); mdelay(1); + write_phy_ofdm(dev, 0x25, 0x00); mdelay(1); + write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); + + write_phy_ofdm(dev, 0x27, 0x88); mdelay(1); + + + // <> Set init. gain to m74dBm. + + rtl8225z2_set_gain(dev,4); + //rtl8225z2_set_gain(dev,2); + + write_phy_cck(dev, 0x0, 0x98); mdelay(1); + write_phy_cck(dev, 0x3, 0x20); mdelay(1); + write_phy_cck(dev, 0x4, 0x7e); mdelay(1); + write_phy_cck(dev, 0x5, 0x12); mdelay(1); + write_phy_cck(dev, 0x6, 0xfc); mdelay(1); + write_phy_cck(dev, 0x7, 0x78);mdelay(1); + /* Ver C & D & 8187*/ + write_phy_cck(dev, 0x8, 0x2e);mdelay(1); + + write_phy_cck(dev, 0x9, 0x11);mdelay(1); + write_phy_cck(dev, 0xa, 0x17);mdelay(1); + write_phy_cck(dev, 0xb, 0x11);mdelay(1); + + write_phy_cck(dev, 0x10, ((priv->card_type == USB) ? 0x9b: 0x93)); mdelay(1); + write_phy_cck(dev, 0x11, 0x88); mdelay(1); + write_phy_cck(dev, 0x12, 0x47); mdelay(1); + write_phy_cck(dev, 0x13, 0xd0); /* Ver C & D & 8187*/ + + write_phy_cck(dev, 0x19, 0x0); mdelay(1); + write_phy_cck(dev, 0x1a, 0xa0); mdelay(1); + write_phy_cck(dev, 0x1b, 0x8); mdelay(1); + write_phy_cck(dev, 0x1d, 0x0); mdelay(1); + + write_phy_cck(dev, 0x40, 0x86); /* CCK Carrier Sense Threshold */ mdelay(1); + + write_phy_cck(dev, 0x41, 0x9d);mdelay(1); + + + write_phy_cck(dev, 0x42, 0x15); mdelay(1); + write_phy_cck(dev, 0x43, 0x18); mdelay(1); + + + write_phy_cck(dev, 0x44, 0x36); mdelay(1); + write_phy_cck(dev, 0x45, 0x35); mdelay(1); + write_phy_cck(dev, 0x46, 0x2e); mdelay(1); + write_phy_cck(dev, 0x47, 0x25); mdelay(1); + write_phy_cck(dev, 0x48, 0x1c); mdelay(1); + write_phy_cck(dev, 0x49, 0x12); mdelay(1); + write_phy_cck(dev, 0x4a, 0x09); mdelay(1); + write_phy_cck(dev, 0x4b, 0x04); mdelay(1); + write_phy_cck(dev, 0x4c, 0x5);mdelay(1); + + + write_nic_byte(dev, 0x5b, 0x0d); mdelay(1); + + + + // <> + // // TESTR 0xb 8187 + // write_phy_cck(dev, 0x10, 0x93);// & 0xfb); + // + // //if(priv->card_type != USB){ + // write_phy_ofdm(dev, 0x2, 0x62); + // write_phy_ofdm(dev, 0x6, 0x0); + // write_phy_ofdm(dev, 0x8, 0x0); + // //} + + rtl8225z2_SetTXPowerLevel(dev, channel); + + write_phy_cck(dev, 0x10, 0x9b); mdelay(1); /* Rx ant A, 0xdb for B */ + write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); /* Rx ant A, 0x10 for B */ + + rtl8185_tx_antenna(dev, 0x3); /* TX ant A, 0x0 for B */ + + /* switch to high-speed 3-wire + * last digit. 2 for both cck and ofdm + */ + if(priv->card_type == USB) + write_nic_dword(dev, 0x94, 0x3dc00002); + else{ + write_nic_dword(dev, 0x94, 0x15c00002); + rtl8185_rf_pins_enable(dev); + } + + // if(priv->card_type != USB) + // rtl8225_set_gain(dev, 4); /* FIXME this '1' is random */ // <> + // rtl8225_set_mode(dev, 1); /* FIXME start in B mode */ // <> + // + // /* make sure is waken up! */ + // write_rtl8225(dev,0x4, 0x9ff); + // rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_ON); + // rtl8185_set_anaparam2(dev, RTL8225_ANAPARAM2_ON); + + rtl8225_rf_set_chan(dev, priv->chan); + + //write_nic_word(dev,BRSR,brsr); + + //rtl8225z2_rf_set_mode(dev); + } +} + +void rtl8225z2_rf_set_mode(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if(priv->ieee80211->mode == IEEE_A) + { + write_rtl8225(dev, 0x5, 0x1865); + write_nic_dword(dev, RF_PARA, 0x10084); + write_nic_dword(dev, RF_TIMING, 0xa8008); + write_phy_ofdm(dev, 0x0, 0x0); + write_phy_ofdm(dev, 0xa, 0x6); + write_phy_ofdm(dev, 0xb, 0x99); + write_phy_ofdm(dev, 0xf, 0x20); + write_phy_ofdm(dev, 0x11, 0x7); + + rtl8225z2_set_gain(dev,4); + + write_phy_ofdm(dev,0x15, 0x40); + write_phy_ofdm(dev,0x17, 0x40); + + write_nic_dword(dev, 0x94,0x10000000); + }else{ + + write_rtl8225(dev, 0x5, 0x1864); + write_nic_dword(dev, RF_PARA, 0x10044); + write_nic_dword(dev, RF_TIMING, 0xa8008); + write_phy_ofdm(dev, 0x0, 0x1); + write_phy_ofdm(dev, 0xa, 0x6); + write_phy_ofdm(dev, 0xb, 0x99); + write_phy_ofdm(dev, 0xf, 0x20); + write_phy_ofdm(dev, 0x11, 0x7); + + rtl8225z2_set_gain(dev,4); + + write_phy_ofdm(dev,0x15, 0x40); + write_phy_ofdm(dev,0x17, 0x40); + + write_nic_dword(dev, 0x94,0x04000002); + } +} diff --git a/drivers/net/wireless/rtl8187b/r8180_wx.c b/drivers/net/wireless/rtl8187b/r8180_wx.c new file mode 100644 index 0000000..2bb9fe3 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_wx.c @@ -0,0 +1,2067 @@ +/* + This file contains wireless extension handlers. + + This is part of rtl8180 OpenSource driver. + Copyright (C) Andrea Merello 2004-2005 + Released under the terms of GPL (General Public Licence) + + Parts of this driver are based on the GPL part + of the official realtek driver. + + Parts of this driver are based on the rtl8180 driver skeleton + from Patric Schenke & Andres Salomon. + + Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver. + + We want to tanks the Authors of those projects and the Ndiswrapper + project Authors. +*/ + + + +#include "r8187.h" +#include "r8180_hw.h" +//added 1117 +#include "ieee80211/ieee80211.h" +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + + +//#define RATE_COUNT 4 +u32 rtl8180_rates[] = {1000000,2000000,5500000,11000000, + 6000000,9000000,12000000,18000000,24000000,36000000,48000000,54000000}; +#define RATE_COUNT sizeof(rtl8180_rates)/(sizeof(rtl8180_rates[0])) + +#ifdef _RTL8187_EXT_PATCH_ +#define IW_MODE_MESH 11 +static int r8180_wx_join_mesh(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +int r8180_wx_set_channel(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +static int r8180_wx_mesh_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +static int r8180_wx_get_mesh_list(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +#endif + +static int r8180_wx_get_freq(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + return ieee80211_wx_get_freq(priv->ieee80211,a,wrqu,b); +} + + +#if 0 + +static int r8180_wx_set_beaconinterval(struct net_device *dev, struct iw_request_info *aa, + union iwreq_data *wrqu, char *b) +{ + int *parms = (int *)b; + int bi = parms[0]; + + struct r8180_priv *priv = ieee80211_priv(dev); + if(priv->ieee80211->bHwRadioOff) + return 0; + down(&priv->wx_sem); + DMESG("setting beacon interval to %x",bi); + + priv->ieee80211->beacon_interval=bi; + rtl8180_commit(dev); + up(&priv->wx_sem); + + return 0; +} + + +static int r8180_wx_set_forceassociate(struct net_device *dev, struct iw_request_info *aa, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv=ieee80211_priv(dev); + int *parms = (int *)extra; + if(priv->ieee80211->bHwRadioOff) + return 0; + + priv->ieee80211->force_associate = (parms[0] > 0); + + + return 0; +} + +#endif +static int r8180_wx_get_mode(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8180_priv *priv=ieee80211_priv(dev); + + return ieee80211_wx_get_mode(priv->ieee80211,a,wrqu,b); +} + + + +static int r8180_wx_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + return ieee80211_wx_get_rate(priv->ieee80211,info,wrqu,extra); +} + + + +static int r8180_wx_set_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + struct r8180_priv *priv = ieee80211_priv(dev); + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + + ret = ieee80211_wx_set_rate(priv->ieee80211,info,wrqu,extra); + + up(&priv->wx_sem); + + return ret; +} +#ifdef JOHN_IOCTL +u16 read_rtl8225(struct net_device *dev, u8 addr); +void write_rtl8225(struct net_device *dev, u8 adr, u16 data); +u32 john_read_rtl8225(struct net_device *dev, u8 adr); +void _write_rtl8225(struct net_device *dev, u8 adr, u16 data); + +static int r8180_wx_read_regs(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 addr = 0; + u16 data1; + + down(&priv->wx_sem); + + + get_user(addr,(u8*)wrqu->data.pointer); + data1 = read_rtl8225(dev, addr); + wrqu->data.length = data1; + + up(&priv->wx_sem); + return 0; + +} + +static int r8180_wx_write_regs(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 addr = 0; + + down(&priv->wx_sem); + + get_user(addr, (u8*)wrqu->data.pointer); + write_rtl8225(dev, addr, wrqu->data.length); + + up(&priv->wx_sem); + return 0; + +} + +void rtl8187_write_phy(struct net_device *dev, u8 adr, u32 data); +u8 rtl8187_read_phy(struct net_device *dev,u8 adr, u32 data); + +static int r8180_wx_read_bb(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 databb; +#if 0 + int i; + for(i=0;i<12;i++) printk("%8x\n", read_cam(dev, i) ); +#endif + + down(&priv->wx_sem); + + databb = rtl8187_read_phy(dev, (u8)wrqu->data.length, 0x00000000); + wrqu->data.length = databb; + + up(&priv->wx_sem); + return 0; +} + +void rtl8187_write_phy(struct net_device *dev, u8 adr, u32 data); +static int r8180_wx_write_bb(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 databb = 0; + + down(&priv->wx_sem); + + get_user(databb, (u8*)wrqu->data.pointer); + rtl8187_write_phy(dev, wrqu->data.length, databb); + + up(&priv->wx_sem); + return 0; + +} + + +static int r8180_wx_write_nicb(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u32 addr = 0; + + down(&priv->wx_sem); + + get_user(addr, (u32*)wrqu->data.pointer); + write_nic_byte(dev, addr, wrqu->data.length); + + up(&priv->wx_sem); + return 0; + +} +static int r8180_wx_read_nicb(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u32 addr = 0; + u16 data1; + + down(&priv->wx_sem); + + get_user(addr,(u32*)wrqu->data.pointer); + data1 = read_nic_byte(dev, addr); + wrqu->data.length = data1; + + up(&priv->wx_sem); + return 0; +} + +static inline int is_same_network(struct ieee80211_network *src, + struct ieee80211_network *dst, + struct ieee80211_device *ieee) +{ + /* A network is only a duplicate if the channel, BSSID, ESSID + * and the capability field (in particular IBSS and BSS) all match. + * We treat all with the same BSSID and channel + * as one network */ + return (((src->ssid_len == dst->ssid_len)||(ieee->iw_mode == IW_MODE_INFRA)) && //YJ,mod, 080819,for hidden ap + //((src->ssid_len == dst->ssid_len) && + (src->channel == dst->channel) && + !memcmp(src->bssid, dst->bssid, ETH_ALEN) && + (!memcmp(src->ssid, dst->ssid, src->ssid_len)||(ieee->iw_mode == IW_MODE_INFRA)) && //YJ,mod, 080819,for hidden ap + //!memcmp(src->ssid, dst->ssid, src->ssid_len) && + ((src->capability & WLAN_CAPABILITY_IBSS) == + (dst->capability & WLAN_CAPABILITY_IBSS)) && + ((src->capability & WLAN_CAPABILITY_BSS) == + (dst->capability & WLAN_CAPABILITY_BSS))); +} + +static int r8180_wx_get_ap_status(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + struct ieee80211_network *target; + int name_len; + + down(&priv->wx_sem); + + //count the length of input ssid + for(name_len=0 ; ((char*)wrqu->data.pointer)[name_len]!='\0' ; name_len++); + + //search for the correspoding info which is received + list_for_each_entry(target, &ieee->network_list, list) { + if ( (target->ssid_len == name_len) && + (strncmp(target->ssid, (char*)wrqu->data.pointer, name_len)==0)){ + if( ((jiffies-target->last_scanned)/HZ > 1) && (ieee->state == IEEE80211_LINKED) && (is_same_network(&ieee->current_network,target, ieee)) ) + wrqu->data.length = 999; + else + wrqu->data.length = target->SignalStrength; + if(target->wpa_ie_len>0 || target->rsn_ie_len>0 ) + //set flags=1 to indicate this ap is WPA + wrqu->data.flags = 1; + else wrqu->data.flags = 0; + + + break; + } + } + + if (&target->list == &ieee->network_list){ + wrqu->data.flags = 3; + } + up(&priv->wx_sem); + return 0; +} + + + +#endif + +static int r8180_wx_set_rawtx(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int ret; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + + ret = ieee80211_wx_set_rawtx(priv->ieee80211, info, wrqu, extra); + + up(&priv->wx_sem); + + return ret; + +} + +static int r8180_wx_set_crcmon(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int *parms = (int *)extra; + int enable = (parms[0] > 0); + short prev = priv->crcmon; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + + if(enable) + priv->crcmon=1; + else + priv->crcmon=0; + + DMESG("bad CRC in monitor mode are %s", + priv->crcmon ? "accepted" : "rejected"); + + if(prev != priv->crcmon && priv->up){ + rtl8180_down(dev); + rtl8180_up(dev); + } + + up(&priv->wx_sem); + + return 0; +} + +static int r8180_wx_set_mode(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int ret; + if(priv->ieee80211->bHwRadioOff) + return 0; + +#ifdef _RTL8187_EXT_PATCH_ + if (priv->mshobj && (priv->ieee80211->iw_ext_mode==11)) return 0; +#endif + down(&priv->wx_sem); + +#ifdef CONFIG_IPS + if(priv->bInactivePs){ + if(wrqu->mode != IW_MODE_INFRA){ + down(&priv->ieee80211->ips_sem); + IPSLeave(dev); + up(&priv->ieee80211->ips_sem); + } + } +#endif + ret = ieee80211_wx_set_mode(priv->ieee80211,a,wrqu,b); + + rtl8187_set_rxconf(dev); + + up(&priv->wx_sem); + return ret; +} + + +//YJ,add,080819,for hidden ap +struct iw_range_with_scan_capa +{ + /* 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; +}; +//YJ,add,080819,for hidden ap + +static int rtl8180_wx_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + struct r8180_priv *priv = ieee80211_priv(dev); + u16 val; + int i; + struct iw_range_with_scan_capa* tmp = (struct iw_range_with_scan_capa*)range; //YJ,add,080819,for hidden ap + + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + /* Let's try to keep this struct in the same order as in + * linux/include/wireless.h + */ + + /* TODO: See what values we can set, and remove the ones we can't + * set, or fill them with some default data. + */ + + /* ~5 Mb/s real (802.11b) */ + range->throughput = 5 * 1000 * 1000; + + // TODO: Not used in 802.11b? +// range->min_nwid; /* Minimal NWID we are able to set */ + // TODO: Not used in 802.11b? +// range->max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ +// range->old_num_channels; +// range->old_num_frequency; +// range->old_freq[6]; /* Filler to keep "version" at the same offset */ + if(priv->rf_set_sens != NULL) + range->sensitivity = priv->max_sens; /* signal level threshold range */ + + range->max_qual.qual = 100; + /* TODO: Find real max RSSI and stick here */ + range->max_qual.level = 0; + range->max_qual.noise = -98; + range->max_qual.updated = 7; /* Updated all three */ + + range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */ + /* TODO: Find real 'good' to 'bad' threshol value for RSSI */ + range->avg_qual.level = 20 + -98; + range->avg_qual.noise = 0; + range->avg_qual.updated = 7; /* Updated all three */ + + range->num_bitrates = RATE_COUNT; + + for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) { + range->bitrate[i] = rtl8180_rates[i]; + } + + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->pm_capa = 0; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 16; + +// range->retry_capa; /* What retry options are supported */ +// range->retry_flags; /* How to decode max/min retry limit */ +// range->r_time_flags; /* How to decode max/min retry life */ +// range->min_retry; /* Minimal number of retries */ +// range->max_retry; /* Maximal number of retries */ +// range->min_r_time; /* Minimal retry lifetime */ +// range->max_r_time; /* Maximal retry lifetime */ + + range->num_channels = 14; + + for (i = 0, val = 0; i < 14; i++) { + + // Include only legal frequencies for some countries +#ifdef ENABLE_DOT11D + if ((GET_DOT11D_INFO(priv->ieee80211)->channel_map)[i+1]) { +#else + if ((priv->ieee80211->channel_map)[i+1]) { +#endif + range->freq[val].i = i + 1; + range->freq[val].m = ieee80211_wlan_frequencies[i] * 100000; + range->freq[val].e = 1; + val++; + } else { + // FIXME: do we need to set anything for channels + // we don't use ? + } + + if (val == IW_MAX_FREQUENCIES) + break; + } + + range->num_frequency = val; + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + tmp->scan_capa = 0x01; //YJ,add,080819,for hidden ap + + return 0; +} + + +static int r8180_wx_set_scan(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device* ieee = priv->ieee80211; + int ret; + + if(priv->ieee80211->bHwRadioOff) + return 0; + //printk("==============>%s()\n",__FUNCTION__); + if(!priv->up) + return -1; + + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) + { + struct iw_scan_req* req = (struct iw_scan_req*)b; + if (req->essid_len) + { + ieee->current_network.ssid_len = req->essid_len; + memcpy(ieee->current_network.ssid, req->essid, req->essid_len); + } + } + + //set Tr switch to hardware control to scan more bss + if(priv->TrSwitchState == TR_SW_TX) { + //YJ,add,080611 + write_nic_byte(dev, RFPinsSelect, (u8)(priv->wMacRegRfPinsSelect)); + write_nic_byte(dev, RFPinsOutput, (u8)(priv->wMacRegRfPinsOutput)); + //YJ,add,080611,end + priv->TrSwitchState = TR_HW_CONTROLLED; + } +#ifdef _RTL8187_EXT_PATCH_ + if((priv->ieee80211->iw_mode == IW_MODE_MESH) && (priv->ieee80211->iw_ext_mode == IW_MODE_MESH)){ + r8180_wx_mesh_scan(dev,a,wrqu,b); + ret = 0; + } + else +#endif + { + down(&priv->wx_sem); + if(priv->ieee80211->state != IEEE80211_LINKED){ + //printk("===>start no link scan\n"); + //ieee80211_start_scan(priv->ieee80211); + //lzm mod 090115 because wq can't scan complete once + //because after start protocal wq scan is in doing + //so we should stop it first. + ieee80211_stop_scan(priv->ieee80211); + ieee80211_start_scan_syncro(priv->ieee80211); + ret = 0; + } else { + ret = ieee80211_wx_set_scan(priv->ieee80211,a,wrqu,b); + } + up(&priv->wx_sem); + } + return ret; +} + + +static int r8180_wx_get_scan(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + + int ret; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(!priv->up) return -1; +#ifdef _RTL8187_EXT_PATCH_ + if((priv->ieee80211->iw_mode == IW_MODE_MESH) && (priv->ieee80211->iw_ext_mode == IW_MODE_MESH)){ + ret = r8180_wx_get_mesh_list(dev, a, wrqu, b); + } + else +#endif + { + down(&priv->wx_sem); + + ret = ieee80211_wx_get_scan(priv->ieee80211,a,wrqu,b); + + up(&priv->wx_sem); + } + return ret; +} + + +static int r8180_wx_set_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int ret; +#ifdef _RTL8187_EXT_PATCH_ + struct ieee80211_device *ieee = priv->ieee80211; + char ch = 0; + char tmpmeshid[32]; + char *p; + int tmpmeshid_len=0; + int i; + short proto_started; +#endif + if(priv->ieee80211->bHwRadioOff) + return 0; + //printk("==========>%s()\n",__FUNCTION__); + down(&priv->wx_sem); + +#ifdef _RTL8187_EXT_PATCH_ + if((priv->ieee80211->iw_mode == IW_MODE_MESH) && (priv->ieee80211->iw_ext_mode == IW_MODE_MESH)){ + if (wrqu->essid.length > IW_ESSID_MAX_SIZE){ + ret= -E2BIG; + goto out; + } + if (wrqu->essid.flags && (wrqu->essid.length > 1)) { + memset(tmpmeshid,0,32); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + tmpmeshid_len=wrqu->essid.length; +#else + tmpmeshid_len=wrqu->essid.length + 1; +#endif + p=b+tmpmeshid_len-2; + for(i=tmpmeshid_len-1;i>0;i--) + { + if((*p)=='@') + break; + p--; + } + if((i == 0) || (i == 1)){ + printk("error:wrong meshid\n"); + ret = -1; + goto out; + } + + memcpy(tmpmeshid,b,(i-1)); + p++; + if((tmpmeshid_len-1-i)==1) + { + if(*p > '9'|| *p <= '0'){ + goto out; + } else { + ch = *p - '0'; + } + } + else if((tmpmeshid_len-1-i)==2) + { + if((*p == '1') && (*(p+1) >= '0') && (*(p+1) <= '9')) + ch = (*p - '0') * 10 + (*(p+1) - '0'); + else + goto out; + } + else { + ret = 0; + goto out; + } + if(ch > 14) + { + ret = 0; + printk("channel is invalid: %d\n",ch); + goto out; + } + ieee->sync_scan_hurryup = 1; + + proto_started = ieee->proto_started; + if(proto_started) + ieee80211_stop_protocol(ieee); + + printk("==============>tmpmeshid is %s\n",tmpmeshid); + priv->mshobj->ext_patch_r8180_wx_set_meshID(dev, tmpmeshid); + priv->mshobj->ext_patch_r8180_wx_set_mesh_chan(dev,ch); + r8180_wx_set_channel(dev, NULL, NULL, &ch); + if (proto_started) + ieee80211_start_protocol(ieee); + } + else{ + printk("BUG:meshid is null\n"); + ret=0; + goto out; + } + + ret = 0; + } + else +#endif + { + ret = ieee80211_wx_set_essid(priv->ieee80211,a,wrqu,b); + } + +#ifdef _RTL8187_EXT_PATCH_ +out: +#endif + up(&priv->wx_sem); + return ret; +} + + +static int r8180_wx_get_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct r8180_priv *priv = ieee80211_priv(dev); + + down(&priv->wx_sem); + + ret = ieee80211_wx_get_essid(priv->ieee80211, a, wrqu, b); + + up(&priv->wx_sem); + + return ret; +} + + +static int r8180_wx_set_freq(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + + ret = ieee80211_wx_set_freq(priv->ieee80211, a, wrqu, b); + + up(&priv->wx_sem); + return ret; +} + +static int r8180_wx_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + return ieee80211_wx_get_name(priv->ieee80211, info, wrqu, extra); +} + + +static int r8180_wx_set_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if(priv->ieee80211->bHwRadioOff) + return 0; + + if (wrqu->frag.disabled) + priv->ieee80211->fts = DEFAULT_FRAG_THRESHOLD; + else { + if (wrqu->frag.value < MIN_FRAG_THRESHOLD || + wrqu->frag.value > MAX_FRAG_THRESHOLD) + return -EINVAL; + + priv->ieee80211->fts = wrqu->frag.value & ~0x1; + } + + return 0; +} + + +static int r8180_wx_get_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + wrqu->frag.value = priv->ieee80211->fts; + wrqu->frag.fixed = 0; /* no auto select */ + wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FRAG_THRESHOLD); + + return 0; +} + + +static int r8180_wx_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra) +{ + int ret; + struct r8180_priv *priv = ieee80211_priv(dev); + if(priv->ieee80211->bHwRadioOff) + return 0; + + //printk("in function %s\n",__FUNCTION__); +#ifdef _RTL8187_EXT_PATCH_ + if (priv->mshobj && (priv->ieee80211->iw_ext_mode==11)){ + return 0; + } +#endif + down(&priv->wx_sem); + + ret = ieee80211_wx_set_wap(priv->ieee80211,info,awrq,extra); + + up(&priv->wx_sem); + return ret; + +} + + +static int r8180_wx_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + return ieee80211_wx_get_wap(priv->ieee80211,info,wrqu,extra); +} + + +static int r8180_wx_get_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + return ieee80211_wx_get_encode(priv->ieee80211, info, wrqu, key); +} + +static int r8180_wx_set_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int ret; +#ifdef JOHN_HWSEC +// struct ieee80211_device *ieee = priv->ieee80211; +// u32 TargetContent; + u32 hwkey[4]={0,0,0,0}; + u8 mask=0xff; + u32 key_idx=0; + u8 broadcast_addr[6] ={ 0xff,0xff,0xff,0xff,0xff,0xff}; + u8 zero_addr[4][6] ={ {0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x01}, + {0x00,0x00,0x00,0x00,0x00,0x02}, + {0x00,0x00,0x00,0x00,0x00,0x03} }; + int i; + +#endif + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + + DMESG("Setting SW wep key"); + ret = ieee80211_wx_set_encode(priv->ieee80211,info,wrqu,key); + + up(&priv->wx_sem); + +#ifdef JOHN_HWSEC + + //sometimes, the length is zero while we do not type key value + if(wrqu->encoding.length!=0){ + + for(i=0 ; i<4 ; i++){ + hwkey[i] |= key[4*i+0]&mask; + if(i==1&&(4*i+1)==wrqu->encoding.length) mask=0x00; + if(i==3&&(4*i+1)==wrqu->encoding.length) mask=0x00; + hwkey[i] |= (key[4*i+1]&mask)<<8; + hwkey[i] |= (key[4*i+2]&mask)<<16; + hwkey[i] |= (key[4*i+3]&mask)<<24; + } + + #define CONF_WEP40 0x4 + #define CONF_WEP104 0x14 + + switch(wrqu->encoding.flags){ + case 0: + case 1: key_idx = 0; break; + case 2: key_idx = 1; break; + case 3: key_idx = 2; break; + case 4: key_idx = 3; break; + default: break; + } + + if(wrqu->encoding.length==0x5){ + setKey( dev, + key_idx, //EntryNo + key_idx, //KeyIndex + KEY_TYPE_WEP40, //KeyType + zero_addr[key_idx], + 0, //DefaultKey + hwkey); //KeyContent + + if(key_idx == 0){ + + write_nic_byte(dev, WPA_CONFIG, 7); + + setKey( dev, + 4, //EntryNo + key_idx, //KeyIndex + KEY_TYPE_WEP40, //KeyType + broadcast_addr, //addr + 0, //DefaultKey + hwkey); //KeyContent + } + } + + else if(wrqu->encoding.length==0xd){ + setKey( dev, + key_idx, //EntryNo + key_idx, //KeyIndex + KEY_TYPE_WEP104, //KeyType + zero_addr[key_idx], + 0, //DefaultKey + hwkey); //KeyContent + + if(key_idx == 0){ + + write_nic_byte(dev, WPA_CONFIG, 7); + + setKey( dev, + 4, //EntryNo + key_idx, //KeyIndex + KEY_TYPE_WEP104, //KeyType + broadcast_addr, //addr + 0, //DefaultKey + hwkey); //KeyContent + } + } + else printk("wrong type in WEP, not WEP40 and WEP104\n"); + + } + + //consider the setting different key index situation + //wrqu->encoding.flags = 801 means that we set key with index "1" + if(wrqu->encoding.length==0 && (wrqu->encoding.flags >>8) == 0x8 ){ + + write_nic_byte(dev, WPA_CONFIG, 7); + + //copy wpa config from default key(key0~key3) to broadcast key(key5) + // + key_idx = (wrqu->encoding.flags & 0xf)-1 ; + write_cam(dev, (4*6), 0xffff0000|read_cam(dev, key_idx*6) ); + write_cam(dev, (4*6)+1, 0xffffffff); + write_cam(dev, (4*6)+2, read_cam(dev, (key_idx*6)+2) ); + write_cam(dev, (4*6)+3, read_cam(dev, (key_idx*6)+3) ); + write_cam(dev, (4*6)+4, read_cam(dev, (key_idx*6)+4) ); + write_cam(dev, (4*6)+5, read_cam(dev, (key_idx*6)+5) ); + } + +#endif /*JOHN_HWSEC*/ + return ret; +} + + +static int r8180_wx_set_scan_type(struct net_device *dev, struct iw_request_info *aa, union + iwreq_data *wrqu, char *p){ + + struct r8180_priv *priv = ieee80211_priv(dev); + int *parms=(int*)p; + int mode=parms[0]; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + priv->ieee80211->active_scan = mode; + + return 1; +} + + + +static int r8180_wx_set_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int err = 0; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + + if (wrqu->retry.flags & IW_RETRY_LIFETIME || + wrqu->retry.disabled){ + err = -EINVAL; + goto exit; + } + if (!(wrqu->retry.flags & IW_RETRY_LIMIT)){ + err = -EINVAL; + goto exit; + } + + if(wrqu->retry.value > R8180_MAX_RETRY){ + err= -EINVAL; + goto exit; + } + if (wrqu->retry.flags & IW_RETRY_MAX) { + priv->retry_rts = wrqu->retry.value; + DMESG("Setting retry for RTS/CTS data to %d", wrqu->retry.value); + + }else { + priv->retry_data = wrqu->retry.value; + DMESG("Setting retry for non RTS/CTS data to %d", wrqu->retry.value); + } + + /* FIXME ! + * We might try to write directly the TX config register + * or to restart just the (R)TX process. + * I'm unsure if whole reset is really needed + */ + + rtl8180_commit(dev); + /* + if(priv->up){ + rtl8180_rtx_disable(dev); + rtl8180_rx_enable(dev); + rtl8180_tx_enable(dev); + + } + */ +exit: + up(&priv->wx_sem); + + return err; +} + +static int r8180_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + + wrqu->retry.disabled = 0; /* can't be disabled */ + + if ((wrqu->retry.flags & IW_RETRY_TYPE) == + IW_RETRY_LIFETIME) + return -EINVAL; + + if (wrqu->retry.flags & IW_RETRY_MAX) { + wrqu->retry.flags = IW_RETRY_LIMIT & IW_RETRY_MAX; + wrqu->retry.value = priv->retry_rts; + } else { + wrqu->retry.flags = IW_RETRY_LIMIT & IW_RETRY_MIN; + wrqu->retry.value = priv->retry_data; + } + //DMESG("returning %d",wrqu->retry.value); + + + return 0; +} + +static int r8180_wx_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + if(priv->rf_set_sens == NULL) + return -1; /* we have not this support for this radio */ + wrqu->sens.value = priv->sens; + return 0; +} + + +static int r8180_wx_set_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + struct r8180_priv *priv = ieee80211_priv(dev); + + short err = 0; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + //DMESG("attempt to set sensivity to %ddb",wrqu->sens.value); + if(priv->rf_set_sens == NULL) { + err= -1; /* we have not this support for this radio */ + goto exit; + } + if(priv->rf_set_sens(dev, wrqu->sens.value) == 0) + priv->sens = wrqu->sens.value; + else + err= -EINVAL; + +exit: + up(&priv->wx_sem); + + return err; +} + + +static int dummy(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu,char *b) +{ + return -1; +} +static int r8180_wx_set_enc_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + struct r8180_priv *priv = ieee80211_priv(dev); + //printk("===>%s()\n", __FUNCTION__); + + int ret=0; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + ret = ieee80211_wx_set_encode_ext(priv->ieee80211, info, wrqu, extra); + up(&priv->wx_sem); + return ret; + +} +static int r8180_wx_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data* data, char *extra) +{ + //printk("====>%s()\n", __FUNCTION__); + struct r8180_priv *priv = ieee80211_priv(dev); + int ret=0; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + ret = ieee80211_wx_set_auth(priv->ieee80211, info, &(data->param), extra); + up(&priv->wx_sem); + return ret; +} + +static int r8180_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + //printk("====>%s()\n", __FUNCTION__); + + int ret=0; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); +#if 1 + ret = ieee80211_wx_set_mlme(priv->ieee80211, info, wrqu, extra); +#endif + up(&priv->wx_sem); + return ret; +} + +static int r8180_wx_set_gen_ie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data* data, char *extra) +{ + //printk("====>%s(), len:%d\n", __FUNCTION__, data->length); + int ret=0; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); +#if 1 + ret = ieee80211_wx_set_gen_ie(priv->ieee80211, extra, data->data.length); +#endif + up(&priv->wx_sem); + //printk("<======%s(), ret:%d\n", __FUNCTION__, ret); + return ret; + + +} + +#ifdef _RTL8187_EXT_PATCH_ +/* + Output: + (case 1) Mesh: Enable. MESHID=[%s] (max length of %s is 32 bytes). + (case 2) Mesh: Disable. +*/ +static int r8180_wx_get_meshinfo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_get_meshinfo ) + return 0; + return priv->mshobj->ext_patch_r8180_wx_get_meshinfo(dev, info, wrqu, extra); +} + + +static int r8180_wx_enable_mesh(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + + int ret = 0; + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_enable_mesh ) + return 0; + + down(&priv->wx_sem); + if(priv->mshobj->ext_patch_r8180_wx_enable_mesh(dev)) + { + union iwreq_data tmprqu; + tmprqu.mode = ieee->iw_mode; + ieee->iw_mode = 0; + ret = ieee80211_wx_set_mode(ieee, info, &tmprqu, extra); + rtl8187_set_rxconf(dev); + } + + up(&priv->wx_sem); + + return ret; + +} + +static int r8180_wx_disable_mesh(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + + int ret = 0; + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_disable_mesh ) + return 0; + + down(&priv->wx_sem); + if(priv->mshobj->ext_patch_r8180_wx_disable_mesh(dev)) + { + union iwreq_data tmprqu; + tmprqu.mode = ieee->iw_mode; + ieee->iw_mode = 999; + ret = ieee80211_wx_set_mode(ieee, info, &tmprqu, extra); + rtl8187_set_rxconf(dev); + } + + up(&priv->wx_sem); + + return ret; +} + + +int r8180_wx_set_channel(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ch = *extra; + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + // is 11s ? + if (!priv->mshobj || (ieee->iw_mode != ieee->iw_ext_mode) || !priv->mshobj->ext_patch_r8180_wx_set_channel ) + return 0; + + printk("set channel = %d\n", ch); + if ( ch < 0 ) + { + ieee80211_start_scan(ieee); // auto + ieee->meshScanMode =2; + } + else + { +//#ifdef NETWORKMANAGER_UI + if((priv->ieee80211->iw_mode == IW_MODE_MESH) && (priv->ieee80211->iw_ext_mode == IW_MODE_MESH)){ + } +//#else + else{ + down(&priv->wx_sem);} +//#endif + ieee->meshScanMode =0; + // ieee->set_chan(dev, ch); +//#ifdef _RTL8187_EXT_PATCH_ + if(priv->mshobj->ext_patch_r8180_wx_set_channel) + { + priv->mshobj->ext_patch_r8180_wx_set_channel(ieee, ch); + priv->mshobj->ext_patch_r8180_wx_set_mesh_chan(dev,ch); + } +//#endif + ieee->set_chan(ieee->dev, ch); + ieee->current_network.channel = ch; + queue_work(ieee->wq, &ieee->ext_stop_scan_wq); + ieee80211_ext_send_11s_beacon(ieee); +//#ifdef NETWORKMANAGER_UI + if((priv->ieee80211->iw_mode == IW_MODE_MESH) && (priv->ieee80211->iw_ext_mode == IW_MODE_MESH)){ + } +//#else + else{ + up(&priv->wx_sem);} +//#endif + //up(&ieee->wx_sem); + + // ieee80211_stop_scan(ieee); // user set + // + + /* + netif_carrier_off(ieee->dev); + + if (ieee->data_hard_stop) + ieee->data_hard_stop(ieee->dev); + + ieee->state = IEEE80211_NOLINK; + ieee->link_change(ieee->dev); + + ieee->current_network.channel = fwrq->m; + ieee->set_chan(ieee->dev, ieee->current_network.channel); + + + if (ieee->data_hard_resume) + ieee->data_hard_resume(ieee->dev); + + netif_carrier_on(ieee->dev); + */ + + } + + return 0; +} + +static int r8180_wx_set_meshID(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if(priv->ieee80211->bHwRadioOff) + return 0; + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_set_meshID ) + return 0; + + //printk("len=%d\n", wrqu->data.length); + //printk("\nCall setMeshid."); + return priv->mshobj->ext_patch_r8180_wx_set_meshID(dev, wrqu->data.pointer); +} + + +/* reserved for future +static int r8180_wx_add_mac_allow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_set_add_mac_allow ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_set_add_mac_allow(dev, info, wrqu, extra); +} + +static int r8180_wx_del_mac_allow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_set_del_mac_allow ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_set_del_mac_allow(dev, info, wrqu, extra); +} +*/ +static int r8180_wx_add_mac_deny(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_set_add_mac_deny ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_set_add_mac_deny(dev, info, wrqu, extra); +} + +static int r8180_wx_del_mac_deny(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_set_del_mac_deny ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_set_del_mac_deny(dev, info, wrqu, extra); +} + +/* reserved for future +static int r8180_wx_get_mac_allow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_get_mac_allow ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_get_mac_allow(dev, info, wrqu, extra); +} +*/ + +static int r8180_wx_get_mac_deny(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_get_mac_deny ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_get_mac_deny(dev, info, wrqu, extra); +} + + +static int r8180_wx_get_mesh_list(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_get_mesh_list ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_get_mesh_list(dev, info, wrqu, extra); +} + +static int r8180_wx_mesh_scan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if( ! priv->mshobj || !priv->mshobj->ext_patch_r8180_wx_mesh_scan ) + return 0; + + return priv->mshobj->ext_patch_r8180_wx_mesh_scan(dev, info, wrqu, extra); +} + +static int r8180_wx_join_mesh(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int index; + int ret=0; + char extmeshid[32]; + int len=0; + char id[50], ch; +//#ifdef NETWORKMANAGER_UI + + if((priv->ieee80211->iw_mode == IW_MODE_MESH) && (priv->ieee80211->iw_ext_mode == IW_MODE_MESH)){ + printk("join mesh %s\n",extra); + if (wrqu->essid.length > IW_ESSID_MAX_SIZE){ + ret= -E2BIG; + goto out; + } + //printk("wrqu->essid.length is %d\n",wrqu->essid.length); + //printk("wrqu->essid.flags is %d\n",wrqu->essid.flags); + if((wrqu->essid.length == 1) && (wrqu->essid.flags == 1)){ + ret = 0; + goto out; + } + if (wrqu->essid.flags && wrqu->essid.length) { + if(priv->mshobj->ext_patch_r8180_wx_get_selected_mesh_channel(dev, extra, &ch)) + { + priv->mshobj->ext_patch_r8180_wx_set_meshID(dev, extra); + priv->mshobj->ext_patch_r8180_wx_set_mesh_chan(dev,ch); + r8180_wx_set_channel(dev, NULL, NULL, &ch); + } + else + printk("invalid mesh #\n"); + + } +#if 0 + else{ + if(priv->mshobj->ext_patch_r8180_wx_get_selected_mesh_channel(dev, 0, &ch)) + { + priv->mshobj->ext_patch_r8180_wx_set_meshID(dev, extra); + priv->mshobj->ext_patch_r8180_wx_set_mesh_chan(dev,ch); + r8180_wx_set_channel(dev, NULL, NULL, &ch); + } + else + printk("invalid mesh #\n"); + + } +#endif + } + else{ +//#else + index = *(extra); +// printk("index=%d\n", index); + + if( ! priv->mshobj + || !priv->mshobj->ext_patch_r8180_wx_set_meshID + || !priv->mshobj->ext_patch_r8180_wx_get_selected_mesh ) + return 0; + + if( priv->mshobj->ext_patch_r8180_wx_get_selected_mesh(dev, index, &ch, id) ) + { + // printk("ch=%d, id=%s\n", ch, id); + priv->mshobj->ext_patch_r8180_wx_set_meshID(dev, id); + priv->mshobj->ext_patch_r8180_wx_set_mesh_chan(dev,ch); + r8180_wx_set_channel(dev, NULL, NULL, &ch); + } + else + printk("invalid mesh #\n"); + } +//#endif +out: + return ret; +} + +#endif // _RTL8187_EXT_PATCH_ + + +static int r8180_wx_get_radion(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); +// u8 addr; + + down(&priv->wx_sem); + if(priv->radion == 1) { + *(int *)extra = 1; + } else { + + *(int *)extra = 0; + } + up(&priv->wx_sem); + return 0; + +} + +static int r8180_wx_set_radion(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int radion = *extra; + struct r8180_priv *priv = ieee80211_priv(dev); +// struct ieee80211_device *ieee = priv->ieee80211; + u8 btCR9346, btConfig3; + int i; + u16 u2bTFPC = 0; + u8 u1bTmp; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + printk("set radion = %d\n", radion); + +#ifdef _RTL8187_EXT_PATCH_ + if(ieee->iw_mode == ieee->iw_ext_mode) { + printk("mesh mode:: could not set radi on/off = %d\n", radion); + up(&priv->wx_sem); + return 0; + } +#endif + // Set EEM0 and EEM1 in 9346CR. + btCR9346 = read_nic_byte(dev, CR9346); + write_nic_byte(dev, CR9346, (btCR9346|0xC0) ); + // Set PARM_En in Config3. + btConfig3 = read_nic_byte(dev, CONFIG3); + write_nic_byte(dev, CONFIG3, (btConfig3|CONFIG3_PARM_En) ); + + if ( radion == 1) //radion off + { + printk("==================>RF on\n"); + write_nic_dword(dev, ANAPARAM, ANAPARM_ON); + write_nic_dword(dev, ANAPARAM2, ANAPARM2_ON); + write_nic_byte(dev, CONFIG4, (priv->RFProgType)); + + write_nic_byte(dev, 0x085, 0x24); // 061219, SD3 ED: for minicard CCK power leakage issue. + write_rtl8225(dev, 0x4, 0x9FF); + + u1bTmp = read_nic_byte(dev, 0x24E); + write_nic_byte(dev, 0x24E, (u1bTmp & (~(BIT5|BIT6))) );// 070124 SD1 Alex: turn on CCK and OFDM. + priv->radion = 1; //radion on + } + else + { + printk("==================>RF off\n"); + for(i = 0; i < MAX_DOZE_WAITING_TIMES_87B; i++) + { // Make sure TX FIFO is empty befor turn off RFE pwoer. + u2bTFPC = read_nic_word(dev, TFPC); + if(u2bTFPC == 0) + { + break; + } + else + { + printk("%d times TFPC: %d != 0 before doze!\n", (i+1), u2bTFPC); + udelay(10); + } + } + if( i == MAX_DOZE_WAITING_TIMES_87B ) + { + printk("\n\n\n SetZebraRFPowerState8187B(): %d times TFPC: %d != 0 !!!\n\n\n",\ + MAX_DOZE_WAITING_TIMES_87B, u2bTFPC); + } + + u1bTmp = read_nic_byte(dev, 0x24E); + write_nic_byte(dev, 0x24E, (u1bTmp|BIT5|BIT6));// 070124 SD1 Alex: turn off CCK and OFDM. + + write_rtl8225(dev, 0x4,0x1FF); // Turn off RF first to prevent BB lock up, suggested by PJ, 2006.03.03. + write_nic_byte(dev, 0x085, 0x04); // 061219, SD3 ED: for minicard CCK power leakage issue. + + write_nic_byte(dev, CONFIG4, (priv->RFProgType|Config4_PowerOff)); + + write_nic_dword(dev, ANAPARAM, ANAPARM_OFF); + write_nic_dword(dev, ANAPARAM2, ANAPARM2_OFF); // 070301, SD1 William: to reduce RF off power consumption to 80 mA. + priv->radion = 0; //radion off + } + // Clear PARM_En in Config3. + btConfig3 &= ~(CONFIG3_PARM_En); + write_nic_byte(dev, CONFIG3, btConfig3); + // Clear EEM0 and EEM1 in 9346CR. + btCR9346 &= ~(0xC0); + write_nic_byte(dev, CR9346, btCR9346); + + up(&priv->wx_sem); + + return 0; +} + +static int r8180_wx_set_ratadpt (struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ratadapt = *extra; + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + + if(priv->ieee80211->bHwRadioOff) + return 0; + + down(&priv->wx_sem); + printk("Set rate adaptive %s\n", (ratadapt==0)?"on":"off"); + if(ratadapt == 0) { + del_timer_sync(&priv->rateadapter_timer); + cancel_delayed_work(&priv->ieee80211->rate_adapter_wq); + priv->rateadapter_timer.function((unsigned long)dev); + } else { + del_timer_sync(&priv->rateadapter_timer); + cancel_delayed_work(&priv->ieee80211->rate_adapter_wq); + printk("force rate to %d\n", ratadapt); + ieee->rate = ratadapt; + } + up(&priv->wx_sem); + return 0; +} + +#ifdef ENABLE_TOSHIBA_CONFIG +static int r8180_wx_get_tblidx(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + //extern u8 chan_plan_index; + //printk("=========>%s(), %x\n", __FUNCTION__, priv->channel_plan); + down(&priv->wx_sem); + put_user(priv->channel_plan, (u8*)wrqu->data.pointer); + up(&priv->wx_sem); + return 0; + +} + +//This func will be called after probe auto +static int r8180_wx_set_tbl (struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 len = 0; + s8 err = -1; + extern CHANNEL_LIST Current_tbl; + down(&priv->wx_sem); + if (!wrqu->data.pointer) + { + printk("user data pointer is null\n"); + goto exit; + } + len = wrqu->data.length; + //printk("=========>%s(), len:%d\n", __FUNCTION__, len); + //memset(&Current_tbl, 0, sizeof(CHANNEL_LIST)); + if (copy_from_user((u8*)&Current_tbl, (void*)wrqu->data.pointer, len)) + { + printk("error copy from user\n"); + goto exit; + } + { + int i; + Current_tbl.Len = len; + //printk("%d\n", Current_tbl.Len); + + Dot11d_Init(priv->ieee80211); + priv->ieee80211->bGlobalDomain = false; + priv->ieee80211->bWorldWide13 = false; + + //lzm add 081205 + priv->ieee80211->MinPassiveChnlNum=12; + priv->ieee80211->IbssStartChnl= 10; + + for (i=0; ichannel_plan == COUNTRY_CODE_ETSI) + { + if(Current_tbl.Channel[i] <= 11) + { +#ifdef ENABLE_DOT11D + GET_DOT11D_INFO(priv->ieee80211)->channel_map[Current_tbl.Channel[i]] = 1; +#else + priv->ieee80211->channel_map[Current_tbl.Channel[i]] = 1; +#endif + } + else if((Current_tbl.Channel[i] >= 11) && (Current_tbl.Channel[i] <= 13)) + { +#ifdef ENABLE_DOT11D + GET_DOT11D_INFO(priv->ieee80211)->channel_map[Current_tbl.Channel[i]] = 2; +#else + priv->ieee80211->channel_map[Current_tbl.Channel[i]] = 2; +#endif + } + } + else + { + if(Current_tbl.Channel[i] <= 14) + { +#ifdef ENABLE_DOT11D + GET_DOT11D_INFO(priv->ieee80211)->channel_map[Current_tbl.Channel[i]] = 1; +#else + priv->ieee80211->channel_map[Current_tbl.Channel[i]] = 1; +#endif + } + } + } +#if 0 + printk("\n"); + for(i=1; iieee80211)->channel_map[i]); +#else + printk("%2d ", priv->ieee80211->channel_map[i]); +#endif + } + printk("\n"); + +#endif + if(priv->ieee80211->proto_started) + {//we need to restart protocol now if it was start before channel map + ieee80211_softmac_stop_protocol(priv->ieee80211); + //mdelay(1); + ieee80211_softmac_start_protocol(priv->ieee80211); + } + } + err = 0; +exit: + up(&priv->wx_sem); + return err; + + +} + +#endif + + +static iw_handler r8180_wx_handlers[] = +{ + NULL, /* SIOCSIWCOMMIT */ + r8180_wx_get_name, /* SIOCGIWNAME */ + dummy, /* SIOCSIWNWID */ + dummy, /* SIOCGIWNWID */ + r8180_wx_set_freq, /* SIOCSIWFREQ */ + r8180_wx_get_freq, /* SIOCGIWFREQ */ + r8180_wx_set_mode, /* SIOCSIWMODE */ + r8180_wx_get_mode, /* SIOCGIWMODE */ + r8180_wx_set_sens, /* SIOCSIWSENS */ + r8180_wx_get_sens, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + rtl8180_wx_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + dummy, /* SIOCSIWSPY */ + dummy, /* SIOCGIWSPY */ + NULL, /* SIOCGIWTHRSPY */ + NULL, /* SIOCWIWTHRSPY */ + r8180_wx_set_wap, /* SIOCSIWAP */ + r8180_wx_get_wap, /* SIOCGIWAP */ + r8180_wx_set_mlme, //NULL, /* SIOCSIWMLME*/ /* -- hole -- */ + dummy, /* SIOCGIWAPLIST -- depricated */ + r8180_wx_set_scan, /* SIOCSIWSCAN */ + r8180_wx_get_scan, /* SIOCGIWSCAN */ + r8180_wx_set_essid, /* SIOCSIWESSID */ + r8180_wx_get_essid, /* SIOCGIWESSID */ + dummy, /* SIOCSIWNICKN */ + dummy, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + r8180_wx_set_rate, /* SIOCSIWRATE */ + r8180_wx_get_rate, /* SIOCGIWRATE */ + dummy, /* SIOCSIWRTS */ + dummy, /* SIOCGIWRTS */ + r8180_wx_set_frag, /* SIOCSIWFRAG */ + r8180_wx_get_frag, /* SIOCGIWFRAG */ + dummy, /* SIOCSIWTXPOW */ + dummy, /* SIOCGIWTXPOW */ + r8180_wx_set_retry, /* SIOCSIWRETRY */ + r8180_wx_get_retry, /* SIOCGIWRETRY */ + r8180_wx_set_enc, /* SIOCSIWENCODE */ + r8180_wx_get_enc, /* SIOCGIWENCODE */ + dummy, /* SIOCSIWPOWER */ + dummy, /* SIOCGIWPOWER */ + NULL, /*---hole---*/ + NULL, /*---hole---*/ + r8180_wx_set_gen_ie,//NULL, /* SIOCSIWGENIE */ + NULL, /* SIOCSIWGENIE */ + r8180_wx_set_auth,//NULL, /* SIOCSIWAUTH */ + NULL,//r8180_wx_get_auth,//NULL, /* SIOCSIWAUTH */ + r8180_wx_set_enc_ext, /* SIOCSIWENCODEEXT */ + NULL,//r8180_wx_get_enc_ext,//NULL, /* SIOCSIWENCODEEXT */ + NULL, /* SIOCSIWPMKSA */ + NULL, /*---hole---*/ +}; + + +static const struct iw_priv_args r8180_private_args[] = { + { + SIOCIWFIRSTPRIV + 0x0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "badcrc" + }, + + { + SIOCIWFIRSTPRIV + 0x1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "activescan" + + }, + { + SIOCIWFIRSTPRIV + 0x2, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rawtx" + }, +#ifdef JOHN_IOCTL + { + SIOCIWFIRSTPRIV + 0x3, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "readRF" + } + , + { + SIOCIWFIRSTPRIV + 0x4, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "writeRF" + } + , + { + SIOCIWFIRSTPRIV + 0x5, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "readBB" + } + , + { + SIOCIWFIRSTPRIV + 0x6, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "writeBB" + } + , + { + SIOCIWFIRSTPRIV + 0x7, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "readnicb" + } + , + { + SIOCIWFIRSTPRIV + 0x8, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "writenicb" + } + , + { + SIOCIWFIRSTPRIV + 0x9, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "apinfo" + }, +#endif + { + SIOCIWFIRSTPRIV + 0xA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED |1, "getradion" + }, + { + SIOCIWFIRSTPRIV + 0xB, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setradion" + }, + { + SIOCIWFIRSTPRIV + 0xC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ratadpt" + }, +#ifdef ENABLE_TOSHIBA_CONFIG + { + SIOCIWFIRSTPRIV + 0xD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettblidx" + }, + { + SIOCIWFIRSTPRIV + 0xE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,0, "settblidx" + }, + +#endif + +}; + +/* + * Private ioctl interface information + +struct iw_priv_args +{ +// __u32 cmd; +// __u16 set_args; +// __u16 get_args; +// char name[IFNAMSIZ]; +//}; +*/ +//If get cmd's number is big,there may cause some problemes. +//So modified by Lawrence,071120 + +static iw_handler r8180_private_handler[] = { +// r8180_wx_set_monitor, /* SIOCIWFIRSTPRIV */ + r8180_wx_set_crcmon, /*SIOCIWSECONDPRIV*/ +// r8180_wx_set_forceassociate, +// r8180_wx_set_beaconinterval, +// r8180_wx_set_monitor_type, + r8180_wx_set_scan_type, + r8180_wx_set_rawtx, + +#if 0 +#ifdef _RTL8187_EXT_PATCH_ + r8180_wx_get_meshinfo, + r8180_wx_enable_mesh, + r8180_wx_disable_mesh, + r8180_wx_set_channel, + r8180_wx_set_meshID, + +// r8180_wx_add_mac_allow, +// r8180_wx_get_mac_allow, +// r8180_wx_del_mac_allow, + r8180_wx_add_mac_deny, + r8180_wx_get_mac_deny, + r8180_wx_del_mac_deny, + r8180_wx_get_mesh_list, + r8180_wx_mesh_scan, + r8180_wx_join_mesh, +#endif +#endif + +#ifdef JOHN_IOCTL + r8180_wx_read_regs, + r8180_wx_write_regs, + r8180_wx_read_bb, + r8180_wx_write_bb, + r8180_wx_read_nicb, + r8180_wx_write_nicb, + r8180_wx_get_ap_status, +#endif + r8180_wx_get_radion, + r8180_wx_set_radion, + r8180_wx_set_ratadpt, +#ifdef ENABLE_TOSHIBA_CONFIG + r8180_wx_get_tblidx, + r8180_wx_set_tbl, +#endif +}; + +#if WIRELESS_EXT >= 17 +//WB modefied to show signal to GUI on 18-01-2008 +static struct iw_statistics *r8180_get_wireless_stats(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_device* ieee = priv->ieee80211; + struct iw_statistics* wstats = &priv->wstats; +// struct ieee80211_network* target = NULL; + int tmp_level = 0; + int tmp_qual = 0; + int tmp_noise = 0; +// unsigned long flag; + + if (ieee->state < IEEE80211_LINKED) + { + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + return wstats; + } +#if 0 + spin_lock_irqsave(&ieee->lock, flag); + list_for_each_entry(target, &ieee->network_list, list) + { + if (is_same_network(target, &ieee->current_network)) + { + printk("it's same network:%s\n", target->ssid); +#if 0 + if (!tmp_level) + { + tmp_level = target->stats.signalstrength; + tmp_qual = target->stats.signal; + } + else + { + + tmp_level = (15*tmp_level + target->stats.signalstrength)/16; + tmp_qual = (15*tmp_qual + target->stats.signal)/16; + } +#else + tmp_level = target->stats.signal; + tmp_qual = target->stats.signalstrength; + tmp_noise = target->stats.noise; + printk("level:%d, qual:%d, noise:%d\n", tmp_level, tmp_qual, tmp_noise); +#endif + break; + } + } + spin_unlock_irqrestore(&ieee->lock, flag); +#endif + tmp_level = (&ieee->current_network)->stats.signal; + tmp_qual = (&ieee->current_network)->stats.signalstrength; + tmp_noise = (&ieee->current_network)->stats.noise; + //printk("level:%d, qual:%d, noise:%d\n", tmp_level, tmp_qual, tmp_noise); + + wstats->qual.level = tmp_level; + wstats->qual.qual = tmp_qual; + wstats->qual.noise = tmp_noise; + wstats->qual.updated = IW_QUAL_ALL_UPDATED| IW_QUAL_DBM; + return wstats; +} +#endif + + +struct iw_handler_def r8180_wx_handlers_def={ + .standard = r8180_wx_handlers, + .num_standard = sizeof(r8180_wx_handlers) / sizeof(iw_handler), + .private = r8180_private_handler, + .num_private = sizeof(r8180_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(r8180_private_args) / sizeof(struct iw_priv_args), +#if WIRELESS_EXT >= 17 + .get_wireless_stats = r8180_get_wireless_stats, +#endif + .private_args = (struct iw_priv_args *)r8180_private_args, +}; +#ifdef _RTL8187_EXT_PATCH_ +EXPORT_SYMBOL(r8180_wx_set_channel); +#endif diff --git a/drivers/net/wireless/rtl8187b/r8180_wx.h b/drivers/net/wireless/rtl8187b/r8180_wx.h new file mode 100644 index 0000000..0c62a99 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8180_wx.h @@ -0,0 +1,21 @@ +/* + This is part of rtl8180 OpenSource driver - v 0.3 + Copyright (C) Andrea Merello 2004 + Released under the terms of GPL (General Public Licence) + + Parts of this driver are based on the GPL part of the official realtek driver + Parts of this driver are based on the rtl8180 driver skeleton from Patric Schenke & Andres Salomon + Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver + + We want to tanks the Authors of such projects and the Ndiswrapper project Authors. +*/ + +/* this file (will) contains wireless extension handlers*/ + +#ifndef R8180_WX_H +#define R8180_WX_H +#include +#include "ieee80211/ieee80211.h" +extern struct iw_handler_def r8180_wx_handlers_def; + +#endif diff --git a/drivers/net/wireless/rtl8187b/r8187.h b/drivers/net/wireless/rtl8187b/r8187.h new file mode 100644 index 0000000..ab18683 --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8187.h @@ -0,0 +1,811 @@ +/* + This is part of rtl8187 OpenSource driver. + Copyright (C) Andrea Merello 2004-2005 + Released under the terms of GPL (General Public Licence) + + Parts of this driver are based on the GPL part of the + official realtek driver + + Parts of this driver are based on the rtl8180 driver skeleton + from Patric Schenke & Andres Salomon + + Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver + + We want to tanks the Authors of those projects and the Ndiswrapper + project Authors. +*/ + +#ifndef R8180H +#define R8180H + + +#define RTL8187_MODULE_NAME "rtl8187" +#define DMESG(x,a...) printk(KERN_INFO RTL8187_MODULE_NAME ": " x "\n", ## a) +#define DMESGW(x,a...) printk(KERN_WARNING RTL8187_MODULE_NAME ": WW:" x "\n", ## a) +#define DMESGE(x,a...) printk(KERN_WARNING RTL8187_MODULE_NAME ": EE:" x "\n", ## a) + +#include +#include +//#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include //for rtnl_lock() +#include +#include +#include // Necessary because we use the proc fs +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) +#include +#endif +#include "ieee80211/ieee80211.h" +#ifdef _RTL8187_EXT_PATCH_ +#include "msh_class.h" +#endif +#ifdef LED +#include "r8187_led.h" +#endif + +//added for HW security, john.0629 +#define FALSE 0 +#define TRUE 1 +#define MAX_KEY_LEN 61 +#define KEY_BUF_SIZE 5 + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +//8187B Security +#define RWCAM 0xA0 // Software read/write CAM config +#define WCAMI 0xA4 // Software write CAM input content +#define RCAMO 0xA8 // Output value from CAM according to 0xa0 setting +#define DCAM 0xAC // Debug CAM Interface +#define SECR 0xB0 // Security configuration register +#define AESMSK_FC 0xB2 // AES Mask register for frame control (0xB2~0xB3). Added by Annie, 2006-03-06. +#define AESMSK_SC 0x1FC // AES Mask for Sequence Control (0x1FC~0X1FD). Added by Annie, 2006-03-06. +#define AESMSK_QC 0x1CE // AES Mask register for QoS Control when computing AES MIC, default = 0x000F. (2 bytes) + +#define AESMSK_FC_DEFAULT 0xC78F // default value of AES MASK for Frame Control Field. (2 bytes) +#define AESMSK_SC_DEFAULT 0x000F // default value of AES MASK for Sequence Control Field. (2 bytes) +#define AESMSK_QC_DEFAULT 0x000F // default value of AES MASK for QoS Control Field. (2 bytes) + +#define CAM_CONTENT_COUNT 6 +#define CFG_DEFAULT_KEY BIT5 +#define CFG_VALID BIT15 + +//---------------------------------------------------------------------------- +// 8187B WPA Config Register (offset 0xb0, 1 byte) +//---------------------------------------------------------------------------- +#define SCR_UseDK 0x01 +#define SCR_TxSecEnable 0x02 +#define SCR_RxSecEnable 0x04 + +//---------------------------------------------------------------------------- +// 8187B CAM Config Setting (offset 0xb0, 1 byte) +//---------------------------------------------------------------------------- +#define CAM_VALID 0x8000 +#define CAM_NOTVALID 0x0000 +#define CAM_USEDK 0x0020 + + +#define CAM_NONE 0x0 +#define CAM_WEP40 0x01 +#define CAM_TKIP 0x02 +#define CAM_AES 0x04 +#define CAM_WEP104 0x05 + + +//#define CAM_SIZE 16 +#define TOTAL_CAM_ENTRY 16 +#define CAM_ENTRY_LEN_IN_DW 6 // 6, unit: in u4byte. Added by Annie, 2006-05-25. +#define CAM_ENTRY_LEN_IN_BYTE (CAM_ENTRY_LEN_IN_DW*sizeof(u4Byte)) // 24, unit: in u1byte. Added by Annie, 2006-05-25. + +#define CAM_CONFIG_USEDK 1 +#define CAM_CONFIG_NO_USEDK 0 + +#define CAM_WRITE 0x00010000 +#define CAM_READ 0x00000000 +#define CAM_POLLINIG 0x80000000 + +//================================================================= +//================================================================= + +#define EPROM_93c46 0 +#define EPROM_93c56 1 + +#define DEFAULT_FRAG_THRESHOLD 2342U +#define MIN_FRAG_THRESHOLD 256U +#define DEFAULT_BEACONINTERVAL 0x64U +#define DEFAULT_BEACON_ESSID "Rtl8187" + +#define DEFAULT_SSID "" +#define DEFAULT_RETRY_RTS 7 +#define DEFAULT_RETRY_DATA 7 +#define PRISM_HDR_SIZE 64 + +typedef enum _WIRELESS_MODE { + WIRELESS_MODE_UNKNOWN = 0x00, + WIRELESS_MODE_A = 0x01, + WIRELESS_MODE_B = 0x02, + WIRELESS_MODE_G = 0x04, + WIRELESS_MODE_AUTO = 0x08, +} WIRELESS_MODE; + +typedef enum _TR_SWITCH_STATE{ + TR_HW_CONTROLLED = 0, + TR_SW_TX = 1, +}TR_SWITCH_STATE, *PTR_SWITCH_STATE; + + +#define RTL_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 + +typedef struct buffer +{ + struct buffer *next; + u32 *buf; + +} buffer; + +typedef struct rtl_reg_debug{ + unsigned int cmd; + struct { + unsigned char type; + unsigned char addr; + unsigned char page; + unsigned char length; + } head; + unsigned char buf[0xff]; +}rtl_reg_debug; +typedef struct _CHANNEL_LIST{ + u8 Channel[MAX_CHANNEL_NUMBER + 1]; + u8 Len; +}CHANNEL_LIST, *PCHANNEL_LIST; + +#define MAX_LD_SLOT_NUM 10 +#define DEFAULT_SLOT_NUM 2 +#define KEEP_ALIVE_INTERVAL 20 // in seconds. +#define CHECK_FOR_HANG_PERIOD 2 //be equal to watchdog check time +#define DEFAULT_KEEP_ALIVE_LEVEL 1 + +typedef struct _link_detect_t +{ + u32 RxFrameNum[MAX_LD_SLOT_NUM]; // number of Rx Frame / CheckForHang_period to determine link status + u16 SlotNum; // number of CheckForHang period to determine link status, default is 2 + u16 SlotIndex; + + u32 NumTxOkInPeriod; //number of packet transmitted during CheckForHang + u32 NumRxOkInPeriod; //number of packet received during CheckForHang + + u8 IdleCount; // (KEEP_ALIVE_INTERVAL / CHECK_FOR_HANG_PERIOD) + u32 LastNumTxUnicast; + u32 LastNumRxUnicast; + + bool bBusyTraffic; //when it is set to 1, UI cann't scan at will. +}link_detect_t, *plink_detect_t; + +#if 0 + +typedef struct tx_pendingbuf +{ + struct ieee80211_txb *txb; + short ispending; + short descfrag; +} tx_pendigbuf; + +#endif + +typedef struct Stats +{ + unsigned long txrdu; +// unsigned long rxrdu; + //unsigned long rxnolast; + //unsigned long rxnodata; +// unsigned long rxreset; +// unsigned long rxwrkaround; +// unsigned long rxnopointer; + unsigned long rxok; + unsigned long rxurberr; + unsigned long rxstaterr; + unsigned long txnperr; + unsigned long txnpdrop; + unsigned long txresumed; +// unsigned long rxerr; +// unsigned long rxoverflow; +// unsigned long rxint; + unsigned long txnpokint; +// unsigned long txhpokint; +// unsigned long txhperr; +// unsigned long ints; +// unsigned long shints; + unsigned long txoverflow; +// unsigned long rxdmafail; +// unsigned long txbeacon; +// unsigned long txbeaconerr; + unsigned long txlpokint; + unsigned long txlpdrop; + unsigned long txlperr; + unsigned long txbeokint; + unsigned long txbedrop; + unsigned long txbeerr; + unsigned long txbkokint; + unsigned long txbkdrop; + unsigned long txbkerr; + unsigned long txviokint; + unsigned long txvidrop; + unsigned long txvierr; + unsigned long txvookint; + unsigned long txvodrop; + unsigned long txvoerr; + unsigned long txbeaconokint; + unsigned long txbeacondrop; + unsigned long txbeaconerr; + unsigned long txmanageokint; + unsigned long txmanagedrop; + unsigned long txmanageerr; + unsigned long txdatapkt; +} Stats; + +typedef struct ChnlAccessSetting { + u16 SIFS_Timer; + u16 DIFS_Timer; + u16 SlotTimeTimer; + u16 EIFS_Timer; + u16 CWminIndex; + u16 CWmaxIndex; +}*PCHANNEL_ACCESS_SETTING,CHANNEL_ACCESS_SETTING; + + +typedef enum _RT_RF_POWER_STATE +{ + eRfOn, + eRfSleep, + eRfOff +}RT_RF_POWER_STATE; +typedef enum _RT_PS_MODE +{ + eActive, // Active/Continuous access. + eMaxPs, // Max power save mode. + eFastPs // Fast power save mode. +}RT_PS_MODE; +// +// Three wire mode. +// +#define IC_DEFAULT_THREE_WIRE 0 +#define SW_THREE_WIRE 1 +//RTL818xB +#define SW_THREE_WIRE_BY_8051 2 +#define HW_THREE_WIRE 3 +#define HW_THREE_WIRE_BY_8051 4 +//lzm add for write time out test +typedef struct write_read_register +{ + u32 address; + u32 content; + u32 flag; +} write_read_register; +//lzm add for write time out test +typedef struct r8180_priv +{ +//lzm add for write time out test + struct write_read_register write_read_registers[200]; + u8 write_read_register_index; +//lzm add for write time out test + + struct usb_device *udev; + short epromtype; + int irq; + struct ieee80211_device *ieee80211; + + short card_8187; /* O: rtl8180, 1:rtl8185 V B/C, 2:rtl8185 V D */ + short card_8187_Bversion; /* if TCR reports card V B/C this discriminates */ + short phy_ver; /* meaningful for rtl8225 1:A 2:B 3:C */ + short enable_gpio0; + enum card_type {PCI,MINIPCI,CARDBUS,USB/*rtl8187*/}card_type; + short hw_plcp_len; + short plcp_preamble_mode; + + spinlock_t irq_lock; +// spinlock_t irq_th_lock; + spinlock_t tx_lock; +//by amy for ps + spinlock_t rf_ps_lock; +//by amy for ps + + u16 irq_mask; +// short irq_enabled; + struct net_device *dev; + short chan; + short sens; + short max_sens; + u8 chtxpwr[15]; //channels from 1 to 14, 0 not used + u8 chtxpwr_ofdm[15]; //channels from 1 to 14, 0 not used + u8 cck_txpwr_base; + u8 ofdm_txpwr_base; + u8 challow[15]; //channels from 1 to 14, 0 not used + short up; + short crcmon; //if 1 allow bad crc frame reception in monitor mode +// short prism_hdr; + +// struct timer_list scan_timer; + /*short scanpending; + short stopscan;*/ +// spinlock_t scan_lock; +// u8 active_probe; + //u8 active_scan_num; + struct semaphore wx_sem; + struct semaphore set_chan_sem; +// short hw_wep; + +// short digphy; +// short antb; +// short diversity; +// u8 cs_treshold; +// short rcr_csense; + short rf_chip; +// u32 key0[4]; + short (*rf_set_sens)(struct net_device *dev,short sens); + void (*rf_set_chan)(struct net_device *dev,short ch); + void (*rf_close)(struct net_device *dev); + void (*rf_init)(struct net_device *dev); + //short rate; + short promisc; + /*stats*/ + struct Stats stats; + struct _link_detect_t link_detect; //added on 1016.2008 + struct iw_statistics wstats; + struct proc_dir_entry *dir_dev; + + /*RX stuff*/ +// u32 *rxring; +// u32 *rxringtail; +// dma_addr_t rxringdma; + struct urb **rx_urb; +#ifdef THOMAS_BEACON + unsigned long *oldaddr; //lzm for 64bit CPU crash +#endif + +#ifdef THOMAS_TASKLET + atomic_t irt_counter;//count for irq_rx_tasklet +#endif +#ifdef JACKSON_NEW_RX + struct sk_buff **pp_rxskb; + int rx_inx; +#endif + + short tx_urb_index; + + //struct buffer *rxbuffer; + //struct buffer *rxbufferhead; + //int rxringcount; + //u16 rxbuffersize; + + //struct sk_buff *rx_skb; + + //short rx_skb_complete; + + //u32 rx_prevlen; + //atomic_t tx_lp_pending; + //atomic_t tx_np_pending; + atomic_t tx_pending[0x10];//UART_PRIORITY+1 + +#if 0 + /*TX stuff*/ + u32 *txlpring; + u32 *txhpring; + u32 *txnpring; + dma_addr_t txlpringdma; + dma_addr_t txhpringdma; + dma_addr_t txnpringdma; + u32 *txlpringtail; + u32 *txhpringtail; + u32 *txnpringtail; + u32 *txlpringhead; + u32 *txhpringhead; + u32 *txnpringhead; + struct buffer *txlpbufs; + struct buffer *txhpbufs; + struct buffer *txnpbufs; + struct buffer *txlpbufstail; + struct buffer *txhpbufstail; + struct buffer *txnpbufstail; + int txringcount; + int txbuffsize; + + //struct tx_pendingbuf txnp_pending; + struct tasklet_struct irq_tx_tasklet; +#endif + struct tasklet_struct irq_rx_tasklet; + struct urb *rxurb_task; +// u8 dma_poll_mask; + //short tx_suspend; + + /* adhoc/master mode stuff */ +#if 0 + u32 *txbeacontail; + dma_addr_t txbeaconringdma; + u32 *txbeaconring; + int txbeaconcount; +#endif +// struct ieee_tx_beacon *beacon_buf; + //char *master_essid; +// dma_addr_t beacondmabuf; + //u16 master_beaconinterval; +// u32 master_beaconsize; + //u16 beacon_interval; + + //2 Tx Related variables + u16 ShortRetryLimit; + u16 LongRetryLimit; + u32 TransmitConfig; + u8 RegCWinMin; // For turbo mode CW adaptive. Added by Annie, 2005-10-27. + + //2 Rx Related variables + u16 EarlyRxThreshold; + u32 ReceiveConfig; + u8 AcmControl; + + u8 RFProgType; + + u8 retry_data; + u8 retry_rts; + u16 rts; + +//by amy + long LastSignalStrengthInPercent; + long SignalStrength; + long SignalQuality; + u8 antenna_flag; + bool flag_beacon; +//by amy +//by amy for rate adaptive + struct timer_list rateadapter_timer; + u16 LastRetryCnt; + u16 LastRetryRate; + unsigned long LastTxokCnt; + unsigned long LastRxokCnt; + u16 CurrRetryCnt; + long RecvSignalPower; + unsigned long LastTxOKBytes; + u8 LastFailTxRate; + long LastFailTxRateSS; + u8 FailTxRateCount; + u32 LastTxThroughput; + unsigned long txokbytestotal; + //for up rate + unsigned short bTryuping; + u8 CurrTxRate; //the rate before up + u16 CurrRetryRate; + u16 TryupingCount; + u8 TryDownCountLowData; + u8 TryupingCountNoData; + + u8 CurrentOperaRate; +//by amy for rate adaptive +//by amy for power save + struct timer_list watch_dog_timer; + bool bInactivePs; + bool bSwRfProcessing; + RT_RF_POWER_STATE eInactivePowerState; + RT_RF_POWER_STATE eRFPowerState; + u32 RfOffReason; + bool RFChangeInProgress; + bool bInHctTest; + bool SetRFPowerStateInProgress; + //u8 RFProgType; + bool bLeisurePs; + RT_PS_MODE dot11PowerSaveMode; + u32 NumRxOkInPeriod; + u32 NumTxOkInPeriod; + u8 RegThreeWireMode; + bool ps_mode; +//by amy for power save +//by amy for DIG + bool bDigMechanism; + bool bCCKThMechanism; + u8 InitialGain; + u8 StageCCKTh; + u8 RegBModeGainStage; + u8 RegDigOfdmFaUpTh; //added by david, 2008.3.6 + u8 DIG_NumberFallbackVote; + u8 DIG_NumberUpgradeVote; + u16 CCKUpperTh; + u16 CCKLowerTh; + u32 FalseAlarmRegValue; //added by david, 2008.3.6 +//by amy for DIG +//{ added by david for high power, 2008.3.11 + int UndecoratedSmoothedSS; + bool bRegHighPowerMechanism; + bool bToUpdateTxPwr; + u8 Z2HiPwrUpperTh; + u8 Z2HiPwrLowerTh; + u8 Z2RSSIHiPwrUpperTh; + u8 Z2RSSIHiPwrLowerTh; + // Current CCK RSSI value to determine CCK high power, asked by SD3 DZ, by Bruce, 2007-04-12. + u8 CurCCKRSSI; + bool bCurCCKPkt; + u32 wMacRegRfPinsOutput; + u32 wMacRegRfPinsSelect; + TR_SWITCH_STATE TrSwitchState; +//} +//{added by david for radio on/off + u8 radion; +//} + struct ChnlAccessSetting ChannelAccessSetting; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct work_struct reset_wq; +#else + struct tq_struct reset_wq; +#endif + +#ifdef _RTL8187_EXT_PATCH_ + struct mshclass *mshobj; +#endif + +#ifdef LED + /* add for led controll */ + u8 EEPROMCustomerID; + RT_CID_TYPE CustomerID; + LED_8187 Gpio0Led; + LED_8187 SwLed0; + LED_8187 SwLed1; + u8 bEnableLedCtrl; + LED_STRATEGY_8187 LedStrategy; + u8 PsrValue; + struct work_struct Gpio0LedWorkItem; + struct work_struct SwLed0WorkItem; + struct work_struct SwLed1WorkItem; +#endif + u8 driver_upping; +#ifdef CPU_64BIT + u8 *usb_buf; + struct dma_pool *usb_pool; +#endif + + +#ifdef SW_ANTE_DIVERSITY + +//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +// struct delayed_work SwAntennaWorkItem; +//#else +// struct work_struct SwAntennaWorkItem; +//#endif + + bool bAntennaDiversityTimerIssued; + short antb; + short diversity; + bool AutoloadFailFlag; + u16 EEPROMVersion; + u8 EEPROMAntennaDiversity; + u16 EEPROMCSThreshold; + u8 EEPROMDefaultAntennaB; + u8 EEPROMDigitalPhy; + u32 EEPROMCSMethod; + u8 EEPROMGEPRFOffState; + // For HW antenna diversity, added by Roger, 2008.01.30. + u32 AdMainAntennaRxOkCnt; // Main antenna Rx OK count. + u32 AdAuxAntennaRxOkCnt; // Aux antenna Rx OK count. + bool bHWAdSwitched; // TRUE if we has switched default antenna by HW evaluation. + u8 EEPROMSwAntennaDiversity; + bool EEPROMDefaultAntenna1; + u8 RegSwAntennaDiversityMechanism;// 0:default from EEPROM, 1: disable, 2: enable. + bool bSwAntennaDiverity; + u8 RegDefaultAntenna;// 0: default from EEPROM, 1: main, 2: aux. Added by Roger, 2007.11.05. + bool bDefaultAntenna1; + //long SignalStrength; + long Stats_SignalStrength; + //long LastSignalStrengthInPercent; // In percentange, used for smoothing, e.g. Moving Average. + //long SignalQuality; // in 0-100 index. + long Stats_SignalQuality; + //long RecvSignalPower; // in dBm. + long Stats_RecvSignalPower; + u8 LastRxPktAntenna; // +by amy 080312 Antenn which received the lasted packet. 0: Aux, 1:Main. Added by Roger, 2008.01.25. + u32 AdRxOkCnt; + long AdRxSignalStrength; // Rx signal strength for Antenna Diversity, which had been smoothing, its valid range is [0,100]. + u8 CurrAntennaIndex; // Index to current Antenna (both Tx and Rx). + u8 AdTickCount; // Times of SwAntennaDiversityTimer happened. + u8 AdCheckPeriod; // # of period SwAntennaDiversityTimer to check Rx signal strength for SW Antenna Diversity. + u8 AdMinCheckPeriod; // Min value of AdCheckPeriod. + u8 AdMaxCheckPeriod; // Max value of AdCheckPeriod. + long AdRxSsThreshold; // Signal strength threshold to switch antenna. + long AdMaxRxSsThreshold; // Max value of AdRxSsThreshold. + bool bAdSwitchedChecking; // TRUE if we shall shall check Rx signal strength for last time switching antenna. + long AdRxSsBeforeSwitched; // Rx signal strength before we swithed antenna. + struct timer_list SwAntennaDiversityTimer; +#endif + u8 commit; + +//#ifdef ENABLE_DOT11D + u8 channel_plan; +//#endif + u8 EEPROMSelectNewGPIO; +}r8180_priv; + +// for rtl8187 +// now mirging to rtl8187B +/* +typedef enum{ + LOW_PRIORITY = 0x02, + NORM_PRIORITY + } priority_t; +*/ +//for rtl8187B +typedef enum{ + BULK_PRIORITY = 0x01, + //RSVD0, + //RSVD1, + LOW_PRIORITY, + NORM_PRIORITY, + VO_PRIORITY, + VI_PRIORITY, //0x05 + BE_PRIORITY, + BK_PRIORITY, + RSVD2, + RSVD3, + BEACON_PRIORITY, //0x0A + HIGH_PRIORITY, + MANAGE_PRIORITY, + RSVD4, + RSVD5, + UART_PRIORITY //0x0F +} priority_t; + +typedef enum{ + NIC_8187 = 1, + NIC_8187B + } nic_t; + + +typedef u32 AC_CODING; +#define AC0_BE 0 // ACI: 0x00 // Best Effort +#define AC1_BK 1 // ACI: 0x01 // Background +#define AC2_VI 2 // ACI: 0x10 // Video +#define AC3_VO 3 // ACI: 0x11 // Voice +#define AC_MAX 4 // Max: define total number; Should not to be used as a real enum. + +// +// ECWmin/ECWmax field. +// Ref: WMM spec 2.2.2: WME Parameter Element, p.13. +// +typedef union _ECW{ + u8 charData; + struct + { + u8 ECWmin:4; + u8 ECWmax:4; + }f; // Field +}ECW, *PECW; + +// +// ACI/AIFSN Field. +// Ref: WMM spec 2.2.2: WME Parameter Element, p.12. +// +typedef union _ACI_AIFSN{ + u8 charData; + + struct + { + u8 AIFSN:4; + u8 ACM:1; + u8 ACI:2; + u8 Reserved:1; + }f; // Field +}ACI_AIFSN, *PACI_AIFSN; + +// +// AC Parameters Record Format. +// Ref: WMM spec 2.2.2: WME Parameter Element, p.12. +// +typedef union _AC_PARAM{ + u32 longData; + u8 charData[4]; + + struct + { + ACI_AIFSN AciAifsn; + ECW Ecw; + u16 TXOPLimit; + }f; // Field +}AC_PARAM, *PAC_PARAM; + +#ifdef JOHN_HWSEC +struct ssid_thread { + struct net_device *dev; + u8 name[IW_ESSID_MAX_SIZE + 1]; +}; +#endif + +short rtl8180_tx(struct net_device *dev,u32* skbuf, int len,priority_t priority,short morefrag,short rate); + +#ifdef JOHN_TKIP +u32 read_cam(struct net_device *dev, u8 addr); +void write_cam(struct net_device *dev, u8 addr, u32 data); +#endif +u8 read_nic_byte(struct net_device *dev, int x); +u8 read_nic_byte_E(struct net_device *dev, int x); +u32 read_nic_dword(struct net_device *dev, int x); +u16 read_nic_word(struct net_device *dev, int x) ; +void write_nic_byte(struct net_device *dev, int x,u8 y); +void write_nic_byte_E(struct net_device *dev, int x,u8 y); +void write_nic_word(struct net_device *dev, int x,u16 y); +void write_nic_dword(struct net_device *dev, int x,u32 y); +void force_pci_posting(struct net_device *dev); + +void rtl8180_rtx_disable(struct net_device *); +void rtl8180_rx_enable(struct net_device *); +void rtl8180_tx_enable(struct net_device *); + +void rtl8180_disassociate(struct net_device *dev); +//void fix_rx_fifo(struct net_device *dev); +void rtl8185_set_rf_pins_enable(struct net_device *dev,u32 a); + +void rtl8180_set_anaparam(struct net_device *dev,u32 a); +void rtl8185_set_anaparam2(struct net_device *dev,u32 a); +void rtl8180_update_msr(struct net_device *dev); +int rtl8180_down(struct net_device *dev); +int rtl8180_up(struct net_device *dev); +void rtl8180_commit(struct net_device *dev); +void rtl8180_set_chan(struct net_device *dev,short ch); +void write_phy(struct net_device *dev, u8 adr, u8 data); +void write_phy_cck(struct net_device *dev, u8 adr, u32 data); +void write_phy_ofdm(struct net_device *dev, u8 adr, u32 data); +void rtl8185_tx_antenna(struct net_device *dev, u8 ant); +void rtl8187_set_rxconf(struct net_device *dev); +bool MgntActSet_RF_State(struct net_device *dev,RT_RF_POWER_STATE StateToSet,u32 ChangeSource); +void IPSEnter(struct net_device *dev); +void IPSLeave(struct net_device *dev); +int r8187b_rfkill_init(struct net_device *dev); +void r8187b_rfkill_exit(void); +int r8187b_wifi_report_state(r8180_priv *priv); +void r8187b_wifi_change_rfkill_state(struct net_device *dev, RT_RF_POWER_STATE eRfPowerStateToSet); +bool SetRFPowerState(struct net_device *dev,RT_RF_POWER_STATE eRFPowerState); +void rtl8180_patch_ieee80211_wx_sync_scan_wq(struct ieee80211_device *ieee); +#ifdef _RTL8187_EXT_PATCH_ +extern int r8180_wx_set_channel(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +#endif +#ifdef JOHN_TKIP +void EnableHWSecurityConfig8187(struct net_device *dev); +void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType, u8 *MacAddr, u8 DefaultKey, u32 *KeyContent ); + +#endif + +#endif diff --git a/drivers/net/wireless/rtl8187b/r8187_core.c b/drivers/net/wireless/rtl8187b/r8187_core.c new file mode 100644 index 0000000..e3ce75b --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8187_core.c @@ -0,0 +1,7047 @@ +/* + This is part of rtl8187 OpenSource driver - v 0.1 + Copyright (C) Andrea Merello 2005 + Released under the terms of GPL (General Public License) + + + Parts of this driver are based on the rtl8180 driver skeleton + from Patric Schenke & Andres Salomon. + + Parts of this driver are based on the Intel Pro Wireless 2*00 GPL drivers. + + some ideas might be derived from David Young rtl8180 netbsd driver. + + Parts of the usb code are from the r8150.c driver in linux kernel + + Some ideas borrowed from the 8139too.c driver included in linux kernel. + + We (I?) want to thanks the Authors of those projecs and also the + Ndiswrapper's project Authors. + + A special big thanks goes also to Realtek corp. for their help in my + attempt to add RTL8187 and RTL8225 support, and to David Young also. + + - Please note that this file is a modified version from rtl8180-sa2400 + drv. So some other people have contributed to this project, and they are + thanked in the rtl8180-sa2400 CHANGELOG. +*/ + +#undef LOOP_TEST +#undef DUMP_RX +#undef DUMP_TX +#undef DEBUG_TX_DESC2 +#undef RX_DONT_PASS_UL +#undef DEBUG_EPROM +#undef DEBUG_RX_VERBOSE +#undef DUMMY_RX +#undef DEBUG_ZERO_RX +#undef DEBUG_RX_SKB +#undef DEBUG_TX_FRAG +#undef DEBUG_RX_FRAG +#undef DEBUG_TX_FILLDESC +#undef DEBUG_TX +#undef DEBUG_IRQ +#undef DEBUG_RX +#undef DEBUG_RXALLOC +#undef DEBUG_REGISTERS +#undef DEBUG_RING +#undef DEBUG_IRQ_TASKLET +#undef DEBUG_TX_ALLOC +#undef DEBUG_TX_DESC +#undef CONFIG_SOFT_BEACON +#undef DEBUG_RW_REGISTER + +#define CONFIG_RTL8180_IO_MAP +//#define CONFIG_SOFT_BEACON +//#define DEBUG_RW_REGISTER + +#include "r8180_hw.h" +#include "r8187.h" +#include "r8180_rtl8225.h" /* RTL8225 Radio frontend */ +#include "r8180_93cx6.h" /* Card EEPROM */ +#include "r8180_wx.h" +#include "r8180_dm.h" + +#include + +#include +// FIXME: check if 2.6.7 is ok +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)) +#define usb_kill_urb usb_unlink_urb +#endif + +#ifdef CONFIG_RTL8180_PM +#include "r8180_pm.h" +#endif + +#ifdef ENABLE_DOT11D +#include "dot11d.h" +#endif + +#ifndef USB_VENDOR_ID_REALTEK +#define USB_VENDOR_ID_REALTEK 0x0bda +#endif +#ifndef USB_VENDOR_ID_NETGEAR +#define USB_VENDOR_ID_NETGEAR 0x0846 +#endif + +#define TXISR_SELECT(priority) ((priority == MANAGE_PRIORITY)?rtl8187_managetx_isr:\ + (priority == BEACON_PRIORITY)?rtl8187_beacontx_isr: \ + (priority == VO_PRIORITY)?rtl8187_votx_isr: \ + (priority == VI_PRIORITY)?rtl8187_vitx_isr:\ + (priority == BE_PRIORITY)?rtl8187_betx_isr:rtl8187_bktx_isr) + +static struct usb_device_id rtl8187_usb_id_tbl[] = { + {USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8187)}, + {USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8189)}, +// {USB_DEVICE_VER(USB_VENDOR_ID_REALTEK, 0x8187,0x0200,0x0200)}, + {USB_DEVICE(USB_VENDOR_ID_NETGEAR, 0x6100)}, + {USB_DEVICE(USB_VENDOR_ID_NETGEAR, 0x6a00)}, + {USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8197)}, + {USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8198)}, + {} +}; + +static char* ifname = "wlan%d"; +#if 0 +static int hwseqnum = 0; +static int hwwep = 0; +#endif +static int channels = 0x3fff; +//static int channels = 0x7ff;// change by thomas, use 1 - 11 channel 0907-2007 +#define eqMacAddr(a,b) ( ((a)[0]==(b)[0] && (a)[1]==(b)[1] && (a)[2]==(b)[2] && (a)[3]==(b)[3] && (a)[4]==(b)[4] && (a)[5]==(b)[5]) ? 1:0 ) +//by amy for rate adaptive +#define DEFAULT_RATE_ADAPTIVE_TIMER_PERIOD 300 +//by amy for rate adaptive +//by amy for ps +#define IEEE80211_WATCH_DOG_TIME 2000 +//by amy for ps +MODULE_LICENSE("GPL"); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +MODULE_VERSION("V 1.1"); +#endif +MODULE_DEVICE_TABLE(usb, rtl8187_usb_id_tbl); +MODULE_AUTHOR("Realsil Wlan"); +MODULE_DESCRIPTION("Linux driver for Realtek RTL8187 WiFi cards"); + +#if 0 +MODULE_PARM(ifname,"s"); +MODULE_PARM_DESC(devname," Net interface name, wlan%d=default"); + +MODULE_PARM(hwseqnum,"i"); +MODULE_PARM_DESC(hwseqnum," Try to use hardware 802.11 header sequence numbers. Zero=default"); + +MODULE_PARM(hwwep,"i"); +MODULE_PARM_DESC(hwwep," Try to use hardware WEP support. Still broken and not available on all cards"); + +MODULE_PARM(channels,"i"); +MODULE_PARM_DESC(channels," Channel bitmask for specific locales. NYI"); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) +module_param(ifname, charp, S_IRUGO|S_IWUSR ); +//module_param(hwseqnum,int, S_IRUGO|S_IWUSR); +//module_param(hwwep,int, S_IRUGO|S_IWUSR); +module_param(channels,int, S_IRUGO|S_IWUSR); +#else +MODULE_PARM(ifname, "s"); +//MODULE_PARM(hwseqnum,"i"); +//MODULE_PARM(hwwep,"i"); +MODULE_PARM(channels,"i"); +#endif + +MODULE_PARM_DESC(devname," Net interface name, wlan%d=default"); +//MODULE_PARM_DESC(hwseqnum," Try to use hardware 802.11 header sequence numbers. Zero=default"); +//MODULE_PARM_DESC(hwwep," Try to use hardware WEP support. Still broken and not available on all cards"); +MODULE_PARM_DESC(channels," Channel bitmask for specific locales. NYI"); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static int __devinit rtl8187_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void __devexit rtl8187_usb_disconnect(struct usb_interface *intf); +#else +static void *__devinit rtl8187_usb_probe(struct usb_device *udev,unsigned int ifnum, + const struct usb_device_id *id); +static void __devexit rtl8187_usb_disconnect(struct usb_device *udev, void *ptr); +#endif + + +static struct usb_driver rtl8187_usb_driver = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) + .owner = THIS_MODULE, +#endif + .name = RTL8187_MODULE_NAME, /* Driver name */ + .id_table = rtl8187_usb_id_tbl, /* PCI_ID table */ + .probe = rtl8187_usb_probe, /* probe fn */ + .disconnect = rtl8187_usb_disconnect, /* remove fn */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0) +#ifdef CONFIG_RTL8180_PM + .suspend = rtl8187_suspend, /* PM suspend fn */ + .resume = rtl8187_resume, /* PM resume fn */ + .reset_resume = rtl8187_resume, /* PM resume fn */ +#else + .suspend = NULL, /* PM suspend fn */ + .resume = NULL, /* PM resume fn */ +#endif +#endif +}; + +#ifdef JOHN_HWSEC +void CAM_mark_invalid(struct net_device *dev, u8 ucIndex) +{ + u32 ulContent=0; + u32 ulCommand=0; + u32 ulEncAlgo=CAM_AES; + + // keyid must be set in config field + ulContent |= (ucIndex&3) | ((u16)(ulEncAlgo)<<2); + + ulContent |= BIT15; + // polling bit, and No Write enable, and address + ulCommand= CAM_CONTENT_COUNT*ucIndex; + ulCommand= ulCommand | BIT31|BIT16; + // write content 0 is equall to mark invalid + + write_nic_dword(dev, WCAMI, ulContent); //delay_ms(40); + //RT_TRACE(COMP_SEC, DBG_LOUD, ("CAM_mark_invalid(): WRITE A4: %x \n",ulContent)); + write_nic_dword(dev, RWCAM, ulCommand); //delay_ms(40); + //RT_TRACE(COMP_SEC, DBG_LOUD, ("CAM_mark_invalid(): WRITE A0: %x \n",ulCommand)); +} + +void CAM_empty_entry(struct net_device *dev, u8 ucIndex) +{ + u32 ulCommand=0; + u32 ulContent=0; + u8 i; + u32 ulEncAlgo=CAM_AES; + + for(i=0;i<6;i++) + { + + // filled id in CAM config 2 byte + if( i == 0) + { + ulContent |=(ucIndex & 0x03) | (ulEncAlgo<<2); + ulContent |= BIT15; + + } + else + { + ulContent = 0; + } + // polling bit, and No Write enable, and address + ulCommand= CAM_CONTENT_COUNT*ucIndex+i; + ulCommand= ulCommand | BIT31|BIT16; + // write content 0 is equall to mark invalid + write_nic_dword(dev, WCAMI, ulContent); //delay_ms(40); + //RT_TRACE(COMP_SEC, DBG_LOUD, ("CAM_empty_entry(): WRITE A4: %x \n",ulContent)); + write_nic_dword(dev, RWCAM, ulCommand); //delay_ms(40); + //RT_TRACE(COMP_SEC, DBG_LOUD, ("CAM_empty_entry(): WRITE A0: %x \n",ulCommand)); + } +} + +void CamResetAllEntry(struct net_device *dev) +{ + u8 ucIndex; + + //2004/02/11 In static WEP, OID_ADD_KEY or OID_ADD_WEP are set before STA associate to AP. + // However, ResetKey is called on OID_802_11_INFRASTRUCTURE_MODE and MlmeAssociateRequest + // In this condition, Cam can not be reset because upper layer will not set this static key again. + //if(Adapter->EncAlgorithm == WEP_Encryption) + // return; + //debug + //DbgPrint("========================================\n"); + //DbgPrint(" Call ResetAllEntry \n"); + //DbgPrint("========================================\n\n"); + + for(ucIndex=0;ucIndexwrite_read_register_index % 200) ; + + priv->write_read_registers[reg_index].address = 0; + priv->write_read_registers[reg_index].content = 0; + priv->write_read_registers[reg_index].flag = 0; + + priv->write_read_registers[reg_index].address = addr; + priv->write_read_registers[reg_index].content = cont; + priv->write_read_registers[reg_index].flag = flag; + + priv->write_read_register_index = (priv->write_read_register_index + 1) % 200; +} + +bool print_once = 0; + +void print_rw_registers(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + int reg_index = 0; + int watchdog = 0; + if(print_once == false) + { + print_once = true; + for(reg_index = ((priv->write_read_register_index + 1) % 200); watchdog <= 199; reg_index++) + { + watchdog++; + printk("====>reg_addr:0x%x, reg_cont:0x%x, read_or_write:0x%d\n", + priv->write_read_registers[reg_index].address, + priv->write_read_registers[reg_index].content, + priv->write_read_registers[reg_index].flag); + } + } +} +//lzm add for write time out test +#endif + +#ifdef CPU_64BIT +void write_nic_byte_E(struct net_device *dev, int indx, u8 data) +{ + int status; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + indx|0xfe00, 0, &data, 1, HZ / 2); + +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, data, 2); +#endif + + if (status < 0) + { + printk("write_nic_byte_E TimeOut!addr:%x, status:%x\n", indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } +} + +u8 read_nic_byte_E(struct net_device *dev, int indx) +{ + int status; + u8 data, *buf; + dma_addr_t dma_handle; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + buf = dma_pool_alloc(priv->usb_pool, GFP_ATOMIC, &dma_handle); + if (!buf) { + printk("read_nic_byte_E out of memory\n"); + return -ENOMEM; + } + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + indx|0xfe00, 0, buf, 1, HZ / 2); +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, buf[0], 1); +#endif + + if (status < 0) + { + printk("read_nic_byte_E TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + + data = buf[0]; + dma_pool_free(priv->usb_pool, buf, dma_handle); + return data; +} + +void write_nic_byte(struct net_device *dev, int indx, u8 data) +{ + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + (indx&0xff)|0xff00, (indx>>8)&0x03, &data, 1, HZ / 2); + +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, data, 2); +#endif + if (status < 0) + { + printk("write_nic_byte TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + + +} + +void write_nic_word(struct net_device *dev, int indx, u16 data) +{ + + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + (indx&0xff)|0xff00, (indx>>8)&0x03, &data, 2, HZ / 2); + +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, data, 2); + + if(priv->write_read_register_index == 199) + { + //print_rw_registers(dev); + } +#endif + if (status < 0) + { + printk("write_nic_word TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + +} + +void write_nic_dword(struct net_device *dev, int indx, u32 data) +{ + + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + (indx&0xff)|0xff00, (indx>>8)&0x03, &data, 4, HZ / 2); +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, data, 2); +#endif + + + if (status < 0) + { + printk("write_nic_dword TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + +} + + u8 read_nic_byte(struct net_device *dev, int indx) +{ + u8 data, *buf; + int status; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + dma_addr_t dma_handle; + + buf = dma_pool_alloc(priv->usb_pool, GFP_ATOMIC, &dma_handle); + if (!buf) { + printk("read_nic_byte: out of memory\n"); + return -ENOMEM; + } + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + (indx&0xff)|0xff00, (indx>>8)&0x03, buf, 1, HZ / 2); +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, buf[0], 1); +#endif + + if (status < 0) + { + printk("read_nic_byte TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + + data = buf[0]; + dma_pool_free(priv->usb_pool, buf, dma_handle); + return data; +} + +u16 read_nic_word(struct net_device *dev, int indx) +{ + u16 data, *buf; + int status; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + dma_addr_t dma_handle; + + buf = dma_pool_alloc(priv->usb_pool, GFP_ATOMIC, &dma_handle); + if (!buf) { + printk("read_nic_word: out of memory\n"); + return -ENOMEM; + } + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + (indx&0xff)|0xff00, (indx>>8)&0x03, buf, 2, HZ / 2); +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, buf[0], 1); +#endif + + if (status < 0) + { + printk("read_nic_word TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + + + data = buf[0]; + dma_pool_free(priv->usb_pool, buf, dma_handle); + return data; +} + +u32 read_nic_dword(struct net_device *dev, int indx) +{ + u32 data, *buf; + int status; + dma_addr_t dma_handle; +// int result; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + buf = dma_pool_alloc(priv->usb_pool, GFP_ATOMIC, &dma_handle); + if (!buf){ + printk("read_nic_dword: out of memory\n"); + return -ENOMEM; + } + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + (indx&0xff)|0xff00, (indx>>8)&0x03, buf, 4, HZ / 2); +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + add_into_rw_registers(dev, indx, buf[0], 1); +#endif + + if (status < 0) + { + printk("read_nic_dword TimeOut!addr:%x, status:%x\n",indx, status); +#ifdef DEBUG_RW_REGISTER + print_rw_registers(dev); +#endif + } + + + + data = buf[0]; + dma_pool_free(priv->usb_pool, buf, dma_handle); + return data; +} +#else +void write_nic_byte_E(struct net_device *dev, int indx, u8 data) +{ + int status; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + indx|0xfe00, 0, &data, 1, HZ / 2); + + if (status < 0) + { + printk("write_nic_byte_E TimeOut!addr:0x%x,val:0x%x, status:%x\n", indx,data,status); + } +} + +u8 read_nic_byte_E(struct net_device *dev, int indx) +{ + int status; + u8 data = 0; + u8 buf[64]; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + indx|0xfe00, 0, buf, 1, HZ / 2); + + if (status < 0) + { + printk("read_nic_byte_E TimeOut!addr:0x%x, status:%x\n", indx, status); + } + + data = *(u8*)buf; + return data; +} + +void write_nic_byte(struct net_device *dev, int indx, u8 data) +{ + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + (indx&0xff)|0xff00, (indx>>8)&0x03, &data, 1, HZ / 2); + + if (status < 0) + { + printk("write_nic_byte TimeOut!addr:0x%x,val:0x%x, status:%x\n", indx,data, status); + } + + +} + +void write_nic_word(struct net_device *dev, int indx, u16 data) +{ + + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + (indx&0xff)|0xff00, (indx>>8)&0x03, &data, 2, HZ / 2); + + if (status < 0) + { + printk("write_nic_word TimeOut!addr:0x%x,val:0x%x, status:%x\n", indx,data, status); + } + +} + +void write_nic_dword(struct net_device *dev, int indx, u32 data) +{ + + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE, + (indx&0xff)|0xff00, (indx>>8)&0x03, &data, 4, HZ / 2); + + + if (status < 0) + { + printk("write_nic_dword TimeOut!addr:0x%x,val:0x%x, status:%x\n", indx,data, status); + } + +} + +u8 read_nic_byte(struct net_device *dev, int indx) +{ + u8 data = 0; + u8 buf[64]; + int status; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + (indx&0xff)|0xff00, (indx>>8)&0x03, buf, 1, HZ / 2); + + if (status < 0) + { + printk("read_nic_byte TimeOut!addr:0x%x,status:%x\n", indx,status); + } + + + data = *(u8*)buf; + return data; +} + +u16 read_nic_word(struct net_device *dev, int indx) +{ + u16 data = 0; + u8 buf[64]; + int status; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + (indx&0xff)|0xff00, (indx>>8)&0x03, buf, 2, HZ / 2); + + if (status < 0) + { + printk("read_nic_word TimeOut!addr:0x%x,status:%x\n", indx,status); + } + + data = *(u16*)buf; + return data; +} + +u32 read_nic_dword(struct net_device *dev, int indx) +{ + u32 data = 0; + u8 buf[64]; + int status; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct usb_device *udev = priv->udev; + + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8187_REQ_GET_REGS, RTL8187_REQT_READ, + (indx&0xff)|0xff00, (indx>>8)&0x03, buf, 4, HZ / 2); + + if (status < 0) + { + printk("read_nic_dword TimeOut!addr:0x%x,status:%x\n", indx, status); + } + + + data = *(u32*)buf; + return data; +} +#endif + + +u8 read_phy_cck(struct net_device *dev, u8 adr); +u8 read_phy_ofdm(struct net_device *dev, u8 adr); +/* this might still called in what was the PHY rtl8185/rtl8187 common code + * plans are to possibilty turn it again in one common code... + */ +inline void force_pci_posting(struct net_device *dev) +{ +} + + +static struct net_device_stats *rtl8180_stats(struct net_device *dev); +void rtl8180_commit(struct net_device *dev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_restart(struct work_struct *work); +#else +void rtl8180_restart(struct net_device *dev); +#endif +/**************************************************************************** + -----------------------------PROCFS STUFF------------------------- +*****************************************************************************/ + +static struct proc_dir_entry *rtl8180_proc = NULL; +static int proc_get_stats_ap(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + struct ieee80211_network *target; + + int len = 0; + + list_for_each_entry(target, &ieee->network_list, list) { + + len += snprintf(page + len, count - len, + "%s ", target->ssid); + len += snprintf(page + len, count - len, + "%ld ", (jiffies-target->last_scanned)/HZ); + + + + if(target->wpa_ie_len>0 || target->rsn_ie_len>0){ + len += snprintf(page + len, count - len, + "WPA\n"); + } + else{ + len += snprintf(page + len, count - len, + "non_WPA\n"); + } + + } + + *eof = 1; + return len; +} + +static int proc_get_registers(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + + int len = 0; + int i,n; + + int max=0xff; + + /* This dump the current register page */ +len += snprintf(page + len, count - len, + "\n####################page 0##################\n "); + + for(n=0;n<=max;) + { + //printk( "\nD: %2x> ", n); + len += snprintf(page + len, count - len, + "\nD: %2x > ",n); + + for(i=0;i<16 && n<=max;i++,n++) + len += snprintf(page + len, count - len, + "%2x ",read_nic_byte(dev,n)); + + // printk("%2x ",read_nic_byte(dev,n)); + } + len += snprintf(page + len, count - len,"\n"); +len += snprintf(page + len, count - len, + "\n####################page 1##################\n "); + for(n=0;n<=max;) + { + //printk( "\nD: %2x> ", n); + len += snprintf(page + len, count - len, + "\nD: %2x > ",n); + + for(i=0;i<16 && n<=max;i++,n++) + len += snprintf(page + len, count - len, + "%2x ",read_nic_byte(dev,0x100|n)); + + // printk("%2x ",read_nic_byte(dev,n)); + } +len += snprintf(page + len, count - len, + "\n####################page 2##################\n "); + for(n=0;n<=max;) + { + //printk( "\nD: %2x> ", n); + len += snprintf(page + len, count - len, + "\nD: %2x > ",n); + + for(i=0;i<16 && n<=max;i++,n++) + len += snprintf(page + len, count - len, + "%2x ",read_nic_byte(dev,0x200|n)); + + // printk("%2x ",read_nic_byte(dev,n)); + } + + + + *eof = 1; + return len; + +} + + +static int proc_get_cck_reg(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + + int len = 0; + int i,n; + + int max = 0x5F; + + /* This dump the current register page */ + for(n=0;n<=max;) + { + //printk( "\nD: %2x> ", n); + len += snprintf(page + len, count - len, + "\nD: %2x > ",n); + + for(i=0;i<16 && n<=max;i++,n++) + len += snprintf(page + len, count - len, + "%2x ",read_phy_cck(dev,n)); + + // printk("%2x ",read_nic_byte(dev,n)); + } + len += snprintf(page + len, count - len,"\n"); + + + *eof = 1; + return len; + +} + + +static int proc_get_ofdm_reg(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + + int len = 0; + int i,n; + + //int max=0xff; + int max = 0x40; + + /* This dump the current register page */ + for(n=0;n<=max;) + { + //printk( "\nD: %2x> ", n); + len += snprintf(page + len, count - len, + "\nD: %2x > ",n); + + for(i=0;i<16 && n<=max;i++,n++) + len += snprintf(page + len, count - len, + "%2x ",read_phy_ofdm(dev,n)); + + // printk("%2x ",read_nic_byte(dev,n)); + } + len += snprintf(page + len, count - len,"\n"); + + + + *eof = 1; + return len; + +} + + +#if 0 +static int proc_get_stats_hw(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + int len = 0; + + len += snprintf(page + len, count - len, + "NIC int: %lu\n" + "Total int: %lu\n", + priv->stats.ints, + priv->stats.shints); + + *eof = 1; + return len; +} +#endif + +static int proc_get_stats_tx(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + int len = 0; + + len += snprintf(page + len, count - len, + "TX VI priority ok int: %lu\n" + "TX VI priority error int: %lu\n" + "TX VO priority ok int: %lu\n" + "TX VO priority error int: %lu\n" + "TX BE priority ok int: %lu\n" + "TX BE priority error int: %lu\n" + "TX BK priority ok int: %lu\n" + "TX BK priority error int: %lu\n" + "TX MANAGE priority ok int: %lu\n" + "TX MANAGE priority error int: %lu\n" + "TX BEACON priority ok int: %lu\n" + "TX BEACON priority error int: %lu\n" +// "TX high priority ok int: %lu\n" +// "TX high priority failed error int: %lu\n" + "TX queue resume: %lu\n" + "TX queue stopped?: %d\n" + "TX fifo overflow: %lu\n" +// "TX beacon: %lu\n" + "TX VI queue: %d\n" + "TX VO queue: %d\n" + "TX BE queue: %d\n" + "TX BK queue: %d\n" + "TX BEACON queue: %d\n" + "TX MANAGE queue: %d\n" +// "TX HW queue: %d\n" + "TX VI dropped: %lu\n" + "TX VO dropped: %lu\n" + "TX BE dropped: %lu\n" + "TX BK dropped: %lu\n" + "TX total data packets %lu\n", +// "TX beacon aborted: %lu\n", + priv->stats.txviokint, + priv->stats.txvierr, + priv->stats.txvookint, + priv->stats.txvoerr, + priv->stats.txbeokint, + priv->stats.txbeerr, + priv->stats.txbkokint, + priv->stats.txbkerr, + priv->stats.txmanageokint, + priv->stats.txmanageerr, + priv->stats.txbeaconokint, + priv->stats.txbeaconerr, +// priv->stats.txhpokint, +// priv->stats.txhperr, + priv->stats.txresumed, + netif_queue_stopped(dev), + priv->stats.txoverflow, +// priv->stats.txbeacon, + atomic_read(&(priv->tx_pending[VI_PRIORITY])), + atomic_read(&(priv->tx_pending[VO_PRIORITY])), + atomic_read(&(priv->tx_pending[BE_PRIORITY])), + atomic_read(&(priv->tx_pending[BK_PRIORITY])), + atomic_read(&(priv->tx_pending[BEACON_PRIORITY])), + atomic_read(&(priv->tx_pending[MANAGE_PRIORITY])), +// read_nic_byte(dev, TXFIFOCOUNT), + priv->stats.txvidrop, + priv->stats.txvodrop, + priv->stats.txbedrop, + priv->stats.txbkdrop, + priv->stats.txdatapkt +// priv->stats.txbeaconerr + ); + + *eof = 1; + return len; +} + + + +static int proc_get_stats_rx(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + int len = 0; + + len += snprintf(page + len, count - len, + "RX packets: %lu\n" + "RX urb status error: %lu\n" + "RX invalid urb error: %lu\n", + priv->stats.rxok, + priv->stats.rxstaterr, + priv->stats.rxurberr); + + *eof = 1; + return len; +} + +#if WIRELESS_EXT < 17 +static struct iw_statistics *r8180_get_wireless_stats(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + return &priv->wstats; +} +#endif + +void rtl8180_proc_module_init(void) +{ + DMESG("Initializing proc filesystem"); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) + rtl8180_proc=create_proc_entry(RTL8187_MODULE_NAME, S_IFDIR, proc_net); +#else + rtl8180_proc=create_proc_entry(RTL8187_MODULE_NAME, S_IFDIR, init_net.proc_net); +#endif +} + + +void rtl8180_proc_module_remove(void) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) + remove_proc_entry(RTL8187_MODULE_NAME, proc_net); +#else + remove_proc_entry(RTL8187_MODULE_NAME, init_net.proc_net); +#endif +} + + +void rtl8180_proc_remove_one(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + if (priv->dir_dev) { + // remove_proc_entry("stats-hw", priv->dir_dev); + remove_proc_entry("stats-tx", priv->dir_dev); + remove_proc_entry("stats-rx", priv->dir_dev); + // remove_proc_entry("stats-ieee", priv->dir_dev); + remove_proc_entry("stats-ap", priv->dir_dev); + remove_proc_entry("registers", priv->dir_dev); + remove_proc_entry("cck-registers",priv->dir_dev); + remove_proc_entry("ofdm-registers",priv->dir_dev); + remove_proc_entry(dev->name, rtl8180_proc); + priv->dir_dev = NULL; + } +} + + +void rtl8180_proc_init_one(struct net_device *dev) +{ + struct proc_dir_entry *e; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + priv->dir_dev = create_proc_entry(dev->name, + S_IFDIR | S_IRUGO | S_IXUGO, + rtl8180_proc); + if (!priv->dir_dev) { + DMESGE("Unable to initialize /proc/net/rtl8187/%s\n", + dev->name); + return; + } + #if 0 + e = create_proc_read_entry("stats-hw", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_stats_hw, dev); + + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/stats-hw\n", + dev->name); + } + #endif + e = create_proc_read_entry("stats-rx", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_stats_rx, dev); + + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/stats-rx\n", + dev->name); + } + + + e = create_proc_read_entry("stats-tx", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_stats_tx, dev); + + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/stats-tx\n", + dev->name); + } + #if 0 + e = create_proc_read_entry("stats-ieee", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_stats_ieee, dev); + + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/stats-ieee\n", + dev->name); + } + + #endif + + e = create_proc_read_entry("stats-ap", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_stats_ap, dev); + + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/stats-ap\n", + dev->name); + } + + e = create_proc_read_entry("registers", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_registers, dev); + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/registers\n", + dev->name); + } + + e = create_proc_read_entry("cck-registers", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_cck_reg, dev); + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/cck-registers\n", + dev->name); + } + + e = create_proc_read_entry("ofdm-registers", S_IFREG | S_IRUGO, + priv->dir_dev, proc_get_ofdm_reg, dev); + if (!e) { + DMESGE("Unable to initialize " + "/proc/net/rtl8187/%s/ofdm-registers\n", + dev->name); + } + +#ifdef _RTL8187_EXT_PATCH_ + if( priv->mshobj && priv->mshobj->ext_patch_create_proc ) + priv->mshobj->ext_patch_create_proc(priv); +#endif + +} +/**************************************************************************** + -----------------------------MISC STUFF------------------------- +*****************************************************************************/ + +/* this is only for debugging */ +void print_buffer(u32 *buffer, int len) +{ + int i; + u8 *buf =(u8*)buffer; + + printk("ASCII BUFFER DUMP (len: %x):\n",len); + + for(i=0;itx_np_pending : &priv->tx_lp_pending); + int used = atomic_read(&priv->tx_pending[priority]); + + return (used < MAX_TX_URB); +} + +void tx_timeout(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + //rtl8180_commit(dev); + printk("@@@@ Transmit timeout at %ld, latency %ld\n", jiffies, + jiffies - dev->trans_start); + + printk("@@@@ netif_queue_stopped = %d\n", netif_queue_stopped(dev)); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + schedule_work(&priv->reset_wq); +#else + schedule_task(&priv->reset_wq); +#endif + //DMESG("TXTIMEOUT"); +} + + +/* this is only for debug */ +void dump_eprom(struct net_device *dev) +{ + int i; + for(i=0; i<63; i++) + DMESG("EEPROM addr %x : %x", i, eprom_read(dev,i)); +} + +/* this is only for debug */ +void rtl8180_dump_reg(struct net_device *dev) +{ + int i; + int n; + int max=0xff; + + DMESG("Dumping NIC register map"); + + for(n=0;n<=max;) + { + printk( "\nD: %2x> ", n); + for(i=0;i<16 && n<=max;i++,n++) + printk("%2x ",read_nic_byte(dev,n)); + } + printk("\n"); +} + +/**************************************************************************** + ------------------------------HW STUFF--------------------------- +*****************************************************************************/ + + +void rtl8180_irq_enable(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + //priv->irq_enabled = 1; + + //write_nic_word(dev,INTA_MASK,INTA_RXOK | INTA_RXDESCERR | INTA_RXOVERFLOW | + // INTA_TXOVERFLOW | INTA_HIPRIORITYDESCERR | INTA_HIPRIORITYDESCOK | + // INTA_NORMPRIORITYDESCERR | INTA_NORMPRIORITYDESCOK | + // INTA_LOWPRIORITYDESCERR | INTA_LOWPRIORITYDESCOK | INTA_TIMEOUT); + + write_nic_word(dev,INTA_MASK, priv->irq_mask); +} + + +void rtl8180_irq_disable(struct net_device *dev) +{ + write_nic_word(dev,INTA_MASK,0); + force_pci_posting(dev); +// priv->irq_enabled = 0; +} + + +void rtl8180_set_mode(struct net_device *dev,int mode) +{ + u8 ecmd; + ecmd=read_nic_byte(dev, EPROM_CMD); + ecmd=ecmd &~ EPROM_CMD_OPERATING_MODE_MASK; + ecmd=ecmd | (mode<ieee80211->state == IEEE80211_LINKED){ + + if (priv->ieee80211->iw_mode == IW_MODE_INFRA) + msr |= (MSR_LINK_MANAGED<ieee80211->iw_mode == IW_MODE_ADHOC) + msr |= (MSR_LINK_ADHOC<ieee80211->iw_mode == IW_MODE_MASTER) + msr |= (MSR_LINK_MASTER<chan=ch; + #if 0 + if(priv->ieee80211->iw_mode == IW_MODE_ADHOC || + priv->ieee80211->iw_mode == IW_MODE_MASTER){ + + priv->ieee80211->link_state = WLAN_LINK_ASSOCIATED; + priv->ieee80211->master_chan = ch; + rtl8180_update_beacon_ch(dev); + } + #endif + + /* this hack should avoid frame TX during channel setting*/ + tx = read_nic_dword(dev,TX_CONF); + tx &= ~TX_LOOPBACK_MASK; + +#ifndef LOOP_TEST + write_nic_dword(dev,TX_CONF, tx |( TX_LOOPBACK_MAC<rf_set_chan(dev,priv->chan); + //mdelay(10); //CPU occupany is too high. LZM 31/10/2008 + write_nic_dword(dev,TX_CONF,tx | (TX_LOOPBACK_NONE<udev, + usb_rcvbulkpipe(priv->udev,(NIC_8187 == priv->card_8187)?0x81:0x83), + rx_urb->transfer_buffer, + RX_URB_SIZE, + rtl8187_rx_isr, + dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + err = usb_submit_urb(rx_urb, GFP_ATOMIC); +#else + err = usb_submit_urb(rx_urb); +#endif + if(err && err != -EPERM){ + DMESGE("cannot submit RX command. URB_STATUS %x",rx_urb->status); + } +} + + +void rtl8187_rx_manage_urbsubmit(struct net_device *dev, struct urb* rx_urb) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + int err; +#ifdef THOMAS_BEACON + usb_fill_bulk_urb(rx_urb,priv->udev, + usb_rcvbulkpipe(priv->udev,0x09), + rx_urb->transfer_buffer, + rx_urb->transfer_buffer_length, + rtl8187_rx_manage_isr, dev); +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + err = usb_submit_urb(rx_urb, GFP_ATOMIC); +#else + err = usb_submit_urb(rx_urb); +#endif + if(err && err != -EPERM){ + DMESGE("cannot submit RX command. URB_STATUS %x",rx_urb->status); + } +} + + + +void rtl8187_rx_initiate(struct net_device *dev) +{ + int i; + unsigned long flags; + struct urb *purb; + + struct sk_buff *pskb; + + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + priv->tx_urb_index = 0; + + if ((!priv->rx_urb) || (!priv->pp_rxskb)) { + + DMESGE("Cannot intiate RX urb mechanism"); + return; + + } + + priv->rx_inx = 0; +#ifdef THOMAS_TASKLET + atomic_set(&priv->irt_counter,0); +#endif + for(i = 0;i < MAX_RX_URB; i++){ + + purb = priv->rx_urb[i] = usb_alloc_urb(0,GFP_KERNEL); + + if(!priv->rx_urb[i]) + goto destroy; + + pskb = priv->pp_rxskb[i] = dev_alloc_skb (RX_URB_SIZE); + + if (pskb == NULL) + goto destroy; + + purb->transfer_buffer_length = RX_URB_SIZE; + purb->transfer_buffer = pskb->data; + } + + spin_lock_irqsave(&priv->irq_lock,flags);//added by thomas + + for(i=0;irx_urb[i]); + + spin_unlock_irqrestore(&priv->irq_lock,flags);//added by thomas + + return; + +destroy: + + for(i = 0; i < MAX_RX_URB; i++) { + + purb = priv->rx_urb[i]; + + if (purb) + usb_free_urb(purb); + + pskb = priv->pp_rxskb[i]; + + if (pskb) + dev_kfree_skb_any(pskb); + + } + + return; +} + + +void rtl8187_rx_manage_initiate(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + if(!priv->rx_urb) + DMESGE("Cannot intiate RX urb mechanism"); + + rtl8187_rx_manage_urbsubmit(dev,priv->rx_urb[MAX_RX_URB]); + +} + + +void rtl8187_set_rxconf(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + u32 rxconf; + + rxconf=read_nic_dword(dev,RX_CONF); + rxconf = rxconf &~ MAC_FILTER_MASK; + rxconf = rxconf | (1<EEPROMCSMethod;//for antenna +#endif + + if (dev->flags & IFF_PROMISC) DMESG ("NIC in promisc mode"); + + if(priv->ieee80211->iw_mode == IW_MODE_MONITOR || \ + dev->flags & IFF_PROMISC){ + rxconf = rxconf | (1<ieee80211->iw_mode == IW_MODE_MASTER){ + rxconf = rxconf | (1<ieee80211->iw_mode == IW_MODE_MONITOR){ + rxconf = rxconf | (1<crcmon == 1 && priv->ieee80211->iw_mode == IW_MODE_MONITOR) + rxconf = rxconf | (1<card_8187) { + cmd=read_nic_byte(dev,CMD); + write_nic_byte(dev,CMD,cmd | (1<ReceiveConfig); + } +} + + +void rtl8180_tx_enable(struct net_device *dev) +{ + u8 cmd; + u8 byte; + u32 txconf = 0; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + if(NIC_8187B == priv->card_8187){ + write_nic_dword(dev, TCR, priv->TransmitConfig); + byte = read_nic_byte(dev, MSR); + byte |= MSR_LINK_ENEDCA; + write_nic_byte(dev, MSR, byte); +#ifdef LOOP_TEST + txconf= read_nic_dword(dev,TX_CONF); + txconf = txconf | (TX_LOOPBACK_MAC<retry_data<retry_rts<dma_poll_mask &=~(1<dma_poll_mask); + rtl8180_set_mode(dev,EPROM_CMD_NORMAL); +} + + +void rtl8180_ +_disable(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + priv->dma_poll_mask |= (1<dma_poll_mask); + rtl8180_set_mode(dev,EPROM_CMD_NORMAL); +} + +#endif + + +void rtl8180_rtx_disable(struct net_device *dev) +{ + u8 cmd; + int i; + struct r8180_priv *priv = ieee80211_priv(dev); + + cmd=read_nic_byte(dev,CMD); + write_nic_byte(dev, CMD, cmd &~ \ + ((1<rx_inx;//0 + i=0; + if(priv->rx_urb){ + while(irx_urb[index]){ + usb_kill_urb(priv->rx_urb[index]); + } + if( index == (MAX_RX_URB-1) ) + index=0; + else + index=index+1; + i++; + } + if(priv->rx_urb[MAX_RX_URB]) + usb_kill_urb(priv->rx_urb[MAX_RX_URB]); + } + } +#endif +} + + +int alloc_tx_beacon_desc_ring(struct net_device *dev, int count) +{ + #if 0 + int i; + u32 *tmp; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + priv->txbeaconring = (u32*)pci_alloc_consistent(priv->pdev, + sizeof(u32)*8*count, + &priv->txbeaconringdma); + if (!priv->txbeaconring) return -1; + for (tmp=priv->txbeaconring,i=0;itxbeaconringdma+((i+1)*8*4); + else + *(tmp+4) = (u32)priv->txbeaconringdma; + + tmp=tmp+8; + } + #endif + return 0; +} + +long NetgearSignalStrengthTranslate(long LastSS,long CurrSS) +{ + long RetSS; + + // Step 1. Scale mapping. + if(CurrSS >= 71 && CurrSS <= 100){ + RetSS = 90 + ((CurrSS - 70) / 3); + }else if(CurrSS >= 41 && CurrSS <= 70){ + RetSS = 78 + ((CurrSS - 40) / 3); + }else if(CurrSS >= 31 && CurrSS <= 40){ + RetSS = 66 + (CurrSS - 30); + }else if(CurrSS >= 21 && CurrSS <= 30){ + RetSS = 54 + (CurrSS - 20); + }else if(CurrSS >= 5 && CurrSS <= 20){ + RetSS = 42 + (((CurrSS - 5) * 2) / 3); + }else if(CurrSS == 4){ + RetSS = 36; + }else if(CurrSS == 3){ + RetSS = 27; + }else if(CurrSS == 2){ + RetSS = 18; + }else if(CurrSS == 1){ + RetSS = 9; + }else{ + RetSS = CurrSS; + } + //RT_TRACE(COMP_DBG, DBG_LOUD, ("##### After Mapping: LastSS: %d, CurrSS: %d, RetSS: %d\n", LastSS, CurrSS, RetSS)); + + // Step 2. Smoothing. + if(LastSS > 0){ + RetSS = ((LastSS * 5) + (RetSS)+ 5) / 6; + } + //RT_TRACE(COMP_DBG, DBG_LOUD, ("$$$$$ After Smoothing: LastSS: %d, CurrSS: %d, RetSS: %d\n", LastSS, CurrSS, RetSS)); + + return RetSS; +} + +extern long TranslateToDbm8187(u8 SignalStrengthIndex); // 0-100 index. +//long TranslateToDbm8187(u8 SignalStrengthIndex) // 0-100 index. +//{ + // long SignalPower; // in dBm. + + // Translate to dBm (x=0.5y-95). + // SignalPower = (long)((SignalStrengthIndex + 1) >> 1); + // SignalPower -= 95; + + // return SignalPower; +//} + + +void rtl8180_reset(struct net_device *dev) +{ + + struct r8180_priv *priv = ieee80211_priv(dev); + u8 cr; + int i; + + + /* make sure the analog power is on before + * reset, otherwise reset may fail + */ + if(NIC_8187 == priv->card_8187) { + rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_ON); + rtl8185_set_anaparam2(dev, RTL8225_ANAPARAM2_ON); + rtl8180_irq_disable(dev); + mdelay(200); + write_nic_byte_E(dev,0x18,0x10); + write_nic_byte_E(dev,0x18,0x11); + write_nic_byte_E(dev,0x18,0x00); + mdelay(200); + } + + + cr=read_nic_byte(dev,CMD); + cr = cr & 2; + cr = cr | (1<card_8187) { + + //printk("This is RTL8187 Reset procedure\n"); + rtl8180_set_mode(dev,EPROM_CMD_LOAD); + force_pci_posting(dev); + mdelay(200); + + /* after the eeprom load cycle, make sure we have + * correct anaparams + */ + rtl8180_set_anaparam(dev, RTL8225_ANAPARAM_ON); + rtl8185_set_anaparam2(dev, RTL8225_ANAPARAM2_ON); + } + else { + //printk("This is RTL8187B Reset procedure\n"); + //test pending bug, john 20070815 + //initialize tx_pending + for(i=0;i<0x10;i++) atomic_set(&(priv->tx_pending[i]), 0); + + } + +} + +inline u16 ieeerate2rtlrate(int rate) +{ + switch(rate){ + case 10: + return 0; + case 20: + return 1; + case 55: + return 2; + case 110: + return 3; + case 60: + return 4; + case 90: + return 5; + case 120: + return 6; + case 180: + return 7; + case 240: + return 8; + case 360: + return 9; + case 480: + return 10; + case 540: + return 11; + default: + return 3; + + } +} +static u16 rtl_rate[] = {10,20,55,110,60,90,120,180,240,360,480,540,720}; +inline u16 rtl8180_rate2rate(short rate) +{ + if (rate >12) return 10; + return rtl_rate[rate]; +} + +void rtl8180_irq_rx_tasklet(struct r8180_priv *priv); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_rx_isr(struct urb *rx_urb, struct pt_regs *regs) +#else +void rtl8187_rx_isr(struct urb* rx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)rx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + priv->rxurb_task = rx_urb; + + + //DMESGW("David: Rx tasklet start!"); + +#ifdef THOMAS_TASKLET + atomic_inc( &priv->irt_counter ); + + //if( likely(priv->irt_counter_head+1 != priv->irt_counter_tail) ){ + // priv->irt_counter_head = (priv->irt_counter_head+1)&0xffff ; + tasklet_schedule(&priv->irq_rx_tasklet); + //} else{ + //DMESG("error: priv->irt_counter_head is going to pass through priv->irt_counter_tail\n"); + /* + skb = priv->pp_rxskb[priv->rx_inx]; + dev_kfree_skb_any(skb); + + skb = dev_alloc_skb(RX_URB_SIZE); + if (skb == NULL) + panic("No Skb For RX!/n"); + + rx_urb->transfer_buffer = skb->data; + + priv->pp_rxskb[priv->rx_inx] = skb; + if(status == 0) + rtl8187_rx_urbsubmit(dev,rx_urb); + else { + priv->pp_rxskb[priv->rx_inx] = NULL; + dev_kfree_skb_any(skb); + printk("RX process aborted due to explicit shutdown (%x) ", status); + } + + if (*prx_inx == (MAX_RX_URB -1)) + *prx_inx = 0; + else + *prx_inx = *prx_inx + 1; + + */ + //} +#endif + +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_rx_manage_isr(struct urb *rx_urb, struct pt_regs *regs) +#else +void rtl8187_rx_manage_isr(struct urb* rx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)rx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + int status,cmd; + struct sk_buff *skb; + u32 *desc; + int ret; + unsigned long flag; + + //DMESG("RX %d ",rx_urb->status); + status = rx_urb->status; + if(status == 0){ + + desc = (u32*)(rx_urb->transfer_buffer); + cmd = (desc[0] >> 30) & 0x03; + //printk(KERN_ALERT "buffersize = %d, length = %d, pipe = %p\n", + //rx_urb->transfer_buffer_length, rx_urb->actual_length, rx_urb->pipe>>15); + + if(cmd == 0x00) {//beacon interrupt + //send beacon packet + + spin_lock_irqsave(&priv->ieee80211->beaconflag_lock,flag); + if(priv->flag_beacon == true){ + //printk("rtl8187_rx_manage_isr(): CMD_TYPE0_BCN_INTR\n"); + + skb = ieee80211_get_beacon(priv->ieee80211); + if(!skb){ + DMESG("not enought memory for allocating beacon"); + return; + } + //printk(KERN_WARNING "to send beacon packet through beacon endpoint!\n"); + ret = rtl8180_tx(dev, (u32*)skb->data, skb->len, BEACON_PRIORITY, + 0, ieeerate2rtlrate(priv->ieee80211->basic_rate)); + + if( ret != 0 ){ + printk(KERN_ALERT "tx beacon packet error : %d !\n", ret); + } + dev_kfree_skb_any(skb); + + //} else {//0x00 + //{ log the device information + // At present, It is not implemented just now. + //} + //} + + } + spin_unlock_irqrestore(&priv->ieee80211->beaconflag_lock,flag); + } + else if(cmd == 0x01){ + //printk("rtl8187_rx_manage_isr(): CMD_TYPE1_TX_CLOSE\n"); + priv->CurrRetryCnt += (u16)desc[0]&0x000000ff; + //printk("priv->CurrRetryCnt is %d\n",priv->CurrRetryCnt); + } + else + printk("HalUsbInCommandComplete8187B(): unknown Type(%#X) !!!\n", cmd); + + }else{ + priv->stats.rxstaterr++; + priv->ieee80211->stats.rx_errors++; + } + + + if( status == 0 ) + //if(status != -ENOENT) + rtl8187_rx_manage_urbsubmit(dev, rx_urb); + else + ;//DMESG("Mangement RX process aborted due to explicit shutdown"); +} + +#if 0 +void rtl8180_tx_queues_stop(struct net_device *dev) +{ + //struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + u8 dma_poll_mask = (1<dma_poll_mask |= (1<dma_poll_mask); + rtl8180_set_mode(dev,EPROM_CMD_NORMAL); + #endif +} + + +void rtl8180_data_hard_resume(struct net_device *dev) +{ + // FIXME !! + #if 0 + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + priv->dma_poll_mask &= ~(1<dma_poll_mask); + rtl8180_set_mode(dev,EPROM_CMD_NORMAL); + #endif +} + +unsigned int PRI2EP[4] = {0x06,0x07,0x05,0x04}; +// this function TX data frames when the ieee80211 stack requires this. +// It checks also if we need to stop the ieee tx queue, eventually do it +void rtl8180_hard_data_xmit(struct sk_buff *skb, struct net_device *dev, int rate) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + short morefrag = 0; + unsigned long flags; + struct ieee80211_hdr *h = (struct ieee80211_hdr *) skb->data; + + unsigned char ep; + short ret; //john + + if (le16_to_cpu(h->frame_ctl) & IEEE80211_FCTL_MOREFRAGS) + morefrag = 1; + //DMESG("%x %x", h->frame_ctl, h->seq_ctl); + /* + * This function doesn't require lock because we make + * sure it's called with the tx_lock already acquired. + * this come from the kernel's hard_xmit callback (trought + * the ieee stack, or from the try_wake_queue (again trought + * the ieee stack. + */ + spin_lock_irqsave(&priv->tx_lock,flags); + + //lzm mod 20081128 for sometimes wlan down but it still have some pkt to tx + if((priv->ieee80211->bHwRadioOff)||(!priv->up)) + { + spin_unlock_irqrestore(&priv->tx_lock,flags); + + return; + } + + if(NIC_8187B == priv->card_8187){ + ep = PRI2EP[skb->priority]; + } else { + ep = LOW_PRIORITY; + } + //if (!check_nic_enought_desc(dev, PRI2EP[skb->priority])){ + if (!check_nic_enought_desc(dev, ep)){ + DMESG("Error: no TX slot "); + ieee80211_stop_queue(priv->ieee80211); + } + +#ifdef LED_SHIN + priv->ieee80211->ieee80211_led_contorl(dev,LED_CTL_TX); +#endif + + ret = rtl8180_tx(dev, (u32*)skb->data, skb->len, ep, morefrag,ieeerate2rtlrate(rate)); + if(ret!=0) DMESG("Error: rtl8180_tx failed in rtl8180_hard_data_xmit\n");//john + + priv->stats.txdatapkt++; + + //if (!check_nic_enought_desc(dev, PRI2EP[skb->priority])){ + if (!check_nic_enought_desc(dev, ep)){ + ieee80211_stop_queue(priv->ieee80211); + } + + spin_unlock_irqrestore(&priv->tx_lock,flags); + +} + +//This is a rough attempt to TX a frame +//This is called by the ieee 80211 stack to TX management frames. +//If the ring is full packet are dropped (for data frame the queue +//is stopped before this can happen). + +int rtl8180_hard_start_xmit(struct sk_buff *skb,struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct ieee80211_device *ieee = priv->ieee80211; + int ret; + unsigned long flags; + spin_lock_irqsave(&priv->tx_lock,flags); + + //lzm mod 20081128 for sometimes wlan down but it still have some pkt to tx + if((priv->ieee80211->bHwRadioOff)||(!priv->up)) + { + spin_unlock_irqrestore(&priv->tx_lock,flags); + return 0; + } + + ret = rtl8180_tx(dev, (u32*)skb->data, skb->len, MANAGE_PRIORITY, 0, ieeerate2rtlrate(ieee->basic_rate)); + + priv->ieee80211->stats.tx_bytes+=skb->len; + priv->ieee80211->stats.tx_packets++; + + spin_unlock_irqrestore(&priv->tx_lock,flags); + + return ret; +} + + +#if 0 +// longpre 144+48 shortpre 72+24 +u16 rtl8180_len2duration(u32 len, short rate,short* ext) +{ + u16 duration; + u16 drift; + *ext=0; + + switch(rate){ + case 0://1mbps + *ext=0; + duration = ((len+4)<<4) /0x2; + drift = ((len+4)<<4) % 0x2; + if(drift ==0 ) break; + duration++; + break; + + case 1://2mbps + *ext=0; + duration = ((len+4)<<4) /0x4; + drift = ((len+4)<<4) % 0x4; + if(drift ==0 ) break; + duration++; + break; + + case 2: //5.5mbps + *ext=0; + duration = ((len+4)<<4) /0xb; + drift = ((len+4)<<4) % 0xb; + if(drift ==0 ) + break; + duration++; + break; + + default: + case 3://11mbps + *ext=0; + duration = ((len+4)<<4) /0x16; + drift = ((len+4)<<4) % 0x16; + if(drift ==0 ) + break; + duration++; + if(drift > 6) + break; + *ext=1; + break; + } + + return duration; +} +#endif + +void rtl8180_try_wake_queue(struct net_device *dev, int pri); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_lptx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_lptx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txlpokint++; + priv->txokbytestotal+=tx_urb->actual_length; + }else{ + priv->stats.txlperr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[LOW_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[LOW_PRIORITY]); + + rtl8180_try_wake_queue(dev,LOW_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_nptx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_nptx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txnpokint++; + }else{ + priv->stats.txnperr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[NORM_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[NORM_PRIORITY]); + //rtl8180_try_wake_queue(dev,NORM_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_votx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_votx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txvookint++; + priv->txokbytestotal+=tx_urb->actual_length; + }else{ + priv->stats.txvoerr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[VO_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[VO_PRIORITY]); + rtl8180_try_wake_queue(dev,VO_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_vitx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_vitx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txviokint++; + priv->txokbytestotal+=tx_urb->actual_length; + }else{ + priv->stats.txvierr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[VI_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[VI_PRIORITY]); + rtl8180_try_wake_queue(dev,VI_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_betx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_betx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txbeokint++; + priv->txokbytestotal+=tx_urb->actual_length; + }else{ + priv->stats.txbeerr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[BE_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[BE_PRIORITY]); + rtl8180_try_wake_queue(dev, BE_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_bktx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_bktx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txbkokint++; + }else{ + priv->stats.txbkerr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[BK_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[BK_PRIORITY]); + rtl8180_try_wake_queue(dev,BK_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_beacontx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_beacontx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txbeaconokint++; + priv->txokbytestotal+=tx_urb->actual_length; + }else{ + priv->stats.txbeaconerr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[BEACON_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[BEACON_PRIORITY]); + //rtl8180_try_wake_queue(dev,BEACON_PRIORITY); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void rtl8187_managetx_isr(struct urb *tx_urb, struct pt_regs *regs) +#else +void rtl8187_managetx_isr(struct urb* tx_urb) +#endif +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0){ + dev->trans_start = jiffies; //john + priv->stats.txmanageokint++; + priv->txokbytestotal+=tx_urb->actual_length; + }else{ + priv->stats.txmanageerr++; + } + + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + + if(atomic_read(&priv->tx_pending[MANAGE_PRIORITY]) >= 1) + atomic_dec(&priv->tx_pending[MANAGE_PRIORITY]); +// rtl8180_try_wake_queue(dev,MANAGE_PRIORITY); +} + +void rtl8187_beacon_stop(struct net_device *dev) +{ + u8 msr, msrm, msr2; + struct r8180_priv *priv = ieee80211_priv(dev); + unsigned long flag; + msr = read_nic_byte(dev, MSR); + msrm = msr & MSR_LINK_MASK; + msr2 = msr & ~MSR_LINK_MASK; + if(NIC_8187B == priv->card_8187) { + spin_lock_irqsave(&priv->ieee80211->beaconflag_lock,flag); + priv->flag_beacon = false; + spin_unlock_irqrestore(&priv->ieee80211->beaconflag_lock,flag); + } + if ((msrm == (MSR_LINK_ADHOC<ieee80211->current_network; + + + write_nic_dword(dev,BSSID,((u32*)net->bssid)[0]); + write_nic_word(dev,BSSID+4,((u16*)net->bssid)[2]); + + rtl8180_update_msr(dev); + + //rtl8180_set_mode(dev,EPROM_CMD_CONFIG); + write_nic_word(dev, AtimWnd, 2); + write_nic_word(dev, AtimtrItv, 100); + write_nic_word(dev, BEACON_INTERVAL, net->beacon_interval); + //write_nic_word(dev, BcnIntTime, 100); + write_nic_word(dev, BcnIntTime, 0x3FF); + + +} + +void rtl8187_beacon_tx(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct sk_buff *skb; + int i = 0; + u8 cr; + unsigned long flag; + rtl8187_net_update(dev); + + if(NIC_8187B == priv->card_8187) { + //Cause TSF timer of MAC reset to 0 + cr=read_nic_byte(dev,CMD); + cr = cr | (1<ieee80211->beaconflag_lock,flag); + priv->flag_beacon = true; + spin_unlock_irqrestore(&priv->ieee80211->beaconflag_lock,flag); + + //rtl8187_rx_manage_initiate(dev); + } else { + printk(KERN_WARNING "get the beacon!\n"); + skb = ieee80211_get_beacon(priv->ieee80211); + if(!skb){ + DMESG("not enought memory for allocating beacon"); + return; + } + + write_nic_byte(dev, BQREQ, read_nic_byte(dev, BQREQ) | (1<<7)); + + i=0; + //while(!read_nic_byte(dev,BQREQ & (1<<7))) + while( (read_nic_byte(dev, BQREQ) & (1<<7)) == 0 ) + { + msleep_interruptible_rtl(HZ/2); + if(i++ > 10){ + DMESGW("get stuck to wait HW beacon to be ready"); + return ; + } + } + //tx + rtl8180_tx(dev, (u32*)skb->data, skb->len, NORM_PRIORITY, + 0, ieeerate2rtlrate(priv->ieee80211->basic_rate)); + if(skb) + dev_kfree_skb_any(skb); + } +} + +#if 0 +void rtl8187_nptx_isr(struct urb *tx_urb, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device*)tx_urb->context; + struct r8180_priv *priv = ieee80211_priv(dev); + + if(tx_urb->status == 0) + priv->stats.txnpokint++; + else + priv->stats.txnperr++; + kfree(tx_urb->transfer_buffer); + usb_free_urb(tx_urb); + atomic_dec(&priv->tx_np_pending); + //rtl8180_try_wake_queue(dev,NORM_PRIORITY); +} +#endif +inline u8 rtl8180_IsWirelessBMode(u16 rate) +{ + if( ((rate <= 110) && (rate != 60) && (rate != 90)) || (rate == 220) ) + return 1; + else return 0; +} + +u16 N_DBPSOfRate(u16 DataRate); + +u16 ComputeTxTime( + u16 FrameLength, + u16 DataRate, + u8 bManagementFrame, + u8 bShortPreamble + ) +{ + u16 FrameTime; + u16 N_DBPS; + u16 Ceiling; + + if( rtl8180_IsWirelessBMode(DataRate) ) + { + if( bManagementFrame || !bShortPreamble || DataRate == 10 ){ // long preamble + FrameTime = (u16)(144+48+(FrameLength*8/(DataRate/10))); + }else{ // Short preamble + FrameTime = (u16)(72+24+(FrameLength*8/(DataRate/10))); + } + if( ( FrameLength*8 % (DataRate/10) ) != 0 ) //Get the Ceilling + FrameTime ++; + } else { //802.11g DSSS-OFDM PLCP length field calculation. + N_DBPS = N_DBPSOfRate(DataRate); + Ceiling = (16 + 8*FrameLength + 6) / N_DBPS + + (((16 + 8*FrameLength + 6) % N_DBPS) ? 1 : 0); + FrameTime = (u16)(16 + 4 + 4*Ceiling + 6); + } + return FrameTime; +} + +u16 N_DBPSOfRate(u16 DataRate) +{ + u16 N_DBPS = 24; + + switch(DataRate) + { + case 60: + N_DBPS = 24; + break; + + case 90: + N_DBPS = 36; + break; + + case 120: + N_DBPS = 48; + break; + + case 180: + N_DBPS = 72; + break; + + case 240: + N_DBPS = 96; + break; + + case 360: + N_DBPS = 144; + break; + + case 480: + N_DBPS = 192; + break; + + case 540: + N_DBPS = 216; + break; + + default: + break; + } + + return N_DBPS; +} +// NOte!!! +// the rate filled in is the rtl_rate. +// while the priv->ieee80211->basic_rate,used in the following code is ieee80211 rate. + +#ifdef JUST_FOR_87SEMESH +#define ActionHeadLen 30 +#endif +#define sCrcLng 4 +#define sAckCtsLng 112 // bits in ACK and CTS frames +short rtl8180_tx(struct net_device *dev, u32* txbuf, int len, priority_t priority, + short morefrag, short rate) +{ + u32 *tx; + int pend ; + int status; + struct urb *tx_urb; + int urb_len; + struct r8180_priv *priv = ieee80211_priv(dev); + struct ieee80211_hdr_3addr_QOS *frag_hdr = (struct ieee80211_hdr_3addr_QOS *)txbuf; + struct ieee80211_device *ieee;//added for descriptor + u8 dest[ETH_ALEN]; + + bool bUseShortPreamble = false; + bool bCTSEnable = false; + bool bRTSEnable = false; + u16 Duration = 0; + u16 RtsDur = 0; + u16 ThisFrameTime = 0; + u16 TxDescDuration = 0; + + ieee = priv->ieee80211; +#if 0 +//{added by david for filter the packet listed in the filter table +#ifdef _RTL8187_EXT_PATCH_ + if((ieee->iw_mode == ieee->iw_ext_mode) && (ieee->ext_patch_ieee80211_acl_query)) + { + if(!ieee->ext_patch_ieee80211_acl_query(ieee, frag_hdr->addr1)) { + return 0; + } + } +#endif +//} +#endif + +#ifdef JUST_FOR_87SEMESH +//#ifdef Lawrence_Mesh + u8* meshtype = (u8*)txbuf; + if(*meshtype == 0xA8) + { + //overflow?? + //memcpy(meshtype+ActionHeadLen+2,meshtype+ActionHeadLen,Len-ActionHeadLen); + //memcpy(meshtype+ActionHeadLen,0,2); + u8 actionframe[256]; + memset(actionframe,0,256); + memcpy(actionframe,meshtype,ActionHeadLen); + memcpy(actionframe+ActionHeadLen+2,meshtype+ActionHeadLen,len-ActionHeadLen); + txbuf = (u32*)actionframe; + len=len+2; + frag_hdr = (struct ieee80211_hdr_3addr_QOS *)txbuf; + } +#endif + + //pend = atomic_read((priority == NORM_PRIORITY)? &priv->tx_np_pending : &priv->tx_lp_pending); + pend = atomic_read(&priv->tx_pending[priority]); + /* we are locked here so the two atomic_read and inc are executed without interleaves */ + if( pend > MAX_TX_URB){ + if(NIC_8187 == priv->card_8187) { + if(priority == NORM_PRIORITY) + priv->stats.txnpdrop++; + else + priv->stats.txlpdrop++; + + } else { + switch (priority) { + case VO_PRIORITY: + priv->stats.txvodrop++; + break; + case VI_PRIORITY: + priv->stats.txvidrop++; + break; + case BE_PRIORITY: + priv->stats.txbedrop++; + break; + case MANAGE_PRIORITY: //lzm for MANAGE_PRIORITY pending + if(priv->commit == 0) + { + priv->commit = 1; + printk(KERN_INFO "manage pkt pending will commit now....\n"); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + schedule_work(&priv->reset_wq); +#else + schedule_task(&priv->reset_wq); +#endif + } + break; + default://BK_PRIORITY + priv->stats.txbkdrop++; + break; + } + } + //printk(KERN_INFO "tx_pending: %d > MAX_TX_URB\n", priority); + return -1; + } + + urb_len = len + ((NIC_8187 == priv->card_8187)?(4*3):(4*8)); + if((0 == (urb_len&63))||(0 == (urb_len&511))) { + urb_len += 1; + } + + tx = kmalloc(urb_len, GFP_ATOMIC); + if(!tx) return -ENOMEM; + memset(tx, 0, sizeof(u32) * 8); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + tx_urb = usb_alloc_urb(0,GFP_ATOMIC); +#else + tx_urb = usb_alloc_urb(0); +#endif + + if(!tx_urb){ + kfree(tx); + return -ENOMEM; + } + + // Check multicast/broadcast + if (ieee->iw_mode == IW_MODE_INFRA) { + /* To DS: Addr1 = BSSID, Addr2 = SA, + Addr3 = DA */ + //memcpy(&dest, frag_hdr->addr3, ETH_ALEN); + memcpy(&dest, frag_hdr->addr1, ETH_ALEN); + } else if (ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, + Addr3 = BSSID */ + memcpy(&dest, frag_hdr->addr1, ETH_ALEN); + } + + if (is_multicast_ether_addr(dest) ||is_broadcast_ether_addr(dest)) + { + Duration = 0; + RtsDur = 0; + bRTSEnable = false; + bCTSEnable = false; + + ThisFrameTime = ComputeTxTime(len + sCrcLng, rtl8180_rate2rate(rate), false, bUseShortPreamble); + TxDescDuration = ThisFrameTime; + } else {// Unicast packet + //u8 AckRate; + u16 AckTime; + + // Figure out ACK rate according to BSS basic rate and Tx rate, 2006.03.08 by rcnjko. + //AckRate = ComputeAckRate( pMgntInfo->mBrates, (u1Byte)(pTcb->DataRate) ); + // Figure out ACK time according to the AckRate and assume long preamble is used on receiver, 2006.03.08, by rcnjko. + //AckTime = ComputeTxTime( sAckCtsLng/8, AckRate, FALSE, FALSE); + //For simplicity, just use the 1M basic rate + AckTime = ComputeTxTime(14, 10,false, false); // AckCTSLng = 14 use 1M bps send + //AckTime = ComputeTxTime(14, 2,false, false); // AckCTSLng = 14 use 1M bps send + + if ( ((len + sCrcLng) > priv->rts) && priv->rts ){ // RTS/CTS. + u16 RtsTime, CtsTime; + //u16 CtsRate; + bRTSEnable = true; + bCTSEnable = false; + + // Rate and time required for RTS. + RtsTime = ComputeTxTime( sAckCtsLng/8,priv->ieee80211->basic_rate, false, false); + // Rate and time required for CTS. + CtsTime = ComputeTxTime(14, 10,false, false); // AckCTSLng = 14 use 1M bps send + + // Figure out time required to transmit this frame. + ThisFrameTime = ComputeTxTime(len + sCrcLng, + rtl8180_rate2rate(rate), + false, + bUseShortPreamble); + + // RTS-CTS-ThisFrame-ACK. + RtsDur = CtsTime + ThisFrameTime + AckTime + 3*aSifsTime; + + TxDescDuration = RtsTime + RtsDur; + }else {// Normal case. + bCTSEnable = false; + bRTSEnable = false; + RtsDur = 0; + + ThisFrameTime = ComputeTxTime(len + sCrcLng, rtl8180_rate2rate(rate), false, bUseShortPreamble); + TxDescDuration = ThisFrameTime + aSifsTime + AckTime; + } + + if(!(frag_hdr->frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { //no more fragment + // ThisFrame-ACK. + Duration = aSifsTime + AckTime; + } else { // One or more fragments remained. + u16 NextFragTime; + NextFragTime = ComputeTxTime( len + sCrcLng, //pretend following packet length equal current packet + rtl8180_rate2rate(rate), + false, bUseShortPreamble ); + + //ThisFrag-ACk-NextFrag-ACK. + Duration = NextFragTime + 3*aSifsTime + 2*AckTime; + } + + } // End of Unicast packet + + + //fill the tx desriptor + tx[0] |= len & 0xfff; +#ifdef JOHN_HWSEC + if(frag_hdr->frame_ctl & IEEE80211_FCTL_WEP ){ + tx[0] &= 0xffff7fff; + //group key may be different from pairwise key + if( frag_hdr->addr1[0]==0xff && + frag_hdr->addr1[0]==0xff && + frag_hdr->addr1[0]==0xff && + frag_hdr->addr1[0]==0xff && + frag_hdr->addr1[0]==0xff && + frag_hdr->addr1[0]==0xff ){ + if(ieee->broadcast_key_type == KEY_TYPE_CCMP) tx[7] |= 0x2;//ccmp + else tx[7] |= 0x1;//wep and tkip + } + else { + if(ieee->pairwise_key_type == KEY_TYPE_CCMP) tx[7] |= 0x2;//CCMP + else tx[7] |= 0x1;//WEP and TKIP + } + } + else +#endif /*JOHN_HWSEC*/ + + tx[0] |= (1<<15); + + if (priv->ieee80211->current_network.capability&WLAN_CAPABILITY_SHORT_PREAMBLE){ + if (priv->plcp_preamble_mode==1 && rate!=0) { // short mode now, not long! + tx[0] |= (1<<16); + } // enable short preamble mode. + } + + if(morefrag) tx[0] |= (1<<17); + //printk(KERN_WARNING "rtl_rate = %d\n", rate); + tx[0] |= (rate << 24); //TX rate + frag_hdr->duration_id = Duration; + + if(NIC_8187B == priv->card_8187) { + if(bCTSEnable) { + tx[0] |= (1<<18); + } + + if(bRTSEnable) //rts enable + { + tx[0] |= ((ieeerate2rtlrate(priv->ieee80211->basic_rate))<<19);//RTS RATE + tx[0] |= (1<<23);//rts enable + tx[1] |= RtsDur;//RTS Duration + } + tx[3] |= (TxDescDuration<<16); //DURATION + if( WLAN_FC_GET_STYPE(le16_to_cpu(frag_hdr->frame_ctl)) == IEEE80211_STYPE_PROBE_RESP ) + tx[5] |= (1<<8);//(priv->retry_data<<8); //retry lim ; + else + tx[5] |= (11<<8);//(priv->retry_data<<8); //retry lim ; + + //frag_hdr->duration_id = Duration; + memcpy(tx+8,txbuf,len); + } else { + if ( (len>priv->rts) && priv->rts && priority==LOW_PRIORITY){ + tx[0] |= (1<<23); //enalbe RTS function + tx[1] |= RtsDur; //Need to edit here! ----hikaru + } + else { + tx[1]=0; + } + tx[0] |= (ieeerate2rtlrate(priv->ieee80211->basic_rate) << 19); /* RTS RATE - should be basic rate */ + + tx[2] = 3; // CW min + tx[2] |= (7<<4); //CW max + tx[2] |= (11<<8);//(priv->retry_data<<8); //retry lim + + // printk("%x\n%x\n",tx[0],tx[1]); + +#ifdef DUMP_TX + int i; + printk("--rate %x---",rate); + for (i = 0; i < (len + 3); i++) + printk("%2x", ((u8*)tx)[i]); + printk("---------------\n"); +#endif + memcpy(tx+3,txbuf,len); + } + +#ifdef JOHN_DUMP_TXDESC + int i; + printk("--rate %x---",rate); + for (i = 0; i < 8; i++) + printk("%8x ", tx[i]); + printk("\n"); +#endif +#ifdef JOHN_DUMP_TXPKT + { + int j; + printk("\n---------------------------------------------------------------------\n"); + printk("--rate %x--urb_len in decimal %d",rate, urb_len); + for (j = 32; j < (urb_len); j++){ + if( ( (j-32)%24 )==0 ) printk("\n"); + printk("%2x ", ((u8*)tx)[j]); + } + printk("\n---------------------------------------------------------------------\n"); + + } +#endif + + if(NIC_8187 == priv->card_8187) { + usb_fill_bulk_urb(tx_urb,priv->udev, + usb_sndbulkpipe(priv->udev,priority), tx, + urb_len, (priority == LOW_PRIORITY)?rtl8187_lptx_isr:rtl8187_nptx_isr, dev); + + } else { + //printk(KERN_WARNING "Tx packet use by submit urb!\n"); + /* FIXME check what EP is for low/norm PRI */ + usb_fill_bulk_urb(tx_urb,priv->udev, + usb_sndbulkpipe(priv->udev,priority), tx, + urb_len, TXISR_SELECT(priority), dev); + } + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + status = usb_submit_urb(tx_urb, GFP_ATOMIC); +#else + status = usb_submit_urb(tx_urb); +#endif + + if (!status){ + //atomic_inc((priority == NORM_PRIORITY)? &priv->tx_np_pending : &priv->tx_lp_pending); + atomic_inc(&priv->tx_pending[priority]); + dev->trans_start = jiffies; + //printk("=====> tx_pending[%d]=%d\n", priority, atomic_read(&priv->tx_pending[priority])); + return 0; + }else{ + DMESGE("Error TX URB %d, error pending %d", + //atomic_read((priority == NORM_PRIORITY)? &priv->tx_np_pending : &priv->tx_lp_pending), + atomic_read(&priv->tx_pending[priority]), + status); + return -1; + } +} + + short rtl8187_usb_initendpoints(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + priv->rx_urb = (struct urb**) kmalloc (sizeof(struct urb*) * (MAX_RX_URB+1), GFP_KERNEL); + + memset(priv->rx_urb, 0, sizeof(struct urb*) * MAX_RX_URB); + +#ifdef JACKSON_NEW_RX + priv->pp_rxskb = (struct sk_buff **)kmalloc(sizeof(struct sk_buff *) * MAX_RX_URB, GFP_KERNEL); + if (priv->pp_rxskb == NULL) + goto destroy; + + memset(priv->pp_rxskb, 0, sizeof(struct sk_buff*) * MAX_RX_URB); +#endif +#ifdef THOMAS_BEACON + { + int align; + unsigned long oldaddr,newaddr; //lzm mod for 64bit cpu crash 20081107 + priv->rx_urb[MAX_RX_URB] = usb_alloc_urb(0, GFP_KERNEL); + priv->oldaddr = kmalloc(16, GFP_KERNEL); + oldaddr = (unsigned long)priv->oldaddr; + align = oldaddr&3; + if(align != 0 ){ + newaddr = oldaddr + 4 - align; + priv->rx_urb[MAX_RX_URB]->transfer_buffer_length = 16-4+align; + } + else{ + newaddr = oldaddr; + priv->rx_urb[MAX_RX_URB]->transfer_buffer_length = 16; + } + priv->rx_urb[MAX_RX_URB]->transfer_buffer = (u32*)newaddr; + } +#endif + + + goto _middle; + + +destroy: + +#ifdef JACKSON_NEW_RX + if (priv->pp_rxskb) { + kfree(priv->pp_rxskb); + priv->pp_rxskb = NULL; + + } +#endif + if (priv->rx_urb) { + kfree(priv->rx_urb); + } + priv->rx_urb = NULL; + + DMESGE("Endpoint Alloc Failure"); + return -ENOMEM; + + +_middle: + + return 0; + +} +#ifdef THOMAS_BEACON +void rtl8187_usb_deleteendpoints(struct net_device *dev) +{ + int i; + struct r8180_priv *priv = ieee80211_priv(dev); + + if( in_interrupt() ) + printk(KERN_ALERT " %ld in interrupt \n",in_interrupt() ); + if(priv->rx_urb){ + for(i=0;i<(MAX_RX_URB+1);i++){ + if(priv->rx_urb[i]) { + usb_kill_urb(priv->rx_urb[i]); + usb_free_urb(priv->rx_urb[i]); + } + } + kfree(priv->rx_urb); + priv->rx_urb = NULL; + } + if(priv->oldaddr){ + kfree(priv->oldaddr); + priv->oldaddr = NULL; + } + if (priv->pp_rxskb) { + kfree(priv->pp_rxskb); + priv->pp_rxskb = 0; + } +} +#endif + +void rtl8187_set_rate(struct net_device *dev) +{ + int i; + u16 word; + int basic_rate,min_rr_rate,max_rr_rate; + + //if (ieee80211_is_54g(priv->ieee80211->current_network) && + // priv->ieee80211->state == IEEE80211_LINKED){ + basic_rate = ieeerate2rtlrate(240); + min_rr_rate = ieeerate2rtlrate(60); + max_rr_rate = ieeerate2rtlrate(240); + + /* + }else{ + basic_rate = ieeerate2rtlrate(20); + min_rr_rate = ieeerate2rtlrate(10); + max_rr_rate = ieeerate2rtlrate(110); + } + */ + + write_nic_byte(dev, RESP_RATE, + max_rr_rate<beacon_interval); + rtl8187_net_update(dev); + /*update timing params*/ + rtl8180_set_chan(dev, priv->chan); + rtl8187_set_rxconf(dev); +} + +#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,20) +void rtl8180_wmm_param_update(struct work_struct* work) +{ + struct ieee80211_device * ieee = container_of(work, struct ieee80211_device,wmm_param_update_wq); + struct net_device *dev = ieee->dev; + struct r8180_priv *priv = ieee80211_priv(dev); +#else +void rtl8180_wmm_param_update(struct ieee80211_device *ieee) +{ + struct net_device *dev = ieee->dev; + struct r8180_priv *priv = ieee80211_priv(dev); +#endif + u8 *ac_param = (u8 *)(ieee->current_network.wmm_param); + u8 mode = ieee->current_network.mode; + AC_CODING eACI; + AC_PARAM AcParam; + PAC_PARAM pAcParam; + u8 i; + + //8187 need not to update wmm param, added by David, 2006.9.8 + if(NIC_8187 == priv->card_8187) { + return; + } + + if(!ieee->current_network.QoS_Enable) + { + //legacy ac_xx_param update + + AcParam.longData = 0; + AcParam.f.AciAifsn.f.AIFSN = 2; // Follow 802.11 DIFS. + AcParam.f.AciAifsn.f.ACM = 0; + AcParam.f.Ecw.f.ECWmin = 3; // Follow 802.11 CWmin. + AcParam.f.Ecw.f.ECWmax = 7; // Follow 802.11 CWmax. + AcParam.f.TXOPLimit = 0; + for(eACI = 0; eACI < AC_MAX; eACI++) + { + AcParam.f.AciAifsn.f.ACI = (u8)eACI; + { + u8 u1bAIFS; + u32 u4bAcParam; + + + pAcParam = (PAC_PARAM)(&AcParam); + // Retrive paramters to udpate. + u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN *(((mode&IEEE_G) == IEEE_G)?9:20) + aSifsTime; + u4bAcParam = ((((u32)(pAcParam->f.TXOPLimit)) << AC_PARAM_TXOP_LIMIT_OFFSET) | + (((u32)(pAcParam->f.Ecw.f.ECWmax)) << AC_PARAM_ECW_MAX_OFFSET) | + (((u32)(pAcParam->f.Ecw.f.ECWmin)) << AC_PARAM_ECW_MIN_OFFSET) | + (((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET)); + + switch(eACI) + { + case AC1_BK: + write_nic_dword(dev, AC_BK_PARAM, u4bAcParam); + break; + + case AC0_BE: + write_nic_dword(dev, AC_BE_PARAM, u4bAcParam); + break; + + case AC2_VI: + write_nic_dword(dev, AC_VI_PARAM, u4bAcParam); + break; + + case AC3_VO: + write_nic_dword(dev, AC_VO_PARAM, u4bAcParam); + break; + + default: + printk(KERN_WARNING "SetHwReg8185(): invalid ACI: %d !\n", eACI); + break; + } + } + } + + return; + } + // + for(i = 0; i < AC_MAX; i++){ + pAcParam = (AC_PARAM * )ac_param; + { + AC_CODING eACI; + u8 u1bAIFS; + u32 u4bAcParam; + + // Retrive paramters to udpate. + eACI = pAcParam->f.AciAifsn.f.ACI; + //Mode G/A: slotTimeTimer = 9; Mode B: 20 + u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN * (((mode&IEEE_G) == IEEE_G)?9:20) + aSifsTime; + u4bAcParam = ((((u32)(pAcParam->f.TXOPLimit)) << AC_PARAM_TXOP_LIMIT_OFFSET) | + (((u32)(pAcParam->f.Ecw.f.ECWmax)) << AC_PARAM_ECW_MAX_OFFSET) | + (((u32)(pAcParam->f.Ecw.f.ECWmin)) << AC_PARAM_ECW_MIN_OFFSET) | + (((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET)); + + switch(eACI) + { + case AC1_BK: + write_nic_dword(dev, AC_BK_PARAM, u4bAcParam); + //printk(KERN_WARNING "[%04x]:0x%08x\n",AC_BK_PARAM,read_nic_dword(dev, AC_BK_PARAM)); + break; + + case AC0_BE: + write_nic_dword(dev, AC_BE_PARAM, u4bAcParam); + //printk(KERN_WARNING "[%04x]:0x%08x\n",AC_BE_PARAM,read_nic_dword(dev, AC_BE_PARAM)); + break; + + case AC2_VI: + write_nic_dword(dev, AC_VI_PARAM, u4bAcParam); + //printk(KERN_WARNING "[%04x]:0x%08x\n",AC_VI_PARAM,read_nic_dword(dev, AC_VI_PARAM)); + break; + + case AC3_VO: + write_nic_dword(dev, AC_VO_PARAM, u4bAcParam); + //printk(KERN_WARNING "[%04x]:0x%08x\n",AC_VO_PARAM,read_nic_dword(dev, AC_VO_PARAM)); + break; + + default: + printk(KERN_WARNING "SetHwReg8185(): invalid ACI: %d !\n", eACI); + break; + } + } + ac_param += (sizeof(AC_PARAM)); + } +} + +int IncludedInSupportedRates(struct r8180_priv *priv, u8 TxRate ) +{ + u8 rate_len; + u8 rate_ex_len; + u8 RateMask = 0x7F; + u8 idx; + unsigned short Found = 0; + u8 NaiveTxRate = TxRate&RateMask; + + rate_len = priv->ieee80211->current_network.rates_len; + rate_ex_len = priv->ieee80211->current_network.rates_ex_len; + + for( idx=0; idx< rate_len; idx++ ){ + if( (priv->ieee80211->current_network.rates[idx] & RateMask) == NaiveTxRate ) { + Found = 1; + goto found_rate; + } + } + + for( idx=0; idx< rate_ex_len; idx++ ) { + if( (priv->ieee80211->current_network.rates_ex[idx] & RateMask) == NaiveTxRate ) { + Found = 1; + goto found_rate; + } + } + + return Found; + found_rate: + return Found; +} +// +// Description: +// Get the Tx rate one degree up form the input rate in the supported rates. +// Return the upgrade rate if it is successed, otherwise return the input rate. +// By Bruce, 2007-06-05. +// +u8 GetUpgradeTxRate(struct net_device *dev, u8 rate) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 UpRate; + + // Upgrade 1 degree. + switch(rate) + { + case 108: // Up to 54Mbps. + UpRate = 108; + break; + + case 96: // Up to 54Mbps. + UpRate = 108; + break; + + case 72: // Up to 48Mbps. + UpRate = 96; + break; + + case 48: // Up to 36Mbps. + UpRate = 72; + break; + + case 36: // Up to 24Mbps. + UpRate = 48; + break; + + case 22: // Up to 18Mbps. + UpRate = 36; + break; + + case 11: // Up to 11Mbps. + UpRate = 22; + break; + + case 4: // Up to 5.5Mbps. + UpRate = 11; + break; + + case 2: // Up to 2Mbps. + UpRate = 4; + break; + + default: + printk("GetUpgradeTxRate(): Input Tx Rate(%d) is undefined!\n", rate); + return rate; + } + // Check if the rate is valid. + if(IncludedInSupportedRates(priv, UpRate)) + { +// printk("GetUpgradeTxRate(): GetUpgrade Tx rate(%d) from %d !\n", UpRate, priv->CurrentOperaRate); + return UpRate; + } + else + { + printk("GetUpgradeTxRate(): Tx rate (%d) is not in supported rates\n", UpRate); + return rate; + } + return rate; +} +// +// Description: +// Get the Tx rate one degree down form the input rate in the supported rates. +// Return the degrade rate if it is successed, otherwise return the input rate. +// By Bruce, 2007-06-05. +// +u8 GetDegradeTxRate( struct net_device *dev, u8 rate) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 DownRate; + + // Upgrade 1 degree. + switch(rate) + { + case 108: // Down to 48Mbps. + DownRate = 96; + break; + + case 96: // Down to 36Mbps. + DownRate = 72; + break; + + case 72: // Down to 24Mbps. + DownRate = 48; + break; + + case 48: // Down to 18Mbps. + DownRate = 36; + break; + + case 36: // Down to 11Mbps. + DownRate = 22; + break; + + case 22: // Down to 5.5Mbps. + DownRate = 11; + break; + + case 11: // Down to 2Mbps. + DownRate = 4; + break; + + case 4: // Down to 1Mbps. + DownRate = 2; + break; + + case 2: // Down to 1Mbps. + DownRate = 2; + break; + + default: + printk("GetDegradeTxRate(): Input Tx Rate(%d) is undefined!\n", rate); + return rate; + } + // Check if the rate is valid. + if(IncludedInSupportedRates(priv, DownRate)){ +// printk("GetDegradeTxRate(): GetDegrade Tx rate(%d) from %d!\n", DownRate, priv->CurrentOperaRate); + return DownRate; + }else{ + printk("GetDegradeTxRate(): Tx rate (%d) is not in supported rates\n", DownRate); + return rate; + } + return rate; +} + +// +// Helper function to determine if specified data rate is +// CCK rate. +// 2005.01.25, by rcnjko. +// +bool MgntIsCckRate(u16 rate ) +{ + bool bReturn = false; + + if((rate <= 22) && (rate != 12) && (rate != 18)){ + bReturn = true; + } + + return bReturn; +} +//by amy for rate adaptive +// +// Description: +// Core logic to adjust Tx data rate in STA mode according to +// OFDM retry count ratio. +// +// Note: +// RTL8187 : pHalData->CurrRetryCnt = TallyCnt +// RTL8187B : pHalData->CurrRetryCnt = PktRetryCnt in TxClosedCommand +// +void sta_rateadaptive8187B(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + unsigned long CurrTxokCnt; + u16 CurrRetryCnt; + u16 CurrRetryRate; + unsigned long CurrRxokCnt; + bool bTryUp = false; + bool bTryDown = false; + u8 TryUpTh = 1; + u8 TryDownTh = 2; + u32 TxThroughput; + long CurrSignalStrength; + bool bUpdateInitialGain = false; + CurrRetryCnt = priv->CurrRetryCnt; + CurrTxokCnt = (priv->stats.txbeaconokint + priv->stats.txmanageokint + + priv->stats.txvookint + priv->stats.txviokint + priv->stats.txbeokint)- priv->LastTxokCnt; + CurrRxokCnt = priv->stats.rxok - priv->LastRxokCnt; + CurrSignalStrength = priv->RecvSignalPower; + TxThroughput = (u32)(priv->txokbytestotal - priv->LastTxOKBytes); + priv->LastTxOKBytes = priv->txokbytestotal; + priv->CurrentOperaRate = priv->ieee80211->rate / 5; + //printk("priv->CurrentOperaRate is %d\n",priv->CurrentOperaRate); + +#if 1 + //2 Compute retry ratio. + if (CurrTxokCnt>0) + { + CurrRetryRate = (u16)(CurrRetryCnt*100/CurrTxokCnt); + } + else + { // It may be serious retry. To distinguish serious retry or no packets modified by Bruce + CurrRetryRate = (u16)(CurrRetryCnt*100/1); + } +#endif + + + //printk("\n(1) priv->LastRetryRate: %d \n",priv->LastRetryRate); + //printk("(2) CurrRetryCnt = %d \n", CurrRetryCnt); + //printk("(3) TxokCnt = %d \n", CurrTxokCnt); + //printk("(4) CurrRetryRate = %d \n", CurrRetryRate); + //printk("(5) SignalStrength = %d \n",priv->RecvSignalPower); + + priv->LastRetryCnt = priv->CurrRetryCnt; + priv->LastTxokCnt = (priv->stats.txbeaconokint + priv->stats.txmanageokint + + priv->stats.txvookint + priv->stats.txviokint + priv->stats.txbeokint); + priv->LastRxokCnt = priv->stats.rxok; + priv->CurrRetryCnt = 0; + //2No Tx packets, return to init_rate or not? + if (CurrRetryRate==0 && CurrTxokCnt == 0) + { + // + // 2007.04.09, by Roger. after 4.5 seconds in this condition, we try to raise rate. + // + priv->TryupingCountNoData++; + + //printk("No Tx packets, TryupingCountNoData(%d)\n", priv->TryupingCountNoData); + //printk("(6) priv->CurrentOperaRate =%d\n", priv->CurrentOperaRate); + + if (priv->TryupingCountNoData>15) + { + priv->TryupingCountNoData = 0; + priv->CurrentOperaRate = GetUpgradeTxRate(dev, priv->CurrentOperaRate); + // Reset Fail Record + priv->LastFailTxRate = 0; + priv->LastFailTxRateSS = -200; + priv->FailTxRateCount = 0; + } + goto SetInitialGain; + } + else + { + priv->TryupingCountNoData=0; //Reset trying up times. + } + + // + // For Netgear case, I comment out the following signal strength estimation, + // which can results in lower rate to transmit when sample is NOT enough (e.g. PING request). + // 2007.04.09, by Roger. + // +#if 1 + // If sample is not enough, we use signalstrength. + if ( CurrTxokCnt<10|| CurrRetryCnt<10) + { + //printk("Sample is not enough, we use signalstrength for rate adaptive\n"); + //After 3 sec, and trying up. + priv->TryupingCountNoData++; + if (priv->TryupingCountNoData>10) + { + //printk("Sample is not enough and After 3 sec try up\n"); + priv->TryupingCountNoData=0; + + // + // Added by Roger, 2007.01.04. + // Signal strength plus 3 for air link. + // + + if ( CurrSignalStrength>-68 )//&& IncludedInSupportedRates(Adapter, 108) ) + { + priv->ieee80211->rate = 540; + //pMgntInfo->CurrentOperaRate = 108; + } + else if (CurrSignalStrength>-70)// && IncludedInSupportedRates(Adapter, 96) ) + { + priv->ieee80211->rate = 480; + //pMgntInfo->CurrentOperaRate = 96; + } + else if (CurrSignalStrength>-73)// && IncludedInSupportedRates(Adapter, 72) ) + { + priv->ieee80211->rate = 360; + //pMgntInfo->CurrentOperaRate = 72; + } + else if (CurrSignalStrength>-79)// && IncludedInSupportedRates(Adapter, 48) ) + { + priv->ieee80211->rate = 240; + //pMgntInfo->CurrentOperaRate = 48; + } + else if (CurrSignalStrength>-81)// && IncludedInSupportedRates(Adapter, 36) ) + { + priv->ieee80211->rate = 180; + //pMgntInfo->CurrentOperaRate = 36; + } + else if (CurrSignalStrength>-83)// && IncludedInSupportedRates(Adapter, 22) ) + { + priv->ieee80211->rate = 110; + //pMgntInfo->CurrentOperaRate = 22; + } + else if (CurrSignalStrength>-85)// && IncludedInSupportedRates(Adapter, 11) ) + { + priv->ieee80211->rate = 55; + //pMgntInfo->CurrentOperaRate = 11; + } + else if (CurrSignalStrength>-89)// && IncludedInSupportedRates(Adapter, 4) ) + { + priv->ieee80211->rate = 20; + //pMgntInfo->CurrentOperaRate = 4; + } + + + } + + //2004.12.23 skip record for 0 + //pHalData->LastRetryRate = CurrRetryRate; + //printk("pMgntInfo->CurrentOperaRate =%d\n",priv->ieee80211->rate); + return; + } + else + { + priv->TryupingCountNoData=0; + } +#endif + // + // Restructure rate adaptive as the following main stages: + // (1) Add retry threshold in 54M upgrading condition with signal strength. + // (2) Add the mechanism to degrade to CCK rate according to signal strength + // and retry rate. + // (3) Remove all Initial Gain Updates over OFDM rate. To avoid the complicated + // situation, Initial Gain Update is upon on DIG mechanism except CCK rate. + // (4) Add the mehanism of trying to upgrade tx rate. + // (5) Record the information of upping tx rate to avoid trying upping tx rate constantly. + // By Bruce, 2007-06-05. + // + // + + // 11Mbps or 36Mbps + // Check more times in these rate(key rates). + // + if(priv->CurrentOperaRate == 22 || priv->CurrentOperaRate == 72) + { + TryUpTh += 9; + } + // + // Let these rates down more difficult. + // + if(MgntIsCckRate(priv->CurrentOperaRate) || priv->CurrentOperaRate == 36) + { + TryDownTh += 1; + } + + //1 Adjust Rate. + if (priv->bTryuping == true) + { + //2 For Test Upgrading mechanism + // Note: + // Sometimes the throughput is upon on the capability bwtween the AP and NIC, + // thus the low data rate does not improve the performance. + // We randomly upgrade the data rate and check if the retry rate is improved. + + // Upgrading rate did not improve the retry rate, fallback to the original rate. + if ( (CurrRetryRate > 25) && TxThroughput < priv->LastTxThroughput) + { + //Not necessary raising rate, fall back rate. + bTryDown = true; + //printk("Not necessary raising rate, fall back rate....\n"); + //printk("(7) priv->CurrentOperaRate =%d, TxThroughput = %d, LastThroughput = %d\n", + // priv->CurrentOperaRate, TxThroughput, priv->LastTxThroughput); + } + else + { + priv->bTryuping = false; + } + } + else if (CurrSignalStrength > -51 && (CurrRetryRate < 100)) + { + //2For High Power + // + // Added by Roger, 2007.04.09. + // Return to highest data rate, if signal strength is good enough. + // SignalStrength threshold(-50dbm) is for RTL8186. + // Revise SignalStrength threshold to -51dbm. + // + // Also need to check retry rate for safety, by Bruce, 2007-06-05. + if(priv->CurrentOperaRate != 108) + { + bTryUp = true; + // Upgrade Tx Rate directly. + priv->TryupingCount += TryUpTh; + //printk("StaRateAdaptive87B: Power(%d) is high enough!!. \n", CurrSignalStrength); + } + } + // To avoid unstable rate jumping, comment out this condition, by Bruce, 2007-06-26. + /* + else if(CurrSignalStrength < -86 && CurrRetryRate >= 100) + { + //2 For Low Power + // + // Low signal strength and high current tx rate may cause Tx rate to degrade too slowly. + // Update Tx rate to CCK rate directly. + // By Bruce, 2007-06-05. + // + if(!MgntIsCckRate(pMgntInfo->CurrentOperaRate)) + { + if(CurrSignalStrength > -88 && IncludedInSupportedRates(Adapter, 22)) // 11M + pMgntInfo->CurrentOperaRate = 22; + else if(CurrSignalStrength > -90 && IncludedInSupportedRates(Adapter, 11)) // 5.5M + pMgntInfo->CurrentOperaRate = 11; + else if(CurrSignalStrength > -92 && IncludedInSupportedRates(Adapter, 4)) // 2M + pMgntInfo->CurrentOperaRate = 4; + else // 1M + pMgntInfo->CurrentOperaRate = 2; + } + else if(CurrRetryRate >= 200) + { + pMgntInfo->CurrentOperaRate = GetDegradeTxRate(Adapter, pMgntInfo->CurrentOperaRate); + } + RT_TRACE(COMP_RATE, DBG_LOUD, ("RA: Low Power(%d), or High Retry Rate(%d), set rate to CCK rate (%d). \n", + CurrSignalStrength, CurrRetryRate, pMgntInfo->CurrentOperaRate)); + bUpdateInitialGain = TRUE; + // Reset Fail Record + pHalData->LastFailTxRate = 0; + pHalData->LastFailTxRateSS = -200; + pHalData->FailTxRateCount = 0; + goto SetInitialGain; + } + */ + else if(CurrTxokCnt< 100 && CurrRetryRate >= 600) + { + //2 For Serious Retry + // + // Traffic is not busy but our Tx retry is serious. + // + bTryDown = true; + // Let Rate Mechanism to degrade tx rate directly. + priv->TryDownCountLowData += TryDownTh; + //printk("RA: Tx Retry is serious. Degrade Tx Rate to %d directly...\n", priv->CurrentOperaRate); + } + else if ( priv->CurrentOperaRate == 108 ) + { + //2For 54Mbps + // if ( (CurrRetryRate>38)&&(pHalData->LastRetryRate>35)) + if ( (CurrRetryRate>33)&&(priv->LastRetryRate>32)) + { + //(30,25) for cable link threshold. (38,35) for air link. + //Down to rate 48Mbps. + bTryDown = true; + } + } + else if ( priv->CurrentOperaRate == 96 ) + { + //2For 48Mbps + // if ( ((CurrRetryRate>73) && (pHalData->LastRetryRate>72)) && IncludedInSupportedRates(Adapter, 72) ) + if ( ((CurrRetryRate>48) && (priv->LastRetryRate>47))) + { + //(73, 72) for temp used. + //(25, 23) for cable link, (60,59) for air link. + //CurrRetryRate plus 25 and 26 respectively for air link. + //Down to rate 36Mbps. + bTryDown = true; + } + else if ( (CurrRetryRate<8) && (priv->LastRetryRate<8) ) //TO DO: need to consider (RSSI) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 72 ) + { + //2For 36Mbps + //if ( (CurrRetryRate>97) && (pHalData->LastRetryRate>97)) + if ( (CurrRetryRate>55) && (priv->LastRetryRate>54)) + { + //(30,25) for cable link threshold respectively. (103,10) for air link respectively. + //CurrRetryRate plus 65 and 69 respectively for air link threshold. + //Down to rate 24Mbps. + bTryDown = true; + } + // else if ( (CurrRetryRate<20) && (pHalData->LastRetryRate<20) && IncludedInSupportedRates(Adapter, 96) )//&& (device->LastRetryRate<15) ) //TO DO: need to consider (RSSI) + else if ( (CurrRetryRate<15) && (priv->LastRetryRate<16))//&& (device->LastRetryRate<15) ) //TO DO: need to consider (RSSI) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 48 ) + { + //2For 24Mbps + // if ( ((CurrRetryRate>119) && (pHalData->LastRetryRate>119) && IncludedInSupportedRates(Adapter, 36))) + if ( ((CurrRetryRate>63) && (priv->LastRetryRate>62))) + { + //(15,15) for cable link threshold respectively. (119, 119) for air link threshold. + //Plus 84 for air link threshold. + //Down to rate 18Mbps. + bTryDown = true; + } + // else if ( (CurrRetryRate<14) && (pHalData->LastRetryRate<15) && IncludedInSupportedRates(Adapter, 72)) //TO DO: need to consider (RSSI) + else if ( (CurrRetryRate<20) && (priv->LastRetryRate<21)) //TO DO: need to consider (RSSI) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 36 ) + { + //2For 18Mbps + if ( ((CurrRetryRate>109) && (priv->LastRetryRate>109))) + { + //(99,99) for cable link, (109,109) for air link. + //Down to rate 11Mbps. + bTryDown = true; + } + // else if ( (CurrRetryRate<15) && (pHalData->LastRetryRate<16) && IncludedInSupportedRates(Adapter, 48)) //TO DO: need to consider (RSSI) + else if ( (CurrRetryRate<25) && (priv->LastRetryRate<26)) //TO DO: need to consider (RSSI) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 22 ) + { + //2For 11Mbps + // if (CurrRetryRate>299 && IncludedInSupportedRates(Adapter, 11)) + if (CurrRetryRate>95) + { + bTryDown = true; + } + else if (CurrRetryRate<55)//&& (device->LastRetryRate<55) ) //TO DO: need to consider (RSSI) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 11 ) + { + //2For 5.5Mbps + // if (CurrRetryRate>159 && IncludedInSupportedRates(Adapter, 4) ) + if (CurrRetryRate>149) + { + bTryDown = true; + } + // else if ( (CurrRetryRate<30) && (pHalData->LastRetryRate<30) && IncludedInSupportedRates(Adapter, 22) ) + else if ( (CurrRetryRate<60) && (priv->LastRetryRate < 65)) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 4 ) + { + //2For 2 Mbps + if((CurrRetryRate>99) && (priv->LastRetryRate>99)) + { + bTryDown = true; + } + // else if ( (CurrRetryRate<50) && (pHalData->LastRetryRate<65) && IncludedInSupportedRates(Adapter, 11) ) + else if ( (CurrRetryRate < 65) && (priv->LastRetryRate < 70)) + { + bTryUp = true; + } + } + else if ( priv->CurrentOperaRate == 2 ) + { + //2For 1 Mbps + // if ( (CurrRetryRate<50) && (pHalData->LastRetryRate<65) && IncludedInSupportedRates(Adapter, 4)) + if ( (CurrRetryRate<70) && (priv->LastRetryRate<75)) + { + bTryUp = true; + } + } + if(bTryUp && bTryDown) + printk("StaRateAdaptive87B(): Tx Rate tried upping and downing simultaneously!\n"); + + //1 Test Upgrading Tx Rate + // Sometimes the cause of the low throughput (high retry rate) is the compatibility between the AP and NIC. + // To test if the upper rate may cause lower retry rate, this mechanism randomly occurs to test upgrading tx rate. + if(!bTryUp && !bTryDown && (priv->TryupingCount == 0) && (priv->TryDownCountLowData == 0) + && priv->CurrentOperaRate != 108 && priv->FailTxRateCount < 2) + { +#if 1 + if(jiffies% (CurrRetryRate + 101) == 0) + { + bTryUp = true; + priv->bTryuping = true; + printk("======================================================>StaRateAdaptive87B(): Randomly try upgrading...\n"); + } +#endif + } + //1 Rate Mechanism + if(bTryUp) + { + priv->TryupingCount++; + priv->TryDownCountLowData = 0; + + // + // Check more times if we need to upgrade indeed. + // Because the largest value of pHalData->TryupingCount is 0xFFFF and + // the largest value of pHalData->FailTxRateCount is 0x14, + // this condition will be satisfied at most every 2 min. + // + if((priv->TryupingCount > (TryUpTh + priv->FailTxRateCount * priv->FailTxRateCount)) || + (CurrSignalStrength > priv->LastFailTxRateSS) || priv->bTryuping) + { + priv->TryupingCount = 0; + // + // When transfering from CCK to OFDM, DIG is an important issue. + // + if(priv->CurrentOperaRate == 22) + bUpdateInitialGain = true; + // (1)To avoid upgrade frequently to the fail tx rate, add the FailTxRateCount into the threshold. + // (2)If the signal strength is increased, it may be able to upgrade. + priv->CurrentOperaRate = GetUpgradeTxRate(dev, priv->CurrentOperaRate); + //printk("StaRateAdaptive87B(): Upgrade Tx Rate to %d\n", priv->CurrentOperaRate); + + // Update Fail Tx rate and count. + if(priv->LastFailTxRate != priv->CurrentOperaRate) + { + priv->LastFailTxRate = priv->CurrentOperaRate; + priv->FailTxRateCount = 0; + priv->LastFailTxRateSS = -200; // Set lowest power. + } + } + } + else + { + if(priv->TryupingCount > 0) + priv->TryupingCount --; + } + + if(bTryDown) + { + priv->TryDownCountLowData++; + priv->TryupingCount = 0; + + + //Check if Tx rate can be degraded or Test trying upgrading should fallback. + if(priv->TryDownCountLowData > TryDownTh || priv->bTryuping) + { + priv->TryDownCountLowData = 0; + priv->bTryuping = false; + // Update fail information. + if(priv->LastFailTxRate == priv->CurrentOperaRate) + { + priv->FailTxRateCount ++; + // Record the Tx fail rate signal strength. + if(CurrSignalStrength > priv->LastFailTxRateSS) + { + priv->LastFailTxRateSS = CurrSignalStrength; + } + } + else + { + priv->LastFailTxRate = priv->CurrentOperaRate; + priv->FailTxRateCount = 1; + priv->LastFailTxRateSS = CurrSignalStrength; + } + priv->CurrentOperaRate = GetDegradeTxRate(dev, priv->CurrentOperaRate); + // + // When it is CCK rate, it may need to update initial gain to receive lower power packets. + // + if(MgntIsCckRate(priv->CurrentOperaRate)) + { + bUpdateInitialGain = true; + } + //printk("StaRateAdaptive87B(): Degrade Tx Rate to %d\n", priv->CurrentOperaRate); + } + } + else + { + if(priv->TryDownCountLowData > 0) + priv->TryDownCountLowData --; + } + // Keep the Tx fail rate count to equal to 0x15 at most. + // Reduce the fail count at least to 10 sec if tx rate is tending stable. + if(priv->FailTxRateCount >= 0x15 || + (!bTryUp && !bTryDown && priv->TryDownCountLowData == 0 && priv->TryupingCount && priv->FailTxRateCount > 0x6)) + { + priv->FailTxRateCount --; + } + + // + // We need update initial gain when we set tx rate "from OFDM to CCK" or + // "from CCK to OFDM". + // +SetInitialGain: +#if 1 //to be done + if(bUpdateInitialGain) + { + if(MgntIsCckRate(priv->CurrentOperaRate)) // CCK + { + if(priv->InitialGain > priv->RegBModeGainStage) + { + if(CurrSignalStrength < -85) // Low power, OFDM [0x17] = 26. + { + priv->InitialGain = priv->RegBModeGainStage; + } + else if(priv->InitialGain > priv->RegBModeGainStage + 1) + { + priv->InitialGain -= 2; + } + else + { + priv->InitialGain --; + } + UpdateInitialGain(dev); + } + } + else // OFDM + { + if(priv->InitialGain < 4) + { + priv->InitialGain ++; + UpdateInitialGain(dev); + } + } + } +#endif + //Record the related info + priv->LastRetryRate = CurrRetryRate; + priv->LastTxThroughput = TxThroughput; + priv->ieee80211->rate = priv->CurrentOperaRate * 5; +} + +#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,20) +void rtl8180_rate_adapter(struct work_struct * work) +{ + struct delayed_work *dwork = container_of(work,struct delayed_work,work); + struct ieee80211_device *ieee = container_of(dwork,struct ieee80211_device,rate_adapter_wq); + struct net_device *dev = ieee->dev; +#else +void rtl8180_rate_adapter(struct net_device *dev) +{ + +#endif + sta_rateadaptive8187B(dev); +} + +void timer_rate_adaptive(unsigned long data) +{ + struct r8180_priv* priv = ieee80211_priv((struct net_device *)data); + //DMESG("---->timer_rate_adaptive()\n"); + if(!priv->up) + { + //DMESG("<----timer_rate_adaptive():driver is not up!\n"); + return; + } + if( (priv->ieee80211->mode != IEEE_B) && + (priv->ieee80211->iw_mode != IW_MODE_MASTER) + && ((priv->ieee80211->state == IEEE80211_LINKED)||(priv->ieee80211->state == IEEE80211_MESH_LINKED))) + { + //DMESG("timer_rate_adaptive():schedule rate_adapter_wq\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + queue_delayed_work(priv->ieee80211->wq,&priv->ieee80211->rate_adapter_wq, 0); +#else + queue_work(priv->ieee80211->wq,&priv->ieee80211->rate_adapter_wq); +#endif + } + + mod_timer(&priv->rateadapter_timer, jiffies + MSECS(DEFAULT_RATE_ADAPTIVE_TIMER_PERIOD)); + //DMESG("<----timer_rate_adaptive()\n"); +} +//by amy for rate adaptive + + +void rtl8180_irq_rx_tasklet_new(struct r8180_priv *priv); +void rtl8180_irq_rx_tasklet(struct r8180_priv *priv); + +//YJ,add,080828,for KeepAlive +#if 0 +static void MgntLinkKeepAlive(struct r8180_priv *priv ) +{ + if (priv->keepAliveLevel == 0) + return; + + if(priv->ieee80211->state == IEEE80211_LINKED) + { + // + // Keep-Alive. + // + //printk("LastTx:%d Tx:%d LastRx:%d Rx:%ld Idle:%d\n",priv->link_detect.LastNumTxUnicast,priv->NumTxUnicast, priv->link_detect.LastNumRxUnicast, priv->ieee80211->NumRxUnicast, priv->link_detect.IdleCount); + + if ( (priv->keepAliveLevel== 2) || + (priv->link_detect.LastNumTxUnicast == priv->NumTxUnicast && + priv->link_detect.LastNumRxUnicast == priv->ieee80211->NumRxUnicast ) + ) + { + priv->link_detect.IdleCount++; + + // + // Send a Keep-Alive packet packet to AP if we had been idle for a while. + // + if(priv->link_detect.IdleCount >= ((KEEP_ALIVE_INTERVAL / CHECK_FOR_HANG_PERIOD)-1) ) + { + priv->link_detect.IdleCount = 0; + ieee80211_sta_ps_send_null_frame(priv->ieee80211, false); + } + } + else + { + priv->link_detect.IdleCount = 0; + } + priv->link_detect.LastNumTxUnicast = priv->NumTxUnicast; + priv->link_detect.LastNumRxUnicast = priv->ieee80211->NumRxUnicast; + } +} +//YJ,add,080828,for KeepAlive,end +#endif +void InactivePowerSave(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + // + // This flag "bSwRfProcessing", indicates the status of IPS procedure, should be set if the IPS workitem + // is really scheduled. + // The old code, sets this flag before scheduling the IPS workitem and however, at the same time the + // previous IPS workitem did not end yet, fails to schedule the current workitem. Thus, bSwRfProcessing + // blocks the IPS procedure of switching RF. + // By Bruce, 2007-12-25. + // + priv->bSwRfProcessing = true; + MgntActSet_RF_State(dev, priv->eInactivePowerState, RF_CHANGE_BY_IPS); + + // + // To solve CAM values miss in RF OFF, rewrite CAM values after RF ON. By Bruce, 2007-09-20. + // +#if 0 + while( index < 4 ) + { + if( ( pMgntInfo->SecurityInfo.PairwiseEncAlgorithm == WEP104_Encryption ) || + (pMgntInfo->SecurityInfo.PairwiseEncAlgorithm == WEP40_Encryption) ) + { + if( pMgntInfo->SecurityInfo.KeyLen[index] != 0) + pAdapter->HalFunc.SetKeyHandler(pAdapter, index, 0, FALSE, pMgntInfo->SecurityInfo.PairwiseEncAlgorithm, TRUE, FALSE); + + } + index++; + } +#endif + priv->bSwRfProcessing = false; +} + +// +// Description: +// Enter the inactive power save mode. RF will be off +// 2007.08.17, by shien chang. +// +void IPSEnter(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + RT_RF_POWER_STATE rtState; + + if (priv->bInactivePs) + { + rtState = priv->eRFPowerState; + + // + // Added by Bruce, 2007-12-25. + // Do not enter IPS in the following conditions: + // (1) RF is already OFF or Sleep + // (2) bSwRfProcessing (indicates the IPS is still under going) + // (3) Connectted (only disconnected can trigger IPS) + // (4) IBSS (send Beacon) + // (5) AP mode (send Beacon) + // + if (rtState == eRfOn && !priv->bSwRfProcessing && (priv->ieee80211->iw_mode != IW_MODE_ADHOC) + && (priv->ieee80211->state != IEEE80211_LINKED )) + { +#ifdef CONFIG_RADIO_DEBUG + DMESG("IPSEnter(): Turn off RF."); +#endif + priv->eInactivePowerState = eRfOff; + InactivePowerSave(dev); + //SetRFPowerState(dev, priv->eInactivePowerState); + //MgntActSet_RF_State(dev, priv->eInactivePowerState, RF_CHANGE_BY_IPS); + } + } + //printk("priv->eRFPowerState is %d\n",priv->eRFPowerState); +} + +void IPSLeave(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + RT_RF_POWER_STATE rtState; + if (priv->bInactivePs) + { + rtState = priv->eRFPowerState; + if (rtState == eRfOff && (!priv->bSwRfProcessing) && priv->RfOffReason <= RF_CHANGE_BY_IPS) + { +#ifdef CONFIG_RADIO_DEBUG + DMESG("ISLeave(): Turn on RF."); +#endif + priv->eInactivePowerState = eRfOn; + InactivePowerSave(dev); + } + } +// printk("priv->eRFPowerState is %d\n",priv->eRFPowerState); +} +//by amy for power save + +//YJ,add,081230 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void IPSLeave_wq (struct work_struct *work) +{ + struct ieee80211_device *ieee = container_of(work,struct ieee80211_device,ips_leave_wq); + struct net_device *dev = ieee->dev; +#else +void IPSLeave_wq(struct net_device *dev) +{ +#endif + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + down(&priv->ieee80211->ips_sem); + IPSLeave(dev); + up(&priv->ieee80211->ips_sem); +} + +void ieee80211_ips_leave(struct net_device *dev) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + if(priv->bInactivePs){ + if(priv->eRFPowerState == eRfOff) + { + //DMESG("%s", __FUNCTION__); + queue_work(priv->ieee80211->wq,&priv->ieee80211->ips_leave_wq); + } + } +} +//YJ,add,081230,end + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_watch_dog_wq (struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work,struct delayed_work,work); + struct ieee80211_device *ieee = container_of(dwork,struct ieee80211_device,watch_dog_wq); + struct net_device *dev = ieee->dev; +#else +void rtl8180_watch_dog_wq(struct net_device *dev) +{ +#endif + struct r8180_priv *priv = ieee80211_priv(dev); + //bool bEnterPS = false; + //bool bBusyTraffic = false; + u32 TotalRxNum = 0; + u16 SlotIndex = 0, i=0; + //YJ,add,080828,for link state check + if((priv->ieee80211->state == IEEE80211_LINKED) && (priv->ieee80211->iw_mode == IW_MODE_INFRA)){ + SlotIndex = (priv->link_detect.SlotIndex++) % priv->link_detect.SlotNum; + priv->link_detect.RxFrameNum[SlotIndex] = priv->ieee80211->NumRxDataInPeriod + priv->ieee80211->NumRxBcnInPeriod; + for( i=0; ilink_detect.SlotNum; i++ ) + TotalRxNum+= priv->link_detect.RxFrameNum[i]; +#if 0 //for roaming temp del + if(TotalRxNum == 0){ + priv->ieee80211->state = IEEE80211_ASSOCIATING; + printk("=========>turn to another AP\n"); + queue_work(priv->ieee80211->wq, &priv->ieee80211->associate_procedure_wq); + } +#endif + } + priv->link_detect.NumRxOkInPeriod = 0; + priv->link_detect.NumTxOkInPeriod = 0; + priv->ieee80211->NumRxDataInPeriod = 0; + priv->ieee80211->NumRxBcnInPeriod = 0; + +#ifdef CONFIG_IPS + if(priv->ieee80211->actscanning == false){ + if((priv->ieee80211->iw_mode == IW_MODE_INFRA) && + (priv->ieee80211->state == IEEE80211_NOLINK) && + (priv->eRFPowerState == eRfOn)) + { + //printk("actscanning:%d, state:%d, eRFPowerState:%d\n", + // priv->ieee80211->actscanning, + // priv->ieee80211->state, + // priv->eRFPowerState); + + down(&priv->ieee80211->ips_sem); + IPSEnter(dev); + up(&priv->ieee80211->ips_sem); + } + } + //queue_delayed_work(priv->ieee80211->wq,&priv->ieee80211->watch_dog_wq,IEEE80211_WATCH_DOG_TIME); +#endif + + //printk("========================>leave rtl8180_watch_dog_wq()\n"); +} + +void watch_dog_adaptive(unsigned long data) +{ + struct net_device* dev = (struct net_device*)data; + struct r8180_priv* priv = ieee80211_priv(dev); + //DMESG("---->watch_dog_adaptive()\n"); + if(!priv->up){ + //DMESG("<----watch_dog_adaptive():driver is not up!\n"); + return; + } + // Tx and Rx High Power Mechanism. + if(CheckHighPower(dev)){ + //printk("===============================> high power!\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + queue_delayed_work(priv->ieee80211->wq,&priv->ieee80211->tx_pw_wq, 0); +#else + queue_work(priv->ieee80211->wq,&priv->ieee80211->tx_pw_wq); +#endif + } + + // Schedule an workitem to perform DIG + if(CheckDig(dev) == true){ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + queue_delayed_work(priv->ieee80211->wq,&priv->ieee80211->hw_dig_wq,0); +#else + queue_work(priv->ieee80211->wq,&priv->ieee80211->hw_dig_wq); +#endif + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + queue_delayed_work(priv->ieee80211->wq,&priv->ieee80211->watch_dog_wq,0); +#else + queue_work(priv->ieee80211->wq,&priv->ieee80211->watch_dog_wq); +#endif + + mod_timer(&priv->watch_dog_timer, jiffies + MSECS(IEEE80211_WATCH_DOG_TIME)); + //DMESG("<----watch_dog_adaptive()\n"); +} + +#ifdef ENABLE_DOT11D + +CHANNEL_LIST Current_tbl; + +static CHANNEL_LIST ChannelPlan[] = { + {{1,2,3,4,5,6,7,8,9,10,11,36,40,44,48,52,56,60,64},19}, //FCC + {{1,2,3,4,5,6,7,8,9,10,11},11}, //IC + {{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21}, //ETSI + {{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21}, //Spain. Change to ETSI. + {{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21}, //France. Change to ETSI. + {{14,36,40,44,48,52,56,60,64},9}, //MKK + {{1,2,3,4,5,6,7,8,9,10,11,12,13,14, 36,40,44,48,52,56,60,64},22},//MKK1 + {{1,2,3,4,5,6,7,8,9,10,11,12,13,36,40,44,48,52,56,60,64},21}, //Israel. + {{1,2,3,4,5,6,7,8,9,10,11,12,13,34,38,42,46},17}, // For 11a , TELEC + {{1,2,3,4,5,6,7,8,9,10,11,12,13,14},14}, //For Global Domain. 1-11:active scan, 12-14 passive scan. //+YJ, 080626 + {{1,2,3,4,5,6,7,8,9,10,11,12,13},13} //world wide 13: ch1~ch11 active scan, ch12~13 passive //lzm add 081205 +}; + +static void rtl8180_set_channel_map(u8 channel_plan, struct ieee80211_device *ieee) +{ + int i; + + //lzm add 081205 + ieee->MinPassiveChnlNum=MAX_CHANNEL_NUMBER+1; + ieee->IbssStartChnl=0; + + switch (channel_plan) + { + case COUNTRY_CODE_FCC: + case COUNTRY_CODE_IC: + case COUNTRY_CODE_ETSI: + case COUNTRY_CODE_SPAIN: + case COUNTRY_CODE_FRANCE: + case COUNTRY_CODE_MKK: + case COUNTRY_CODE_MKK1: + case COUNTRY_CODE_ISRAEL: + case COUNTRY_CODE_TELEC: + { + Dot11d_Init(ieee); + ieee->bGlobalDomain = false; + ieee->bWorldWide13 = false; + if (ChannelPlan[channel_plan].Len != 0){ + // Clear old channel map + memset(GET_DOT11D_INFO(ieee)->channel_map, 0, sizeof(GET_DOT11D_INFO(ieee)->channel_map)); + // Set new channel map + for (i=0;ichannel_map[ChannelPlan[channel_plan].Channel[i]] = 1; + } + } + break; + } + case COUNTRY_CODE_GLOBAL_DOMAIN: + { + GET_DOT11D_INFO(ieee)->bEnabled = 0; + Dot11d_Reset(ieee); + ieee->bGlobalDomain = true; + ieee->bWorldWide13 = false; + + //lzm add 081205 + ieee->MinPassiveChnlNum=12; + ieee->IbssStartChnl= 10; + + break; + } + case COUNTRY_CODE_WORLD_WIDE_13_INDEX://lzm add 081205 + { + Dot11d_Init(ieee); + ieee->bGlobalDomain = false; + ieee->bWorldWide13 = true; + + //lzm add 081205 + ieee->MinPassiveChnlNum=12; + ieee->IbssStartChnl= 10; + + if (ChannelPlan[channel_plan].Len != 0){ + // Clear old channel map + memset(GET_DOT11D_INFO(ieee)->channel_map, 0, sizeof(GET_DOT11D_INFO(ieee)->channel_map)); + // Set new channel map + for (i=0;ichannel_map[ChannelPlan[channel_plan].Channel[i]] = 1; + else//ch12~13 passive scan + GET_DOT11D_INFO(ieee)->channel_map[ChannelPlan[channel_plan].Channel[i]] = 2; + } + } + + break; + } + default: + { + Dot11d_Init(ieee); + ieee->bGlobalDomain = false; + ieee->bWorldWide13 = false; + memset(GET_DOT11D_INFO(ieee)->channel_map, 0, sizeof(GET_DOT11D_INFO(ieee)->channel_map)); + for (i=1;i<=14;i++) + { + GET_DOT11D_INFO(ieee)->channel_map[i] = 1; + } + break; + } + } +} +#endif + + +static void rtl8180_link_detect_init(plink_detect_t plink_detect) +{ + memset(plink_detect, 0, sizeof(link_detect_t)); + plink_detect->SlotNum = DEFAULT_SLOT_NUM; +} + +#ifdef SW_ANTE_DIVERSITY +static void rtl8187_antenna_diversity_read(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u16 usValue; + + //2 Read CustomerID + usValue = eprom_read(dev, EEPROM_SW_REVD_OFFSET>>1); + priv->EEPROMCustomerID = (u8)( usValue & EEPROM_CID_MASK ); + //DMESG("EEPROM Customer ID: %02X\n", priv->EEPROMCustomerID); + + //2 Read AntennaDiversity + // SW Antenna Diversity. + if( (usValue & EEPROM_SW_AD_MASK) != EEPROM_SW_AD_ENABLE ){ + priv->EEPROMSwAntennaDiversity = false; + DMESG("EEPROM Disable SW Antenna Diversity"); + }else{ + priv->EEPROMSwAntennaDiversity = true; + DMESG("EEPROM Enable SW Antenna Diversity"); + } + // Default Antenna to use. + if( (usValue & EEPROM_DEF_ANT_MASK) != EEPROM_DEF_ANT_1 ) { + priv->EEPROMDefaultAntenna1 = false; + DMESG("EEPROM Default Main Antenna 0"); + }else{ + priv->EEPROMDefaultAntenna1 = false; + DMESG( "EEPROM Default Aux Antenna 1"); + } + + // + // Antenna diversity mechanism. Added by Roger, 2007.11.05. + // + if( priv->RegSwAntennaDiversityMechanism == 0 ) // Auto //set it to 0 when init + {// 0: default from EEPROM. + priv->bSwAntennaDiverity = priv->EEPROMSwAntennaDiversity; + }else{// 1:disable antenna diversity, 2: enable antenna diversity. + priv->bSwAntennaDiverity = ((priv->RegSwAntennaDiversityMechanism == 1)? false : true); + } + //DMESG("bSwAntennaDiverity = %d\n", priv->bSwAntennaDiverity); + + + // + // Default antenna settings. Added by Roger, 2007.11.05. + // + if( priv->RegDefaultAntenna == 0)//set it to 0 when init + { // 0: default from EEPROM. + priv->bDefaultAntenna1 = priv->EEPROMDefaultAntenna1; + }else{// 1: main, 2: aux. + priv->bDefaultAntenna1 = ((priv->RegDefaultAntenna== 2) ? true : false); + } + //DMESG("bDefaultAntenna1 = %d\n", priv->bDefaultAntenna1); + +//by amy for antenna +} +#endif + +short rtl8180_init(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int i, j; + u16 word; + //int ch; + //u16 version; + u8 hw_version; + //u8 config3; + struct usb_device *udev; + u16 idProduct; + u16 bcdDevice; + //u8 chan_plan_index; + + //FIXME: these constants are placed in a bad pleace. + + //priv->txbuffsize = 1024; + //priv->txringcount = 32; + //priv->rxbuffersize = 1024; + //priv->rxringcount = 32; + //priv->txbeaconcount = 3; + //priv->rx_skb_complete = 1; + //priv->txnp_pending.ispending=0; + /* ^^ the SKB does not containt a partial RXed packet (is empty) */ + + //memcpy(priv->stats,0,sizeof(struct Stats)); + + //priv->irq_enabled=0; + priv->driver_upping = 0; + priv->commit = 0; + + //priv->stats.rxdmafail=0; + priv->stats.txrdu=0; + //priv->stats.rxrdu=0; + //priv->stats.rxnolast=0; + //priv->stats.rxnodata=0; + //priv->stats.rxreset=0; + //priv->stats.rxwrkaround=0; + //priv->stats.rxnopointer=0; + priv->stats.txbeerr=0; + priv->stats.txbkerr=0; + priv->stats.txvierr=0; + priv->stats.txvoerr=0; + priv->stats.txmanageerr=0; + priv->stats.txbeaconerr=0; + priv->stats.txresumed=0; + //priv->stats.rxerr=0; + //priv->stats.rxoverflow=0; + //priv->stats.rxint=0; + priv->stats.txbeokint=0; + priv->stats.txbkokint=0; + priv->stats.txviokint=0; + priv->stats.txvookint=0; + priv->stats.txmanageokint=0; + priv->stats.txbeaconokint=0; + /*priv->stats.txhpokint=0; + priv->stats.txhperr=0;*/ + priv->stats.rxurberr=0; + priv->stats.rxstaterr=0; + priv->stats.txoverflow=0; + priv->stats.rxok=0; + //priv->stats.txbeaconerr=0; + //priv->stats.txlperr=0; + //priv->stats.txlpokint=0; +//john + priv->stats.txnpdrop=0; + priv->stats.txlpdrop =0; + priv->stats.txbedrop =0; + priv->stats.txbkdrop =0; + priv->stats.txvidrop =0; + priv->stats.txvodrop =0; + priv->stats.txbeacondrop =0; + priv->stats.txmanagedrop =0; + + // priv->stats.txokbytestotal =0; +//by amy + priv->LastSignalStrengthInPercent=0; + priv->SignalStrength=0; + priv->SignalQuality=0; + priv->antenna_flag=0; + priv->flag_beacon = false; +//by amy +//david + //radion on defaultly + priv->radion = 1; +//david +//by amy for rate adaptive + priv->CurrRetryCnt=0; + priv->LastRetryCnt=0; + priv->LastTxokCnt=0; + priv->LastRxokCnt=0; + priv->LastRetryRate=0; + priv->bTryuping=0; + priv->CurrTxRate=0; + priv->CurrRetryRate=0; + priv->TryupingCount=0; + priv->TryupingCountNoData=0; + priv->TryDownCountLowData=0; + priv->RecvSignalPower=0; + priv->LastTxOKBytes=0; + priv->LastFailTxRate=0; + priv->LastFailTxRateSS=0; + priv->FailTxRateCount=0; + priv->LastTxThroughput=0; + priv->txokbytestotal=0; +//by amy for rate adaptive +//by amy for ps + priv->RFChangeInProgress = false; + priv->SetRFPowerStateInProgress = false; + priv->RFProgType = 0; + priv->bInHctTest = false; + priv->bInactivePs = true;//false; + priv->ieee80211->bInactivePs = priv->bInactivePs; + priv->eInactivePowerState = eRfOn;//lzm add for IPS and Polling methord + priv->bSwRfProcessing = false; + priv->eRFPowerState = eRfOff; + priv->RfOffReason = 0; + priv->NumRxOkInPeriod = 0; + priv->NumTxOkInPeriod = 0; + priv->bLeisurePs = true; + priv->dot11PowerSaveMode = eActive; + priv->RegThreeWireMode=HW_THREE_WIRE_BY_8051; + priv->ps_mode = false; +//by amy for ps +//by amy for DIG + priv->bDigMechanism = 1; + priv->bCCKThMechanism = 0; + priv->InitialGain = 0; + priv->StageCCKTh = 0; + priv->RegBModeGainStage = 2; +//by amy for DIG +// {by david for DIG, 2008.3.6 + priv->RegDigOfdmFaUpTh = 0x0c; + priv->RegBModeGainStage = 0x02; + priv->DIG_NumberFallbackVote = 0; + priv->DIG_NumberUpgradeVote = 0; + priv->CCKUpperTh = 0x100; + priv->CCKLowerTh = 0x20; +//} +//{added by david for High tx power, 2008.3.11 + priv->bRegHighPowerMechanism = true; + priv->bToUpdateTxPwr = false; + + priv->Z2HiPwrUpperTh = 77; + priv->Z2HiPwrLowerTh = 75; + priv->Z2RSSIHiPwrUpperTh = 70; + priv->Z2RSSIHiPwrLowerTh = 20; + //specify for rtl8187B + priv->wMacRegRfPinsOutput = 0x0480; + priv->wMacRegRfPinsSelect = 0x2488; + // + // Note that, we just set TrSwState to TR_HW_CONTROLLED here instead of changing + // HW setting because we assume it should be inialized as HW controlled. 061010, by rcnjko. + // + priv->TrSwitchState = TR_HW_CONTROLLED; +//} + priv->ieee80211->iw_mode = IW_MODE_INFRA; +//test pending bug, john 20070815 + for(i=0;i<0x10;i++) atomic_set(&(priv->tx_pending[i]), 0); +//by lizhaoming for LED +#ifdef LED + priv->ieee80211->ieee80211_led_contorl = LedControl8187; +#endif +#ifdef CONFIG_IPS + priv->ieee80211->ieee80211_ips_leave = ieee80211_ips_leave;//IPSLeave; +#endif + +#ifdef SW_ANTE_DIVERSITY + priv->antb=0; + priv->diversity=1; + priv->LastRxPktAntenna = 0; + priv->AdMinCheckPeriod = 5; + priv->AdMaxCheckPeriod = 10; + // Lower signal strength threshold to fit the HW participation in antenna diversity. +by amy 080312 + priv->AdMaxRxSsThreshold = 30;//60->30 + priv->AdRxSsThreshold = 20;//50->20 + priv->AdCheckPeriod = priv->AdMinCheckPeriod; + priv->AdTickCount = 0; + priv->AdRxSignalStrength = -1; + priv->RegSwAntennaDiversityMechanism = 0; + priv->RegDefaultAntenna = 0; + priv->SignalStrength = 0; + priv->AdRxOkCnt = 0; + priv->CurrAntennaIndex = 0; + priv->AdRxSsBeforeSwitched = 0; + init_timer(&priv->SwAntennaDiversityTimer); + priv->SwAntennaDiversityTimer.data = (unsigned long)dev; + priv->SwAntennaDiversityTimer.function = (void *)SwAntennaDiversityTimerCallback; +#endif + + priv->retry_rts = DEFAULT_RETRY_RTS; + priv->retry_data = DEFAULT_RETRY_DATA; + priv->ieee80211->rate = 110; //11 mbps + priv->CurrentOperaRate=priv->ieee80211->rate/5; + priv->ieee80211->short_slot = 1; + priv->ieee80211->mode = IEEE_G; + priv->promisc = (dev->flags & IFF_PROMISC) ? 1:0; + + rtl8180_link_detect_init(&priv->link_detect); + + spin_lock_init(&priv->tx_lock); + spin_lock_init(&priv->irq_lock);//added by thomas + spin_lock_init(&priv->rf_ps_lock); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) + INIT_WORK(&priv->reset_wq, (void*)rtl8180_restart); + INIT_WORK(&priv->ieee80211->ips_leave_wq, (void*)IPSLeave_wq); //YJ,add,081230,for IPS + INIT_DELAYED_WORK(&priv->ieee80211->rate_adapter_wq,(void*)rtl8180_rate_adapter); + INIT_DELAYED_WORK(&priv->ieee80211->hw_dig_wq,(void*)rtl8180_hw_dig_wq); + INIT_DELAYED_WORK(&priv->ieee80211->watch_dog_wq,(void*)rtl8180_watch_dog_wq); + INIT_DELAYED_WORK(&priv->ieee80211->tx_pw_wq,(void*)rtl8180_tx_pw_wq); + +#ifdef SW_ANTE_DIVERSITY + INIT_DELAYED_WORK(&priv->ieee80211->SwAntennaWorkItem,(void*) SwAntennaWorkItemCallback); +#endif + +#else + INIT_WORK(&priv->reset_wq,(void*) rtl8180_restart,dev); + INIT_WORK(&priv->ieee80211->ips_leave_wq, (void*)IPSLeave_wq,dev); //YJ,add,081230,for IPS + INIT_WORK(&priv->ieee80211->rate_adapter_wq,(void*)rtl8180_rate_adapter,dev); + INIT_WORK(&priv->ieee80211->hw_dig_wq,(void*)rtl8180_hw_dig_wq,dev); + INIT_WORK(&priv->ieee80211->watch_dog_wq,(void*)rtl8180_watch_dog_wq,dev); + INIT_WORK(&priv->ieee80211->tx_pw_wq,(void*)rtl8180_tx_pw_wq,dev); + +#ifdef SW_ANTE_DIVERSITY + INIT_WORK(&priv->ieee80211->SwAntennaWorkItem,(void*) SwAntennaWorkItemCallback, dev); +#endif + +#endif +#else + tq_init(&priv->reset_wq,(void*) rtl8180_restart,dev); +#endif + sema_init(&priv->wx_sem,1); + sema_init(&priv->set_chan_sem,1); +#ifdef THOMAS_TASKLET + tasklet_init(&priv->irq_rx_tasklet, + (void(*)(unsigned long))rtl8180_irq_rx_tasklet_new, + (unsigned long)priv); +#else + tasklet_init(&priv->irq_rx_tasklet, + (void(*)(unsigned long))rtl8180_irq_rx_tasklet, + (unsigned long)priv); +#endif +//by amy for rate adaptive + init_timer(&priv->rateadapter_timer); + priv->rateadapter_timer.data = (unsigned long)dev; + priv->rateadapter_timer.function = timer_rate_adaptive; +//by amy for rate adaptive +//by amy for ps + init_timer(&priv->watch_dog_timer); + priv->watch_dog_timer.data = (unsigned long)dev; + priv->watch_dog_timer.function = watch_dog_adaptive; +//by amy +//by amy for ps + priv->ieee80211->current_network.beacon_interval = DEFAULT_BEACONINTERVAL; + priv->ieee80211->iw_mode = IW_MODE_INFRA; + priv->ieee80211->softmac_features = IEEE_SOFTMAC_SCAN | + IEEE_SOFTMAC_ASSOCIATE | IEEE_SOFTMAC_PROBERQ | +#ifdef CONFIG_SOFT_BEACON + IEEE_SOFTMAC_BEACONS | //IEEE_SOFTMAC_SINGLE_QUEUE; +#endif + IEEE_SOFTMAC_PROBERS | IEEE_SOFTMAC_TX_QUEUE; + + priv->ieee80211->active_scan = 1; + //priv->ieee80211->ch_lock = 0; + priv->ieee80211->rate = 110; //11 mbps + priv->ieee80211->modulation = IEEE80211_CCK_MODULATION | IEEE80211_OFDM_MODULATION; + priv->ieee80211->host_encrypt = 1; + priv->ieee80211->host_decrypt = 1; +#ifdef CONFIG_SOFT_BEACON + priv->ieee80211->start_send_beacons = NULL; + priv->ieee80211->stop_send_beacons = NULL; +#else + priv->ieee80211->start_send_beacons = rtl8187_beacon_tx; + priv->ieee80211->stop_send_beacons = rtl8187_beacon_stop; +#endif + priv->ieee80211->softmac_hard_start_xmit = rtl8180_hard_start_xmit; + priv->ieee80211->set_chan = rtl8180_set_chan; + priv->ieee80211->link_change = rtl8187_link_change; + priv->ieee80211->softmac_data_hard_start_xmit = rtl8180_hard_data_xmit; + priv->ieee80211->data_hard_stop = rtl8180_data_hard_stop; + priv->ieee80211->data_hard_resume = rtl8180_data_hard_resume; + +#ifdef _RTL8187_EXT_PATCH_ + priv->ieee80211->meshScanMode = 0; + priv->mshobj = alloc_mshobj(priv); + if(priv->mshobj) + { + priv->ieee80211->ext_patch_ieee80211_start_protocol = priv->mshobj->ext_patch_ieee80211_start_protocol; + priv->ieee80211->ext_patch_ieee80211_stop_protocol = priv->mshobj->ext_patch_ieee80211_stop_protocol; +//by amy for mesh + priv->ieee80211->ext_patch_ieee80211_start_mesh = priv->mshobj->ext_patch_ieee80211_start_mesh; +//by amy for mesh + priv->ieee80211->ext_patch_ieee80211_probe_req_1 = priv->mshobj->ext_patch_ieee80211_probe_req_1; + priv->ieee80211->ext_patch_ieee80211_probe_req_2 = priv->mshobj->ext_patch_ieee80211_probe_req_2; + priv->ieee80211->ext_patch_ieee80211_association_req_1 = priv->mshobj->ext_patch_ieee80211_association_req_1; + priv->ieee80211->ext_patch_ieee80211_association_req_2 = priv->mshobj->ext_patch_ieee80211_association_req_2; + priv->ieee80211->ext_patch_ieee80211_assoc_resp_by_net_1 = priv->mshobj->ext_patch_ieee80211_assoc_resp_by_net_1; + priv->ieee80211->ext_patch_ieee80211_assoc_resp_by_net_2 = priv->mshobj->ext_patch_ieee80211_assoc_resp_by_net_2; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_auth = priv->mshobj->ext_patch_ieee80211_rx_frame_softmac_on_auth; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_deauth = priv->mshobj->ext_patch_ieee80211_rx_frame_softmac_on_deauth; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_assoc_req = priv->mshobj->ext_patch_ieee80211_rx_frame_softmac_on_assoc_req; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp = priv->mshobj->ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp; + priv->ieee80211->ext_patch_ieee80211_ext_stop_scan_wq_set_channel = priv->mshobj->ext_patch_ieee80211_ext_stop_scan_wq_set_channel; + priv->ieee80211->ext_patch_ieee80211_process_probe_response_1 = priv->mshobj->ext_patch_ieee80211_process_probe_response_1; + priv->ieee80211->ext_patch_ieee80211_rx_mgt_on_probe_req = priv->mshobj->ext_patch_ieee80211_rx_mgt_on_probe_req; + priv->ieee80211->ext_patch_ieee80211_rx_mgt_update_expire = priv->mshobj->ext_patch_ieee80211_rx_mgt_update_expire; + priv->ieee80211->ext_patch_get_beacon_get_probersp = priv->mshobj->ext_patch_get_beacon_get_probersp; + priv->ieee80211->ext_patch_ieee80211_rx_on_rx = priv->mshobj->ext_patch_ieee80211_rx_on_rx; + priv->ieee80211->ext_patch_ieee80211_xmit = priv->mshobj->ext_patch_ieee80211_xmit; + priv->ieee80211->ext_patch_ieee80211_rx_frame_get_hdrlen = priv->mshobj->ext_patch_ieee80211_rx_frame_get_hdrlen; + priv->ieee80211->ext_patch_ieee80211_rx_is_valid_framectl = priv->mshobj->ext_patch_ieee80211_rx_is_valid_framectl; + priv->ieee80211->ext_patch_ieee80211_rx_process_dataframe = priv->mshobj->ext_patch_ieee80211_rx_process_dataframe; + // priv->ieee80211->ext_patch_is_duplicate_packet = priv->mshobj->ext_patch_is_duplicate_packet; + priv->ieee80211->ext_patch_ieee80211_softmac_xmit_get_rate = priv->mshobj->ext_patch_ieee80211_softmac_xmit_get_rate; + /* added by david for setting acl dynamically */ + priv->ieee80211->ext_patch_ieee80211_acl_query = priv->mshobj->ext_patch_ieee80211_acl_query; + } + +#endif // _RTL8187_EXT_PATCH_ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&priv->ieee80211->wmm_param_update_wq,\ + (void(*)(void*)) rtl8180_wmm_param_update,\ + priv->ieee80211); +#else + INIT_WORK(&priv->ieee80211->wmm_param_update_wq,\ + rtl8180_wmm_param_update); +#endif +#else + tq_init(&priv->ieee80211->wmm_param_update_wq,\ + (void(*)(void*)) rtl8180_wmm_param_update,\ + priv->ieee80211); +#endif + priv->ieee80211->init_wmmparam_flag = 0; + priv->ieee80211->fts = DEFAULT_FRAG_THRESHOLD; + + //priv->card_8185 = 2; + priv->phy_ver = 2; + priv->card_type = USB; +//{add for 87B + priv->ShortRetryLimit = 7; + priv->LongRetryLimit = 7; + priv->EarlyRxThreshold = 7; + + priv->TransmitConfig = + TCR_DurProcMode | //for RTL8185B, duration setting by HW + TCR_DISReqQsize | + (TCR_MXDMA_2048<ShortRetryLimit<LongRetryLimit<ReceiveConfig = + RCR_AMF | RCR_ADF | //accept management/data + RCR_ACF | //accept control frame for SW AP needs PS-poll, 2005.07.07, by rcnjko. + RCR_AB | RCR_AM | RCR_APM | //accept BC/MC/UC + //RCR_AICV | RCR_ACRC32 | //accept ICV/CRC error packet + RCR_MXDMA | // Max DMA Burst Size per Rx DMA Burst, 7: unlimited. + (priv->EarlyRxThreshold<EarlyRxThreshold == 7 ? RCR_ONLYERLPKT:0); + + priv->AcmControl = 0; +//} + priv->rf_chip = 0xff & eprom_read(dev,EPROM_RFCHIPID); + //DMESG("rf_chip:%x\n", priv->rf_chip); + if (!priv->rf_chip) + { + DMESG("Can't get rf chip ID from EEPROM"); + DMESGW("So set rf chip ID to defalut EPROM_RFCHIPID_RTL8225U"); + DMESGW("use it with care and at your own risk and"); + DMESGW("**PLEASE** REPORT SUCCESS/INSUCCESS TO Realtek"); + priv->rf_chip = EPROM_RFCHIPID_RTL8225U; + + } + + +//{put the card to detect different card here, mainly I/O processing + udev = priv->udev; + idProduct = le16_to_cpu(udev->descriptor.idProduct); + //idProduct = 0x8197; + bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice); + DMESG("idProduct:0x%x, bcdDevice:0x%x", idProduct,bcdDevice); + switch (idProduct) { + case 0x8189: + case 0x8197: + case 0x8198: + /* check RF frontend chipset */ + priv->card_8187 = NIC_8187B; + priv->rf_init = rtl8225z2_rf_init; + priv->rf_set_chan = rtl8225z2_rf_set_chan; + priv->rf_set_sens = NULL; + break; + + case 0x8187: + if(bcdDevice == 0x0200){ + priv->card_8187 = NIC_8187B; + priv->rf_init = rtl8225z2_rf_init; + priv->rf_set_chan = rtl8225z2_rf_set_chan; + priv->rf_set_sens = NULL; + break; + }else{ + //0x0100 is for 8187 + //legacy 8187 NIC + //delet + ; + } + default: + /* check RF frontend chipset */ + priv->ieee80211->softmac_features |= IEEE_SOFTMAC_SINGLE_QUEUE; + priv->card_8187 = NIC_8187; + switch (priv->rf_chip) { + case EPROM_RFCHIPID_RTL8225U: + DMESG("Card reports RF frontend Realtek 8225"); + DMESGW("This driver has EXPERIMENTAL support for this chipset."); + DMESGW("use it with care and at your own risk and"); + DMESGW("**PLEASE** REPORT SUCCESS/INSUCCESS TO Realtek"); + if(rtl8225_is_V_z2(dev)){ + priv->rf_init = rtl8225z2_rf_init; + priv->rf_set_chan = rtl8225z2_rf_set_chan; + priv->rf_set_sens = NULL; + DMESG("This seems a new V2 radio"); + }else{ + priv->rf_init = rtl8225_rf_init; + priv->rf_set_chan = rtl8225_rf_set_chan; + priv->rf_set_sens = rtl8225_rf_set_sens; + DMESG("This seems a legacy 1st version radio"); + } + break; + + case EPROM_RFCHIPID_RTL8225U_VF: + priv->rf_init = rtl8225z2_rf_init; + priv->rf_set_chan = rtl8225z2_rf_set_chan; + priv->rf_set_sens = NULL; + DMESG("This seems a new V2 radio"); + break; + + default: + DMESGW("Unknown RF module %x",priv->rf_chip); + DMESGW("Exiting..."); + return -1; + } + break; + } + + priv->rf_close = rtl8225_rf_close; + priv->max_sens = RTL8225_RF_MAX_SENS; + priv->sens = RTL8225_RF_DEF_SENS; + +#ifdef ENABLE_DOT11D +#if 0 + for(i=0;i<0xFF;i++) { + if(i%16 == 0) + printk("\n[%x]: ", i/16); + printk("\t%4.4x", eprom_read(dev,i)); + } +#endif + priv->channel_plan = eprom_read(dev,EPROM_CHANNEL_PLAN) & 0xFF; + //DMESG("EPROM_CHANNEL_PLAN is %x", priv->channel_plan); + + // lzm add 081205 for Toshiba: + // For Toshiba,reading the value from EEPROM 0x6h bit[7] to determine the priority of + // channel plan to be defined. + // -If bit[7] is true, then the channel plan is defined by EEPROM but no user defined. + // -If bit[7] is false, then the channel plan is defined by user first. + // + if(priv->channel_plan & 0x80) { + DMESG("Toshiba channel plan is defined by EEPROM"); + priv->channel_plan &= 0xf; + } + else + priv->channel_plan = 0; + + //if(priv->channel_plan > COUNTRY_CODE_WORLD_WIDE_13_INDEX){ + if(priv->channel_plan > COUNTRY_CODE_GLOBAL_DOMAIN){ + DMESG("rtl8180_init:Error channel plan! Set to default.\n"); + priv->channel_plan = 0; + } + + //priv->channel_plan = 9; //Global Domain + //priv->channel_plan = 2; //ETSI + + + DMESG("Channel plan is %d ",priv->channel_plan); + + //for Toshiba card we read channel plan from ChanPlanBin + if((idProduct != 0x8197) && (idProduct != 0x8198)) + rtl8180_set_channel_map(priv->channel_plan, priv->ieee80211); +#else + int ch; + priv->channel_plan = eprom_read(dev,EPROM_CHANNEL_PLAN) & 0xff; + if(priv->channel_plan & 0x80) { + priv->channel_plan &= 0x7f; + if (ChannelPlan[priv->channel_plan].Len != 0){ + // force channel plan map + for (i=0;ichannel_plan].Len;i++) + priv->ieee80211->channel_map[ChannelPlan[priv->channel_plan].Channel[i]] = 1; + } else { + DMESG("No channels, aborting"); + return -1; + } + } else { + //default channel plan setting + if(!channels){ + DMESG("No channels, aborting"); + return -1; + } + ch=channels; + // set channels 1..14 allowed in given locale + for (i=1; i<=14; i++) { + (priv->ieee80211->channel_map)[i] = (u8)(ch & 0x01); + ch >>= 1; + } + } +#endif + + if((idProduct == 0x8197) || (idProduct == 0x8198)) { + // lzm add 081205 for Toshiba: + // 0x77h bit[0]=1: use GPIO bit[2], bit[0]=0: use GPIO bit[1] + priv->EEPROMSelectNewGPIO =((u8)((eprom_read(dev,EPROM_SELECT_GPIO) & 0xff00) >> 8)) ? true : false; + DMESG("EPROM_SELECT_GPIO:%d", priv->EEPROMSelectNewGPIO); + } else { + priv->EEPROMSelectNewGPIO = false; + } + + + if (NIC_8187 == priv->card_8187){ + hw_version =( read_nic_dword(dev, TCR) & TCR_HWVERID_MASK)>>TCR_HWVERID_SHIFT; + switch (hw_version) { + case 0x06: + //priv->card_8187_Bversion = VERSION_8187B_B; + break; + case 0x05: + priv->card_8187_Bversion = VERSION_8187_D; + break; + default: + priv->card_8187_Bversion = VERSION_8187_B; + break; + } + }else{ + hw_version = read_nic_byte(dev, 0xe1); //87B hw version reg + switch (hw_version){ + case 0x00: + priv->card_8187_Bversion = VERSION_8187B_B; + break; + case 0x01: + priv->card_8187_Bversion = VERSION_8187B_D; + break; + case 0x02: + priv->card_8187_Bversion = VERSION_8187B_E; + break; + default: + priv->card_8187_Bversion = VERSION_8187B_B; //defalt + break; + } + } + + //printk("=====hw_version:%x\n", priv->card_8187_Bversion); + priv->enable_gpio0 = 0; + + /*the eeprom type is stored in RCR register bit #6 */ + if (RCR_9356SEL & read_nic_dword(dev, RCR)){ + priv->epromtype=EPROM_93c56; + DMESG("Reported EEPROM chip is a 93c56 (2Kbit)"); + }else{ + priv->epromtype=EPROM_93c46; + DMESG("Reported EEPROM chip is a 93c46 (1Kbit)"); + } + + dev->dev_addr[0]=eprom_read(dev,MAC_ADR) & 0xff; + dev->dev_addr[1]=(eprom_read(dev,MAC_ADR) & 0xff00)>>8; + dev->dev_addr[2]=eprom_read(dev,MAC_ADR+1) & 0xff; + dev->dev_addr[3]=(eprom_read(dev,MAC_ADR+1) & 0xff00)>>8; + dev->dev_addr[4]=eprom_read(dev,MAC_ADR+2) & 0xff; + dev->dev_addr[5]=((eprom_read(dev,MAC_ADR+2) & 0xff00)>>8)+1; + + DMESG("Card MAC address is "MAC_FMT, MAC_ARG(dev->dev_addr)); + + for(i=1,j=0; i<6; i+=2,j++){ + + word = eprom_read(dev,EPROM_TXPW0 + j); + priv->chtxpwr[i]=word & 0xf; + priv->chtxpwr_ofdm[i]=(word & 0xf0)>>4; + priv->chtxpwr[i+1]=(word & 0xf00)>>8; + priv->chtxpwr_ofdm[i+1]=(word & 0xf000)>>12; + } + + for(i=1,j=0; i<4; i+=2,j++){ + + word = eprom_read(dev,EPROM_TXPW1 + j); + priv->chtxpwr[i+6]=word & 0xf; + priv->chtxpwr_ofdm[i+6]=(word & 0xf0)>>4; + priv->chtxpwr[i+6+1]=(word & 0xf00)>>8; + priv->chtxpwr_ofdm[i+6+1]=(word & 0xf000)>>12; + } + if (priv->card_8187 == NIC_8187){ + for(i=1,j=0; i<4; i+=2,j++){ + word = eprom_read(dev,EPROM_TXPW2 + j); + priv->chtxpwr[i+6+4]=word & 0xf; + priv->chtxpwr_ofdm[i+6+4]=(word & 0xf0)>>4; + priv->chtxpwr[i+6+4+1]=(word & 0xf00)>>8; + priv->chtxpwr_ofdm[i+6+4+1]=(word & 0xf000)>>12; + } + } else{ + word = eprom_read(dev, 0x1B) & 0xff; //channel 11; + priv->chtxpwr[11]=word & 0xf; + priv->chtxpwr_ofdm[11] = (word & 0xf0)>>4; + + word = eprom_read(dev, 0xA) & 0xff; //channel 12; + priv->chtxpwr[12]=word & 0xf; + priv->chtxpwr_ofdm[12] = (word & 0xf0)>>4; + + word = eprom_read(dev, 0x1c) ; //channel 13 ,14; + priv->chtxpwr[13]=word & 0xf; + priv->chtxpwr_ofdm[13] = (word & 0xf0)>>4; + priv->chtxpwr[14]=(word & 0xf00)>>8; + priv->chtxpwr_ofdm[14] = (word & 0xf000)>>12; + } + + word = eprom_read(dev,EPROM_TXPW_BASE); + priv->cck_txpwr_base = word & 0xf; + priv->ofdm_txpwr_base = (word>>4) & 0xf; + + //DMESG("PAPE from CONFIG2: %x",read_nic_byte(dev,CONFIG2)&0x7); + +#ifdef SW_ANTE_DIVERSITY + rtl8187_antenna_diversity_read(dev); +#endif + + if(rtl8187_usb_initendpoints(dev)!=0){ + DMESG("Endopoints initialization failed"); + return -ENOMEM; + } +#ifdef LED + InitSwLeds(dev); +#endif +#ifdef DEBUG_EPROM + dump_eprom(dev); +#endif + return 0; + +} + +void rtl8185_rf_pins_enable(struct net_device *dev) +{ +/* u16 tmp; + tmp = read_nic_word(dev, RFPinsEnable);*/ + write_nic_word(dev, RFPinsEnable, 0x1ff7);// | tmp); +} + + +void rtl8185_set_anaparam2(struct net_device *dev, u32 a) +{ + u8 conf3; + + rtl8180_set_mode(dev, EPROM_CMD_CONFIG); + + conf3 = read_nic_byte(dev, CONFIG3); + write_nic_byte(dev, CONFIG3, conf3 | (1<> 24)); + write_nic_byte(dev, 0x7e, ((phyw & 0x00ff0000) >> 16)); + write_nic_byte(dev, 0x7d, ((phyw & 0x0000ff00) >> 8)); + write_nic_byte(dev, 0x7c, ((phyw & 0x000000ff) )); + + //read_nic_dword(dev, PHY_ADR); +#if 0 + for(i=0;i<10;i++){ + write_nic_dword(dev, PHY_ADR, 0xffffff7f & phyw); + phyr = read_nic_byte(dev, PHY_READ); + if(phyr == (data&0xff)) break; + + } +#endif + /* this is ok to fail when we write AGC table. check for AGC table might be + * done by masking with 0x7f instead of 0xff */ + //if(phyr != (data&0xff)) DMESGW("Phy write timeout %x %x %x", phyr, data, adr); + //msdelay(1);//CPU occupany is too high. LZM 31/10/2008 +} + +u8 rtl8187_read_phy(struct net_device *dev,u8 adr, u32 data) +{ + u32 phyw; + + adr &= ~0x80; + phyw= ((data<<8) | adr); + + + // Note that, we must write 0xff7c after 0x7d-0x7f to write BB register. + write_nic_byte(dev, 0x7f, ((phyw & 0xff000000) >> 24)); + write_nic_byte(dev, 0x7e, ((phyw & 0x00ff0000) >> 16)); + write_nic_byte(dev, 0x7d, ((phyw & 0x0000ff00) >> 8)); + write_nic_byte(dev, 0x7c, ((phyw & 0x000000ff) )); + + return(read_nic_byte(dev,0x7e)); + +} + +u8 read_phy_ofdm(struct net_device *dev, u8 adr) +{ + return(rtl8187_read_phy(dev, adr, 0x000000)); +} + +u8 read_phy_cck(struct net_device *dev, u8 adr) +{ + return(rtl8187_read_phy(dev, adr, 0x10000)); +} + +inline void write_phy_ofdm (struct net_device *dev, u8 adr, u32 data) +{ + data = data & 0xff; + rtl8187_write_phy(dev, adr, data); +} + + +void write_phy_cck (struct net_device *dev, u8 adr, u32 data) +{ + data = data & 0xff; + rtl8187_write_phy(dev, adr, data | 0x10000); +} +// +// Description: Change ZEBRA's power state. +// +// Assumption: This function must be executed in PASSIVE_LEVEL. +// +// 061214, by rcnjko. +// +//#define MAX_DOZE_WAITING_TIMES_87B 1000//500 +#define MAX_DOZE_WAITING_TIMES_87B_MOD 500 + +bool SetZebraRFPowerState8187B(struct net_device *dev,RT_RF_POWER_STATE eRFPowerState) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 btCR9346, btConfig3; + bool bResult = true; + int i; + u16 u2bTFPC = 0; + u8 u1bTmp; + + // Set EEM0 and EEM1 in 9346CR. + btCR9346 = read_nic_byte(dev, CR9346); + write_nic_byte(dev, CR9346, (btCR9346|0xC0) ); + // Set PARM_En in Config3. + btConfig3 = read_nic_byte(dev, CONFIG3); + write_nic_byte(dev, CONFIG3, (btConfig3|CONFIG3_PARM_En) ); + // BB and RF related operations: + switch(eRFPowerState) + { + case eRfOn: +#ifdef CONFIG_RADIO_DEBUG + DMESG("Now Radio ON!"); +#endif + write_nic_dword(dev, ANAPARAM, ANAPARM_ON); + write_nic_dword(dev, ANAPARAM2, ANAPARM2_ON); + //write_nic_byte(dev, CONFIG4, (priv->RFProgType)); + + write_nic_byte(dev, 0x085, 0x24); // 061219, SD3 ED: for minicard CCK power leakage issue. + write_rtl8225(dev, 0x4, 0x9FF); + mdelay(1); + // + // We reset PLL to reduce power consumption about 30 mA. 2008.01.16. + // + { + u8 Reg62; + + write_nic_byte(dev, 0x61, 0x10); + Reg62 = read_nic_byte(dev, 0x62); + write_nic_byte(dev, 0x62, (Reg62 & (~BIT5)) ); + write_nic_byte(dev, 0x62, (Reg62 | BIT5) ); // Enable PLL. + } + + u1bTmp = read_nic_byte(dev, 0x24E); + write_nic_byte(dev, 0x24E, (u1bTmp & (~(BIT5|BIT6))) );// 070124 SD1 Alex: turn on CCK and OFDM. + break; + + case eRfSleep: +#ifdef CONFIG_RADIO_DEBUG + DMESG("Now Radio Sleep!"); +#endif + for(i = 0; i < MAX_DOZE_WAITING_TIMES_87B_MOD; i++) + { // Make sure TX FIFO is empty befor turn off RFE pwoer. + u2bTFPC = read_nic_word(dev, TFPC); + if(u2bTFPC == 0){ + //printk("%d times TFPC: %d == 0 before doze...\n", (i+1), u2bTFPC); + break; + }else{ + //printk("%d times TFPC: %d != 0 before doze!\n", (i+1), u2bTFPC); + udelay(10); + } + } + + if( i == MAX_DOZE_WAITING_TIMES_87B_MOD ){ + //printk("\n\n\n SetZebraRFPowerState8187B(): %d times TFPC: %d != 0 !!!\n\n\n", MAX_DOZE_WAITING_TIMES_87B_MOD, u2bTFPC); + } + + u1bTmp = read_nic_byte(dev, 0x24E); + write_nic_byte(dev, 0x24E, (u1bTmp|BIT5|BIT6));// 070124 SD1 Alex: turn off CCK and OFDM. + + write_rtl8225(dev, 0x4, 0xDFF); // Turn off RF first to prevent BB lock up, suggested by PJ, 2006.03.03. + write_nic_byte(dev, 0x085, 0x04); // 061219, SD3 ED: for minicard CCK power leakage issue. + + //write_nic_byte(dev, CONFIG4, (priv->RFProgType|Config4_PowerOff)); + + write_nic_dword(dev, ANAPARAM, ANAPARM_OFF); + write_nic_dword(dev, ANAPARAM2, 0x72303f70); // 070126, by SD1 William. + break; + + case eRfOff: +#ifdef CONFIG_RADIO_DEBUG + DMESG("Now Radio OFF!"); +#endif + for(i = 0; i < MAX_DOZE_WAITING_TIMES_87B_MOD; i++) + { // Make sure TX FIFO is empty befor turn off RFE pwoer. + u2bTFPC = read_nic_word(dev, TFPC); + if(u2bTFPC == 0) { + //printk("%d times TFPC: %d == 0 before doze...\n", (i+1), u2bTFPC); + break; + }else{ + //printk("%d times TFPC: 0x%x != 0 before doze!\n", (i+1), u2bTFPC); + udelay(10); + } + } + + if( i == MAX_DOZE_WAITING_TIMES_87B_MOD){ + //printk("\n\n\nSetZebraRFPowerState8187B(): %d times TFPC: 0x%x != 0 !!!\n\n\n", MAX_DOZE_WAITING_TIMES_87B_MOD, u2bTFPC); + } + + u1bTmp = read_nic_byte(dev, 0x24E); + write_nic_byte(dev, 0x24E, (u1bTmp|BIT5|BIT6));// 070124 SD1 Alex: turn off CCK and OFDM. + + write_rtl8225(dev, 0x4,0x1FF); // Turn off RF first to prevent BB lock up, suggested by PJ, 2006.03.03. + write_nic_byte(dev, 0x085, 0x04); // 061219, SD3 ED: for minicard CCK power leakage issue. + + //write_nic_byte(dev, CONFIG4, (priv->RFProgType|Config4_PowerOff)); + + write_nic_dword(dev, ANAPARAM, ANAPARM_OFF); + write_nic_dword(dev, ANAPARAM2, ANAPARM2_OFF); // 070301, SD1 William: to reduce RF off power consumption to 80 mA. + break; + + default: + bResult = false; + //printk("SetZebraRFPowerState8187B(): unknow state to set: 0x%X!!!\n", eRFPowerState); + break; + } + + // Clear PARM_En in Config3. + btConfig3 &= ~(CONFIG3_PARM_En); + write_nic_byte(dev, CONFIG3, btConfig3); + // Clear EEM0 and EEM1 in 9346CR. + btCR9346 &= ~(0xC0); + write_nic_byte(dev, CR9346, btCR9346); + + if(bResult){ + // Update current RF state variable. + priv->eRFPowerState = eRFPowerState; + } + + return bResult; +} +//by amy for ps +// +// Description: Chang RF Power State. +// Note that, only MgntActSet_RF_State() is allowed to set HW_VAR_RF_STATE. +// +// Assumption:PASSIVE LEVEL. +// +bool SetRFPowerState(struct net_device *dev,RT_RF_POWER_STATE eRFPowerState) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + bool bResult = false; + + //printk("---------> SetRFPowerState(): eRFPowerState(%d)\n", eRFPowerState); + if(eRFPowerState == priv->eRFPowerState) + { + //printk("<--------- SetRFPowerState(): discard the request for eRFPowerState(%d) is the same.\n", eRFPowerState); + return bResult; + } + + switch(priv->rf_chip) + { + case RF_ZEBRA2: + bResult = SetZebraRFPowerState8187B(dev, eRFPowerState); + break; + + default: + printk("SetRFPowerState8185(): unknown RFChipID: 0x%X!!!\n", priv->rf_chip); + break;; + } + //printk("<--------- SetRFPowerState(): bResult(%d)\n", bResult); + + return bResult; +} + +bool +MgntActSet_RF_State(struct net_device *dev,RT_RF_POWER_STATE StateToSet,u32 ChangeSource) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + bool bActionAllowed = false; + bool bConnectBySSID = false; + RT_RF_POWER_STATE rtState; + u16 RFWaitCounter = 0; + unsigned long flag; + // printk("===>MgntActSet_RF_State(): StateToSet(%d), ChangeSource(0x%x)\n",StateToSet, ChangeSource); + // + // Prevent the race condition of RF state change. By Bruce, 2007-11-28. + // Only one thread can change the RF state at one time, and others should wait to be executed. + // +#if 1 + while(true) + { + //down(&priv->rf_state); + spin_lock_irqsave(&priv->rf_ps_lock,flag); + if(priv->RFChangeInProgress) + { + //up(&priv->rf_state); + //RT_TRACE(COMP_RF, DBG_LOUD, ("MgntActSet_RF_State(): RF Change in progress! Wait to set..StateToSet(%d).\n", StateToSet)); + spin_unlock_irqrestore(&priv->rf_ps_lock,flag); + // Set RF after the previous action is done. + while(priv->RFChangeInProgress) + { + RFWaitCounter ++; + //RT_TRACE(COMP_RF, DBG_LOUD, ("MgntActSet_RF_State(): Wait 1 ms (%d times)...\n", RFWaitCounter)); + udelay(1000); // 1 ms + + // Wait too long, return FALSE to avoid to be stuck here. + if(RFWaitCounter > 1000) // 1sec + { + // DMESG("MgntActSet_RF_State(): Wait too long to set RF"); + // TODO: Reset RF state? + return false; + } + } + } + else + { + priv->RFChangeInProgress = true; +// up(&priv->rf_state); + spin_unlock_irqrestore(&priv->rf_ps_lock,flag); + break; + } + } +#endif + rtState = priv->eRFPowerState; + + switch(StateToSet) + { + case eRfOn: + // + // Turn On RF no matter the IPS setting because we need to update the RF state to Ndis under Vista, or + // the Windows does not allow the driver to perform site survey any more. By Bruce, 2007-10-02. + // + // leave last reasons and kick this reason till priv->RfOffReason = 0; + // if one reason turn radio off check if off->on reason is the same.if so turn, or reject it. + // if more than 1 reasons turn radio off we only turn on radio when all reasons turn on radio, + // so first turn on trys will reject till priv->RfOffReason = 0; + priv->RfOffReason &= (~ChangeSource); + + if(! priv->RfOffReason) + { + priv->RfOffReason = 0; + bActionAllowed = true; + + if(rtState == eRfOff && ChangeSource >=RF_CHANGE_BY_HW && !priv->bInHctTest) + { + bConnectBySSID = true; + } + } else { + ;//lzm must or TX_PENDING 12>MAX_TX_URB + //printk("Turn Radio On Reject because RfOffReason= 0x%x, ChangeSource=0x%X\n", priv->RfOffReason, ChangeSource); + } + break; + + case eRfOff: + // 070125, rcnjko: we always keep connected in AP mode. + if (priv->RfOffReason > RF_CHANGE_BY_IPS) + { + // + // 060808, Annie: + // Disconnect to current BSS when radio off. Asked by QuanTa. + // + + // + // Calling MgntDisconnect() instead of MgntActSet_802_11_DISASSOCIATE(), + // because we do NOT need to set ssid to dummy ones. + // Revised by Roger, 2007.12.04. + // +//by amy not supported + //MgntDisconnect( dev, disas_lv_ss ); + + // Clear content of bssDesc[] and bssDesc4Query[] to avoid reporting old bss to UI. + // 2007.05.28, by shien chang. + //PlatformZeroMemory( pMgntInfo->bssDesc, sizeof(RT_WLAN_BSS)*MAX_BSS_DESC ); + //pMgntInfo->NumBssDesc = 0; + //PlatformZeroMemory( pMgntInfo->bssDesc4Query, sizeof(RT_WLAN_BSS)*MAX_BSS_DESC ); + //pMgntInfo->NumBssDesc4Query = 0; + } + + + + priv->RfOffReason |= ChangeSource; + bActionAllowed = true; + //printk("Turn Radio Off RfOffReason= 0x%x, ChangeSource=0x%X\n", priv->RfOffReason, ChangeSource); + break; + + case eRfSleep: + priv->RfOffReason |= ChangeSource; + bActionAllowed = true; + break; + + default: + break; + } + + if(bActionAllowed) + { + // Config HW to the specified mode. + //printk("MgntActSet_RF_State(): Action is allowed.... StateToSet(%d), RfOffReason(%#X)\n", StateToSet, priv->RfOffReason); + SetRFPowerState(dev, StateToSet); + // Turn on RF. + if(StateToSet == eRfOn) + { + //HalEnableRx8185Dummy(dev); + if(bConnectBySSID) + { + // by amy not supported + //MgntActSet_802_11_SSID(Adapter, Adapter->MgntInfo.Ssid.Octet, Adapter->MgntInfo.Ssid.Length, TRUE ); + } + } + // Turn off RF. + else if(StateToSet == eRfOff) + { + //HalDisableRx8185Dummy(dev); + } + } + else + { + //printk("Action is rejected.... StateToSet(%d), ChangeSource(%#X), RfOffReason(%#X)\n", + // StateToSet, ChangeSource, priv->RfOffReason); + } + + // Release RF spinlock + //down(&priv->rf_state); + spin_lock_irqsave(&priv->rf_ps_lock,flag); + priv->RFChangeInProgress = false; + //up(&priv->rf_state); + spin_unlock_irqrestore(&priv->rf_ps_lock,flag); + return bActionAllowed; +} +//by amy for ps + +void rtl8180_adapter_start(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); +// struct ieee80211_device *ieee = priv->ieee80211; +// u8 InitWirelessMode; +// u8 SupportedWirelessMode; +// bool bInvalidWirelessMode = false; + + + if(NIC_8187 == priv->card_8187) { + //rtl8180_rtx_disable(dev); + rtl8180_reset(dev); + + write_nic_byte(dev,0x85,0); + write_nic_byte(dev,0x91,0); + + /* light blink! */ + write_nic_byte(dev,0x85,4); + write_nic_byte(dev,0x91,1); + write_nic_byte(dev,0x90,0); + + //by lizhaoming for LED POWR ON + //LedControl8187(dev, LED_CTL_POWER_ON); + + /* + write_nic_byte(dev, CR9346, 0xC0); + //LED TYPE + write_nic_byte(dev, CONFIG1,((read_nic_byte(dev, CONFIG1)&0x3f)|0x80)); //turn on bit 5:Clkrun_mode + write_nic_byte(dev, CR9346, 0x0); // disable config register write + */ + priv->irq_mask = 0xffff; + /* + priv->dma_poll_mask = 0; + priv->dma_poll_mask|= (1<dev_addr)[0]); + write_nic_word(dev, MAC4, ((u32*)dev->dev_addr)[1] & 0xffff ); + + rtl8180_set_mode(dev, EPROM_CMD_NORMAL); + rtl8180_update_msr(dev); + + rtl8180_set_mode(dev, EPROM_CMD_CONFIG); + + write_nic_word(dev,0xf4,0xffff); + write_nic_byte(dev, + CONFIG1, (read_nic_byte(dev,CONFIG1) & 0x3f) | 0x80); + + rtl8180_set_mode(dev,EPROM_CMD_NORMAL); + + write_nic_dword(dev,INT_TIMEOUT,0); + +#ifdef DEBUG_REGISTERS + rtl8180_dump_reg(dev); +#endif + + + write_nic_byte(dev, WPA_CONFIG, 0); + + write_nic_byte(dev, RATE_FALLBACK, 0x81); + rtl8187_set_rate(dev); + + priv->rf_init(dev); + + if(priv->rf_set_sens != NULL) { + priv->rf_set_sens(dev,priv->sens); + } + + write_nic_word(dev,0x5e,1); + //mdelay(1); + write_nic_word(dev,0xfe,0x10); + // mdelay(1); + write_nic_byte(dev, TALLY_SEL, 0x80);//Set NQ retry count + write_nic_byte(dev, 0xff, 0x60); + write_nic_word(dev,0x5e,0); + + rtl8180_irq_enable(dev); + } else { + /** + * IO part migrated from Windows Driver. + */ + priv->irq_mask = 0xffff; + // Enable Config3.PARAM_En. + write_nic_byte(dev, CR9346, 0xC0); + + write_nic_byte(dev, CONFIG3, (read_nic_byte(dev, CONFIG3)| CONFIG3_PARM_En|CONFIG3_GNTSel)); + // Turn on Analog power. + // setup A/D D/A parameter for 8185 b2 + // Asked for by William, otherwise, MAC 3-wire can't work, 2006.06.27, by rcnjko. + write_nic_dword(dev, ANA_PARAM2, ANAPARM2_ASIC_ON); + write_nic_dword(dev, ANA_PARAM, ANAPARM_ASIC_ON); + write_nic_byte(dev, ANA_PARAM3, 0x00); + + //by lizhaoming for LED POWR ON + //LedControl8187(dev, LED_CTL_POWER_ON); + + {//added for reset PLL + u8 bReg62; + write_nic_byte(dev, 0x61, 0x10); + bReg62 = read_nic_byte(dev, 0x62); + write_nic_byte(dev, 0x62, bReg62&(~(0x1<<5))); + write_nic_byte(dev, 0x62, bReg62|(0x1<<5)); + } + write_nic_byte(dev, CONFIG3, (read_nic_byte(dev, CONFIG3)&(~CONFIG3_PARM_En))); + write_nic_byte(dev, CR9346, 0x00); + + //rtl8180_rtx_disable(dev); + rtl8180_reset(dev); + write_nic_byte(dev, CR9346, 0xc0); // enable config register write + priv->rf_init(dev); + // Enable tx/rx + + write_nic_byte(dev, CMD, (CR_RE|CR_TE));// Using HW_VAR_COMMAND instead of writing CMDR directly. Rewrited by Annie, 2006-04-07. + + //add this is for 8187B Rx stall problem + + rtl8180_irq_enable(dev); + + write_nic_byte_E(dev, 0x41, 0xF4); + write_nic_byte_E(dev, 0x40, 0x00); + write_nic_byte_E(dev, 0x42, 0x00); + write_nic_byte_E(dev, 0x42, 0x01); + write_nic_byte_E(dev, 0x40, 0x0F); + write_nic_byte_E(dev, 0x42, 0x00); + write_nic_byte_E(dev, 0x42, 0x01); + + // 8187B demo board MAC and AFE power saving parameters from SD1 William, 2006.07.20. + // Parameter revised by SD1 William, 2006.08.21: + // 373h -> 0x4F // Original: 0x0F + // 377h -> 0x59 // Original: 0x4F + // Victor 2006/8/21: ¬Ù¹q°Ñ¼Æ«Øijµ¥SD3 °ª§C·Å and cable link test OK¤~¥¿¦¡ release,¤£«Øij¤Ó§Ö + // 2006/9/5, Victor & ED: it is OK to use. + //if(pHalData->RegBoardType == BT_DEMO_BOARD) + //{ + // AFE. + // + // Revise the content of Reg0x372, 0x374, 0x378 and 0x37a to fix unusual electronic current + // while CTS-To-Self occurs, by DZ's request. + // Modified by Roger, 2007.06.22. + // + write_nic_byte(dev, 0x0DB, (read_nic_byte(dev, 0x0DB)|(BIT2))); + write_nic_word(dev, 0x372, 0x59FA); // 0x4FFA-> 0x59FA. + write_nic_word(dev, 0x374, 0x59D2); // 0x4FD2-> 0x59D2. + write_nic_word(dev, 0x376, 0x59D2); + write_nic_word(dev, 0x378, 0x19FA); // 0x0FFA-> 0x19FA. + write_nic_word(dev, 0x37A, 0x19FA); // 0x0FFA-> 0x19FA. + write_nic_word(dev, 0x37C, 0x00D0); + + write_nic_byte(dev, 0x061, 0x00); + + // MAC. + write_nic_byte(dev, 0x180, 0x0F); + write_nic_byte(dev, 0x183, 0x03); + // 061218, lanhsin: for victorh request + write_nic_byte(dev, 0x0DA, 0x10); + //} + + // + // 061213, rcnjko: + // Set MAC.0x24D to 0x00 to prevent cts-to-self Tx/Rx stall symptom. + // If we set MAC 0x24D to 0x08, OFDM and CCK will turn off + // if not in use, and there is a bug about this action when + // we try to send CCK CTS and OFDM data together. + // + //PlatformEFIOWrite1Byte(Adapter, 0x24D, 0x00); + // 061218, lanhsin: for victorh request + write_nic_byte(dev, 0x24D, 0x08); + + // + // 061215, rcnjko: + // Follow SD3 RTL8185B_87B_MacPhy_Register.doc v0.4. + // + write_nic_dword(dev, HSSI_PARA, 0x0600321B); + // + // 061226, rcnjko: + // Babble found in HCT 2c_simultaneous test, server with 87B might + // receive a packet size about 2xxx bytes. + // So, we restrict RMS to 2048 (0x800), while as IC default value is 0xC00. + // + write_nic_word(dev, RMS, 0x0800); + + /*****20070321 Resolve HW page bug on system logo test + u8 faketemp=read_nic_byte(dev, 0x50);*/ + } +} + +/* this configures registers for beacon tx and enables it via + * rtl8180_beacon_tx_enable(). rtl8180_beacon_tx_disable() might + * be used to stop beacon transmission + */ +#if 0 +void rtl8180_start_tx_beacon(struct net_device *dev) +{ + int i; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + u16 word; + DMESG("Enabling beacon TX"); + //write_nic_byte(dev, 0x42,0xe6);// TCR + //rtl8180_init_beacon(dev); + //set_nic_txring(dev); +// rtl8180_prepare_beacon(dev); + rtl8180_irq_disable(dev); +// rtl8180_beacon_tx_enable(dev); + rtl8180_set_mode(dev,EPROM_CMD_CONFIG); + //write_nic_byte(dev,0x9d,0x20); //DMA Poll + //write_nic_word(dev,0x7a,0); + //write_nic_word(dev,0x7a,0x8000); + + + word = read_nic_word(dev, BcnItv); + word &= ~BcnItv_BcnItv; // clear Bcn_Itv + write_nic_word(dev, BcnItv, word); + + write_nic_word(dev, AtimWnd, + read_nic_word(dev, AtimWnd) &~ AtimWnd_AtimWnd); + + word = read_nic_word(dev, BintrItv); + word &= ~BintrItv_BintrItv; + + //word |= priv->ieee80211->beacon_interval * + // ((priv->txbeaconcount > 1)?(priv->txbeaconcount-1):1); + // FIXME:FIXME check if correct ^^ worked with 0x3e8; + + write_nic_word(dev, BintrItv, word); + + //write_nic_word(dev,0x2e,0xe002); + //write_nic_dword(dev,0x30,0xb8c7832e); + for(i=0; iieee80211->beacon_cell_ssid[i]); + +// rtl8180_update_msr(dev); + + + //write_nic_byte(dev,CONFIG4,3); /* !!!!!!!!!! */ + + rtl8180_set_mode(dev, EPROM_CMD_NORMAL); + + rtl8180_irq_enable(dev); + + /* VV !!!!!!!!!! VV*/ + /* + rtl8180_set_mode(dev,EPROM_CMD_CONFIG); + write_nic_byte(dev,0x9d,0x00); + rtl8180_set_mode(dev,EPROM_CMD_NORMAL); +*/ +} +#endif +/*************************************************************************** + -------------------------------NET STUFF--------------------------- +***************************************************************************/ +static struct net_device_stats *rtl8180_stats(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + return &priv->ieee80211->stats; +} + +int _rtl8180_up(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + //int i; + + priv->driver_upping = 1; + priv->up=1; + +#ifdef LED + if(priv->ieee80211->bHwRadioOff == false) + priv->ieee80211->ieee80211_led_contorl(dev,LED_CTL_POWER_ON); +#endif + MgntActSet_RF_State(dev, eRfOn, RF_CHANGE_BY_SW); + + rtl8180_adapter_start(dev); + rtl8180_rx_enable(dev); + rtl8180_tx_enable(dev); +//by amy for rate adaptive + timer_rate_adaptive((unsigned long)dev); +//by amy for rate adaptive + +#ifdef SW_ANTE_DIVERSITY + if(priv->bSwAntennaDiverity){ + //DMESG("SW Antenna Diversity Enable!"); + SwAntennaDiversityTimerCallback(dev); + } +#endif + + ieee80211_softmac_start_protocol(priv->ieee80211); + +//by amy for ps + watch_dog_adaptive((unsigned long)dev); +//by amy for ps + + ieee80211_reset_queue(priv->ieee80211); + if(!netif_queue_stopped(dev)) + netif_start_queue(dev); + else + netif_wake_queue(dev); + +#ifndef CONFIG_SOFT_BEACON + if(NIC_8187B == priv->card_8187) + rtl8187_rx_manage_initiate(dev); +#endif + +#ifdef _RTL8187_EXT_PATCH_ + if(priv->mshobj && priv->mshobj->ext_patch_rtl8180_up ) + priv->mshobj->ext_patch_rtl8180_up(priv->mshobj); +#endif + + + priv->driver_upping = 0; + //DMESG("rtl8187_open process complete"); + return 0; +} + + +int rtl8180_open(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int ret; +//changed by lizhaoming for power on/off + if(priv->ieee80211->bHwRadioOff == false){ + //DMESG("rtl8187_open process "); + down(&priv->wx_sem); + ret = rtl8180_up(dev); + up(&priv->wx_sem); + return ret; + }else{ + DMESG("rtl8187_open process failed because radio off"); + return -1; + } + +} + + +int rtl8180_up(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if (priv->up == 1) return -1; + + return _rtl8180_up(dev); +} + + +int rtl8180_close(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int ret; + + if (priv->up == 0) return -1; + + down(&priv->wx_sem); + + //DMESG("rtl8187_close process"); + ret = rtl8180_down(dev); +#ifdef LED + priv->ieee80211->ieee80211_led_contorl(dev,LED_CTL_POWER_OFF); +#endif + up(&priv->wx_sem); + + return ret; + +} + +int rtl8180_down(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if (priv->up == 0) return -1; + + priv->up=0; + +/* FIXME */ + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + + //DMESG("rtl8180_down process"); + rtl8180_rtx_disable(dev); + rtl8180_irq_disable(dev); +//by amy for rate adaptive + del_timer_sync(&priv->rateadapter_timer); + cancel_delayed_work(&priv->ieee80211->rate_adapter_wq); +//by amy for rate adaptive + del_timer_sync(&priv->watch_dog_timer); + cancel_delayed_work(&priv->ieee80211->watch_dog_wq); + cancel_delayed_work(&priv->ieee80211->hw_dig_wq); + cancel_delayed_work(&priv->ieee80211->tx_pw_wq); + +#ifdef SW_ANTE_DIVERSITY + del_timer_sync(&priv->SwAntennaDiversityTimer); + cancel_delayed_work(&priv->ieee80211->SwAntennaWorkItem); +#endif + + ieee80211_softmac_stop_protocol(priv->ieee80211); + MgntActSet_RF_State(dev, eRfOff, RF_CHANGE_BY_SW); + //amy,081212 + memset(&(priv->ieee80211->current_network),0,sizeof(struct ieee80211_network)); + return 0; +} + +bool SetZebraRFPowerState8187B(struct net_device *dev,RT_RF_POWER_STATE eRFPowerState); + +void rtl8180_commit(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + int i; + + if (priv->up == 0) return ; + printk("==========>%s()\n", __FUNCTION__); + + /* FIXME */ + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + +//by amy for rate adaptive + del_timer_sync(&priv->rateadapter_timer); + cancel_delayed_work(&priv->ieee80211->rate_adapter_wq); +//by amy for rate adaptive + del_timer_sync(&priv->watch_dog_timer); + cancel_delayed_work(&priv->ieee80211->watch_dog_wq); + cancel_delayed_work(&priv->ieee80211->hw_dig_wq); + cancel_delayed_work(&priv->ieee80211->tx_pw_wq); + +#ifdef SW_ANTE_DIVERSITY + del_timer_sync(&priv->SwAntennaDiversityTimer); + cancel_delayed_work(&priv->ieee80211->SwAntennaWorkItem); +#endif + ieee80211_softmac_stop_protocol(priv->ieee80211); + +#if 0 + if(priv->ieee80211->bHwRadioOff == false){ + MgntActSet_RF_State(dev, eRfOff, RF_CHANGE_BY_HW); + mdelay(10); + MgntActSet_RF_State(dev, eRfOn, RF_CHANGE_BY_HW); + } +#endif + + rtl8180_irq_disable(dev); + rtl8180_rtx_disable(dev); + + //test pending bug, john 20070815 + //initialize tx_pending + for(i=0;i<0x10;i++) atomic_set(&(priv->tx_pending[i]), 0); + + _rtl8180_up(dev); + priv->commit = 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) +void rtl8180_restart(struct work_struct *work) +{ + struct r8180_priv *priv = container_of(work, struct r8180_priv, reset_wq); + struct ieee80211_device* ieee = priv->ieee80211;//for commit crash + struct net_device *dev = ieee->dev;//for commit crash +#else +void rtl8180_restart(struct net_device *dev) +{ + + struct r8180_priv *priv = ieee80211_priv(dev); +#endif + + down(&priv->wx_sem); + + rtl8180_commit(dev); + + up(&priv->wx_sem); +} + +static void r8180_set_multicast(struct net_device *dev) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + short promisc; + + //down(&priv->wx_sem); + + /* FIXME FIXME */ + + promisc = (dev->flags & IFF_PROMISC) ? 1:0; + + if (promisc != priv->promisc) + // rtl8180_commit(dev); + + priv->promisc = promisc; + + //schedule_work(&priv->reset_wq); + //up(&priv->wx_sem); +} + + +int r8180_set_mac_adr(struct net_device *dev, void *mac) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + struct sockaddr *addr = mac; + + down(&priv->wx_sem); + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + schedule_work(&priv->reset_wq); +#else + schedule_task(&priv->reset_wq); +#endif + up(&priv->wx_sem); + + return 0; +} + +struct ipw_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u8 name; + u32 value; + } wpa_param; + struct { + u32 len; + u8 reserved[32]; + u8 data[0]; + } wpa_ie; + struct{ + u32 command; + u32 reason_code; + } mlme; + struct { + u8 alg[16]; + u8 set_tx; + u32 err; + u8 idx; + u8 seq[8]; + u16 key_len; + u8 key[0]; + } crypt; + + } u; +}; + + +/* based on ipw2200 driver */ +int rtl8180_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + struct iwreq *wrq = (struct iwreq *)rq; + int ret=-1; + +#ifdef JOHN_TKIP + u8 broadcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; + + struct ieee80211_device *ieee = priv->ieee80211; + struct ipw_param *ipw = (struct ipw_param *)wrq->u.data.pointer; + u32 key[4]; + +#endif + +#ifdef _RTL8187_EXT_PATCH_ + if(priv->mshobj && (priv->ieee80211->iw_mode == priv->ieee80211->iw_ext_mode) && priv->mshobj->ext_patch_rtl8180_ioctl) + { + // DO NOT put the belowing function in critical section, due to it uses "spin lock" + if((ret = priv->mshobj->ext_patch_rtl8180_ioctl(dev, rq, cmd)) != -EOPNOTSUPP) + return ret; + } +#endif + + down(&priv->wx_sem); + + switch (cmd) { + case RTL_IOCTL_WPA_SUPPLICANT: +#ifdef JOHN_TKIP + +//the following code is specified for ipw driver in wpa_supplicant + if( ((u32*)wrq->u.data.pointer)[0]==3 ){ + + + if( ((u32*)wrq->u.data.pointer)[7] && + ( ((u32*)wrq->u.data.pointer)[3]==0x504d4343 || + ((u32*)wrq->u.data.pointer)[3]==0x50494b54 )) {//50494b54 tkip and 504d4343 ccmp + + //enable HW security of TKIP and CCMP + write_nic_byte(dev, WPA_CONFIG, SCR_TxSecEnable | SCR_RxSecEnable ); + + //copy key from wpa_supplicant ioctl info + key[0] = ((u32*)wrq->u.data.pointer)[12]; + key[1] = ((u32*)wrq->u.data.pointer)[13]; + key[2] = ((u32*)wrq->u.data.pointer)[14]; + key[3] = ((u32*)wrq->u.data.pointer)[15]; + switch (ieee->pairwise_key_type){ + case KEY_TYPE_TKIP: + setKey( dev, + 0, //EntryNo + ipw->u.crypt.idx, //KeyIndex + KEY_TYPE_TKIP, //KeyType + (u8*)ieee->ap_mac_addr, //MacAddr + 0, //DefaultKey + key); //KeyContent + break; + + case KEY_TYPE_CCMP: + setKey( dev, + 0, //EntryNo + ipw->u.crypt.idx, //KeyIndex + KEY_TYPE_CCMP, //KeyType + (u8*)ieee->ap_mac_addr, //MacAddr + 0, //DefaultKey + key); //KeyContent + break +; + default: + printk("error on key_type: %d\n", ieee->pairwise_key_type); + break; + } + } + + //group key for broadcast + if( ((u32*)wrq->u.data.pointer)[9] ) { + + key[0] = ((u32*)wrq->u.data.pointer)[12]; + key[1] = ((u32*)wrq->u.data.pointer)[13]; + key[2] = ((u32*)wrq->u.data.pointer)[14]; + key[3] = ((u32*)wrq->u.data.pointer)[15]; + + if( ((u32*)wrq->u.data.pointer)[3]==0x50494b54 ){//50494b54 is the ASCII code of TKIP in reversed order + setKey( dev, + 1, //EntryNo + ipw->u.crypt.idx,//KeyIndex + KEY_TYPE_TKIP, //KeyType + broadcast_addr, //MacAddr + 0, //DefaultKey + key); //KeyContent + } + else if( ((u32*)wrq->u.data.pointer)[3]==0x504d4343 ){//CCMP + setKey( dev, + 1, //EntryNo + ipw->u.crypt.idx,//KeyIndex + KEY_TYPE_CCMP, //KeyType + broadcast_addr, //MacAddr + 0, //DefaultKey + key); //KeyContent + } + else if( ((u32*)wrq->u.data.pointer)[3]==0x656e6f6e ){//none + //do nothing + } + else if( ((u32*)wrq->u.data.pointer)[3]==0x504557 ){//WEP + setKey( dev, + 1, //EntryNo + ipw->u.crypt.idx,//KeyIndex + KEY_TYPE_WEP40, //KeyType + broadcast_addr, //MacAddr + 0, //DefaultKey + key); //KeyContent + } + else printk("undefine group key type: %8x\n", ((u32*)wrq->u.data.pointer)[3]); + } + + } +#endif /*JOHN_TKIP*/ + + +#ifdef JOHN_HWSEC_DEBUG + { + int i; + //john's test 0711 + printk("@@ wrq->u pointer = "); + for(i=0;iu.data.length;i++){ + if(i%10==0) printk("\n"); + printk( "%8x|", ((u32*)wrq->u.data.pointer)[i] ); + } + printk("\n"); + } +#endif /*JOHN_HWSEC_DEBUG*/ + ret = ieee80211_wpa_supplicant_ioctl(priv->ieee80211, &wrq->u.data); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + up(&priv->wx_sem); + + return ret; +} + + +struct tx_desc { + +#ifdef _LINUX_BYTEORDER_LITTLE_ENDIAN_H + + +//dword 0 +unsigned int tpktsize:12; +unsigned int rsvd0:3; +unsigned int no_encrypt:1; +unsigned int splcp:1; +unsigned int morefrag:1; +unsigned int ctsen:1; +unsigned int rtsrate:4; +unsigned int rtsen:1; +unsigned int txrate:4; +unsigned int last:1; +unsigned int first:1; +unsigned int dmaok:1; +unsigned int own:1; + +//dword 1 +unsigned short rtsdur; +unsigned short length:15; +unsigned short l_ext:1; + +//dword 2 +unsigned int bufaddr; + + +//dword 3 +unsigned short rxlen:12; +unsigned short rsvd1:3; +unsigned short miccal:1; +unsigned short dur; + +//dword 4 +unsigned int nextdescaddr; + +//dword 5 +unsigned char rtsagc; +unsigned char retrylimit; +unsigned short rtdb:1; +unsigned short noacm:1; +unsigned short pifs:1; +unsigned short rsvd2:4; +unsigned short rtsratefallback:4; +unsigned short ratefallback:5; + +//dword 6 +unsigned short delaybound; +unsigned short rsvd3:4; +unsigned short agc:8; +unsigned short antenna:1; +unsigned short spc:2; +unsigned short rsvd4:1; + +//dword 7 +unsigned char len_adjust:2; +unsigned char rsvd5:1; +unsigned char tpcdesen:1; +unsigned char tpcpolarity:2; +unsigned char tpcen:1; +unsigned char pten:1; + +unsigned char bckey:6; +unsigned char enbckey:1; +unsigned char enpmpd:1; +unsigned short fragqsz; + + +#else + +#error "please modify tx_desc to your own\n" + +#endif + + +} __attribute__((packed)); + +struct rx_desc_rtl8187b { + +#ifdef _LINUX_BYTEORDER_LITTLE_ENDIAN_H + +//dword 0 +unsigned int rxlen:12; +unsigned int icv:1; +unsigned int crc32:1; +unsigned int pwrmgt:1; +unsigned int res:1; +unsigned int bar:1; +unsigned int pam:1; +unsigned int mar:1; +unsigned int qos:1; +unsigned int rxrate:4; +unsigned int trsw:1; +unsigned int splcp:1; +unsigned int fovf:1; +unsigned int dmafail:1; +unsigned int last:1; +unsigned int first:1; +unsigned int eor:1; +unsigned int own:1; + + +//dword 1 +unsigned int tsftl; + + +//dword 2 +unsigned int tsfth; + + +//dword 3 +unsigned char sq; +unsigned char rssi:7; +unsigned char antenna:1; + +unsigned char agc; +unsigned char decrypted:1; +unsigned char wakeup:1; +unsigned char shift:1; +unsigned char rsvd0:5; + +//dword 4 +unsigned int num_mcsi:4; +unsigned int snr_long2end:6; +unsigned int cfo_bias:6; + +int pwdb_g12:8; +unsigned int fot:8; + + + + +#else + +#error "please modify tx_desc to your own\n" + +#endif + +}__attribute__((packed)); + + + +struct rx_desc_rtl8187 { + +#ifdef _LINUX_BYTEORDER_LITTLE_ENDIAN_H + +//dword 0 +unsigned int rxlen:12; +unsigned int icv:1; +unsigned int crc32:1; +unsigned int pwrmgt:1; +unsigned int res:1; +unsigned int bar:1; +unsigned int pam:1; +unsigned int mar:1; +unsigned int qos:1; +unsigned int rxrate:4; +unsigned int trsw:1; +unsigned int splcp:1; +unsigned int fovf:1; +unsigned int dmafail:1; +unsigned int last:1; +unsigned int first:1; +unsigned int eor:1; +unsigned int own:1; + +//dword 1 +unsigned char sq; +unsigned char rssi:7; +unsigned char antenna:1; + +unsigned char agc; +unsigned char decrypted:1; +unsigned char wakeup:1; +unsigned char shift:1; +unsigned char rsvd0:5; + +//dword 2 +unsigned int tsftl; + +//dword 3 +unsigned int tsfth; + + + +#else + +#error "please modify tx_desc to your own\n" + +#endif + + +}__attribute__((packed)); + + + +union rx_desc { + +struct rx_desc_rtl8187b desc_87b; +struct rx_desc_rtl8187 desc_87; + +}__attribute__((packed)); + +// +// Description: +// Perform signal smoothing for dynamic mechanism. +// This is different with PerformSignalSmoothing8187 in smoothing fomula. +// No dramatic adjustion is apply because dynamic mechanism need some degree +// of correctness. +// 2007.01.23, by shien chang. +// +void PerformUndecoratedSignalSmoothing8187(struct net_device *dev, struct ieee80211_rx_stats *stats) +{ + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + bool bCckRate = rtl8180_IsWirelessBMode(rtl8180_rate2rate(stats->rate)); + + if(NIC_8187 == priv->card_8187) { + if(priv->UndecoratedSmoothedSS >= 0) { + priv->UndecoratedSmoothedSS = ((priv->UndecoratedSmoothedSS * 50) + (stats->signalstrength * 11)) / 60; + }else{ + priv->UndecoratedSmoothedSS = stats->signalstrength; + } + } else { + // Determin the current packet is CCK rate, by Bruce, 2007-04-12. + priv->bCurCCKPkt = bCckRate; + + // Tesing for SD3 DZ, by Bruce, 2007-04-11. + if(priv->UndecoratedSmoothedSS >= 0) { + priv->UndecoratedSmoothedSS = ((priv->UndecoratedSmoothedSS * 5) + (stats->signalstrength * 10)) / 6; + }else{ + priv->UndecoratedSmoothedSS = stats->signalstrength * 10; + } + + // + // Bacause the AGC parameter is not exactly correct under high power (AGC saturation), we need to record the RSSI value to be + // referenced by DoRxHighPower. It is not necessary to record this value when this packet is sent by OFDM rate. + // Advised by SD3 DZ, by Bruce, 2007-04-12. + // + if(bCckRate){ + priv->CurCCKRSSI = stats->signal; + }else{ + priv->CurCCKRSSI = 0; + } + } + //printk("Sommthing SignalSterngth (%d) => UndecoratedSmoothedSS (%d)\n", stats->signalstrength, priv->UndecoratedSmoothedSS); +} + +#ifdef THOMAS_SKB +void rtl8180_irq_rx_tasklet(struct r8180_priv *priv) +{ + int status,len,flen; + +#ifdef SW_ANTE_DIVERSITY + u8 Antenna = 0; +#endif + u32 SignalStrength = 0; + u32 quality = 0; + bool bCckRate = false; + char RX_PWDB = 0; + long RecvSignalPower=0; + struct sk_buff *skb; + struct sk_buff *skb2;//skb for check out of memory + union rx_desc *desc; + //struct urb *rx_urb = priv->rxurb_task; + struct ieee80211_hdr *hdr;//by amy + u16 fc,type; + u8 bHwError=0,bCRC=0,bICV=0; + long SignalStrengthIndex = 0; + struct ieee80211_rx_stats stats = { + .signal = 0, + .noise = -98, + .rate = 0, + //.mac_time = jiffies, + .freq = IEEE80211_24GHZ_BAND, + }; + + int *prx_inx=&priv->rx_inx; + struct urb *rx_urb=priv->rx_urb[*prx_inx]; //changed by jackson + struct net_device *dev = (struct net_device*)rx_urb->context; + //DMESG("=====>RX %x ",rx_urb->status); + + skb = priv->pp_rxskb[*prx_inx]; + status = rx_urb->status; + skb2 = dev_alloc_skb(RX_URB_SIZE); + + if (skb2 == NULL){ + printk(KERN_ALERT "No Skb For RX!/n"); + //rx_urb->transfer_buffer = skb->data; + //priv->pp_rxskb[*prx_inx] = skb; + } else { + + if(status == 0) + { + if(NIC_8187B == priv->card_8187) + { + stats.nic_type = NIC_8187B; + len = rx_urb->actual_length; + len -= sizeof (struct rx_desc_rtl8187b); + desc = (union rx_desc *)(rx_urb->transfer_buffer + len); + flen = desc->desc_87b.rxlen ; + + if( flen <= rx_urb->actual_length){ +#if 1 +#ifdef SW_ANTE_DIVERSITY + Antenna = desc->desc_87b.antenna; +#endif + stats.mac_time[0] = desc->desc_87b.tsftl; + stats.mac_time[1] = desc->desc_87b.tsfth; + + stats.signal = desc->desc_87b.rssi; + //stats.noise = desc->desc_87b.sq; + quality = desc->desc_87b.sq; + stats.rate = desc->desc_87b.rxrate; + bCckRate = rtl8180_IsWirelessBMode(rtl8180_rate2rate(stats.rate)); + + if(!bCckRate) { // OFDM rate. + if(desc->desc_87b.pwdb_g12 < -106) + SignalStrength = 0; + else + SignalStrength = desc->desc_87b.pwdb_g12 + 106; + RX_PWDB = (desc->desc_87b.pwdb_g12)/2 -42; + } else { // CCK rate. + if(desc->desc_87b.agc> 174) + SignalStrength = 0; + else + SignalStrength = 174 - desc->desc_87b.agc; + RX_PWDB = ((desc->desc_87b.agc)/2)*(-1) - 8; + } + + //lzm mod 081028 based on windows driver + //compensate SignalStrength when switch TR to SW controled + if(priv->TrSwitchState == TR_SW_TX) { + SignalStrength = SignalStrength + 54; + RX_PWDB = RX_PWDB + 27; + } + + if(SignalStrength > 100) + SignalStrength = 100; + SignalStrength = (SignalStrength * 70) / 100 + 30; + + if(SignalStrength > 50) + SignalStrength = SignalStrength + 10; + if(SignalStrength > 100) + SignalStrength = 100; + + RecvSignalPower = RX_PWDB; + //printk("SignalStrength = %d \n",SignalStrength); + bHwError = (desc->desc_87b.fovf | desc->desc_87b.icv | desc->desc_87b.crc32); + bCRC = desc->desc_87b.crc32; + bICV = desc->desc_87b.icv; + priv->wstats.qual.level = (u8)SignalStrength; + + if(!bCckRate){ + if (quality > 127) + quality = 0; + else if (quality <27) + quality = 100; + else + quality = 127 - quality; + } else { + if(quality > 64) + quality = 0; + else + quality = ((64-quality)*100)/64; + } + + + priv ->wstats.qual.qual = quality; + priv->wstats.qual.noise = 100 - priv ->wstats.qual.qual; + + stats.signalstrength = (u8)SignalStrength; + stats.signal = (u8)quality; + stats.noise = desc->desc_87b.snr_long2end; + + skb_put(skb,flen-4); + + priv->stats.rxok++; + //by amy + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + + if((IEEE80211_FTYPE_CTL != type) && + (eqMacAddr(priv->ieee80211->current_network.bssid, (fc & IEEE80211_FCTL_TODS)? hdr->addr1 : (fc & IEEE80211_FCTL_FROMDS )? hdr->addr2 : hdr->addr3)) && (!bHwError) && (!bCRC)&& (!bICV)) + { + // Perform signal smoothing for dynamic mechanism on demand. + // This is different with PerformSignalSmoothing8187 in smoothing fomula. + // No dramatic adjustion is apply because dynamic mechanism need some degree + // of correctness. 2007.01.23, by shien chang. + PerformUndecoratedSignalSmoothing8187(dev, &stats); + + //Update signal strength and realted into private RxStats for UI query. + SignalStrengthIndex = NetgearSignalStrengthTranslate(priv->LastSignalStrengthInPercent, priv->wstats.qual.level); + priv->LastSignalStrengthInPercent = SignalStrengthIndex; + priv->SignalStrength = TranslateToDbm8187((u8)SignalStrengthIndex); + priv->SignalQuality = (priv->SignalQuality*5+quality+5)/6; + priv->RecvSignalPower = (priv->RecvSignalPower * 5 + RecvSignalPower - 1) / 6; +#ifdef SW_ANTE_DIVERSITY + priv->LastRxPktAntenna = Antenna ? 1:0; + SwAntennaDiversityRxOk8185(dev, SignalStrength); +#endif + } + //by amy +#endif + if(!ieee80211_rx(priv->ieee80211,skb, &stats)) { + dev_kfree_skb_any(skb); + } + }else { + priv->stats.rxurberr++; + printk("URB Error flen:%d actual_length:%d\n", flen , rx_urb->actual_length); + dev_kfree_skb_any(skb); + } + } else { + stats.nic_type = NIC_8187; + len = rx_urb->actual_length; + len -= sizeof (struct rx_desc_rtl8187); + desc = (union rx_desc *)(rx_urb->transfer_buffer + len); + flen = desc->desc_87.rxlen ; + + if(flen <= rx_urb->actual_length){ + stats.signal = desc->desc_87.rssi; + stats.noise = desc->desc_87.sq; + stats.rate = desc->desc_87.rxrate; + stats.mac_time[0] = desc->desc_87.tsftl; + stats.mac_time[1] = desc->desc_87.tsfth; + SignalStrength = (desc->desc_87.agc&0xfe) >> 1; + if( ((stats.rate <= 22) && (stats.rate != 12) && (stats.rate != 18)) || (stats.rate == 44) )//need to translate to real rate here + bCckRate= TRUE; + if (!bCckRate) + { + if (SignalStrength > 90) SignalStrength = 90; + else if (SignalStrength < 25) SignalStrength = 25; + SignalStrength = ((90 - SignalStrength)*100)/65; + } + else + { + if (SignalStrength >95) SignalStrength = 95; + else if (SignalStrength < 30) SignalStrength = 30; + SignalStrength = ((95 - SignalStrength)*100)/65; + } + stats.signalstrength = (u8)SignalStrength; + + skb_put(skb,flen-4); + + priv->stats.rxok++; + + if(!ieee80211_rx(priv->ieee80211,skb, &stats)) + dev_kfree_skb_any(skb); + + + }else { + priv->stats.rxurberr++; + printk("URB Error flen:%d actual_length:%d\n", flen , rx_urb->actual_length); + dev_kfree_skb_any(skb); + } + } + }else{ + + //printk("RX Status Error!\n"); + priv->stats.rxstaterr++; + priv->ieee80211->stats.rx_errors++; + dev_kfree_skb_any(skb); + + } + + rx_urb->transfer_buffer = skb2->data; + + priv->pp_rxskb[*prx_inx] = skb2; + } + + if(status != -ENOENT ){ + rtl8187_rx_urbsubmit(dev,rx_urb); + } else { + priv->pp_rxskb[*prx_inx] = NULL; + dev_kfree_skb_any(skb2); + //printk("RX process %d aborted due to explicit shutdown (%x)(%d)\n ", *prx_inx, status, status); + } + + if (*prx_inx == (MAX_RX_URB -1)) + *prx_inx = 0; + else + *prx_inx = *prx_inx + 1; +} +#endif + +#ifdef THOMAS_TASKLET +void rtl8180_irq_rx_tasklet_new(struct r8180_priv *priv){ + unsigned long flags; + while( atomic_read( &priv->irt_counter ) ){ + spin_lock_irqsave(&priv->irq_lock,flags);//added by thomas + rtl8180_irq_rx_tasklet(priv); + spin_unlock_irqrestore(&priv->irq_lock,flags);//added by thomas + if(atomic_read(&priv->irt_counter) >= 1) + atomic_dec( &priv->irt_counter ); + } +} +#endif +/**************************************************************************** + ---------------------------- USB_STUFF--------------------------- +*****************************************************************************/ + +static const struct net_device_ops rtl8187_netdev_ops = { + .ndo_open = rtl8180_open, + .ndo_stop = rtl8180_close, + .ndo_tx_timeout = tx_timeout, + .ndo_do_ioctl = rtl8180_ioctl, + .ndo_set_multicast_list = r8180_set_multicast, + .ndo_set_mac_address = r8180_set_mac_adr, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, + .ndo_start_xmit = ieee80211_xmit, + .ndo_get_stats = rtl8180_stats, +}; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static int __devinit rtl8187_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +#else +static void * __devinit rtl8187_usb_probe(struct usb_device *udev, + unsigned int ifnum, + const struct usb_device_id *id) +#endif +{ + struct net_device *dev = NULL; + struct r8180_priv *priv= NULL; + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct usb_device *udev = interface_to_usbdev(intf); +#endif + + dev = alloc_ieee80211(sizeof(struct r8180_priv)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + SET_MODULE_OWNER(dev); +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + usb_set_intfdata(intf, dev); + SET_NETDEV_DEV(dev, &intf->dev); +#endif + priv = ieee80211_priv(dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + priv->ieee80211 = netdev_priv(dev); +#else + priv->ieee80211 = (struct net_device *)dev->priv; +#endif + priv->udev=udev; + +#ifdef CONFIG_PM + udev->reset_resume = 1; +#endif + +#ifdef CPU_64BIT + priv->usb_buf = kmalloc(0x200, GFP_KERNEL); + priv->usb_pool = dma_pool_create("rtl8187b", NULL, 64, 64, 0); +#endif +//lzm add for write time out test +#ifdef DEBUG_RW_REGISTER + { + int reg_index = 0; + for(reg_index = 0; reg_index <= 199; reg_index++) + { + priv->write_read_registers[reg_index].address = 0; + priv->write_read_registers[reg_index].content = 0; + priv->write_read_registers[reg_index].flag = 0; + } + priv->write_read_register_index = 0; + } +#endif + //init netdev_ops, added by falcon.... + dev->netdev_ops = &rtl8187_netdev_ops; + + dev->wireless_handlers = &r8180_wx_handlers_def; +#if WIRELESS_EXT >= 12 +#if WIRELESS_EXT < 17 + dev->get_wireless_stats = r8180_get_wireless_stats; +#endif + dev->wireless_handlers = (struct iw_handler_def *) &r8180_wx_handlers_def; +#endif + + dev->type=ARPHRD_ETHER; + dev->watchdog_timeo = HZ*3; //modified by john, 0805 + + if (dev_alloc_name(dev, ifname) < 0){ + DMESG("Oops: devname already taken! Trying wlan%%d...\n"); + ifname = "wlan%d"; + dev_alloc_name(dev, ifname); + } + + if(rtl8180_init(dev)!=0){ + DMESG("Initialization failed"); + goto fail; + } + + netif_carrier_off(dev); + register_netdev(dev); + netif_stop_queue(dev); + + rtl8180_proc_init_one(dev); + + /* init rfkill */ + r8187b_rfkill_init(dev); + + DMESG("Driver probe completed"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + return dev; +#else + return 0; +#endif + + +fail: + free_ieee80211(dev); + + DMESG("wlan driver load failed\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + return NULL; +#else + return -ENODEV; +#endif + +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +static void __devexit rtl8187_usb_disconnect(struct usb_interface *intf) +#else +static void __devexit rtl8187_usb_disconnect(struct usb_device *udev, void *ptr) +#endif +{ + struct r8180_priv *priv = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + struct net_device *dev = usb_get_intfdata(intf); +#else + struct net_device *dev = (struct net_device *)ptr; +#endif + if(dev){ + unregister_netdev(dev); + + priv=ieee80211_priv(dev); + + MgntActSet_RF_State(dev, eRfOff, RF_CHANGE_BY_SW); + +#ifdef _RTL8187_EXT_PATCH_ + if(priv && priv->mshobj) + { + if(priv->mshobj->ext_patch_remove_proc) + priv->mshobj->ext_patch_remove_proc(priv); + priv->ieee80211->ext_patch_ieee80211_start_protocol = 0; + priv->ieee80211->ext_patch_ieee80211_stop_protocol = 0; + priv->ieee80211->ext_patch_ieee80211_probe_req_1 = 0; + priv->ieee80211->ext_patch_ieee80211_probe_req_2 = 0; + priv->ieee80211->ext_patch_ieee80211_association_req_1 = 0; + priv->ieee80211->ext_patch_ieee80211_association_req_2 = 0; + priv->ieee80211->ext_patch_ieee80211_assoc_resp_by_net_1 = 0; + priv->ieee80211->ext_patch_ieee80211_assoc_resp_by_net_2 = 0; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_auth =0; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_deauth =0; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_assoc_req = 0; + priv->ieee80211->ext_patch_ieee80211_rx_frame_softmac_on_assoc_rsp = 0; + priv->ieee80211->ext_patch_ieee80211_ext_stop_scan_wq_set_channel = 0; + priv->ieee80211->ext_patch_ieee80211_process_probe_response_1 = 0; + priv->ieee80211->ext_patch_ieee80211_rx_mgt_on_probe_req = 0; + priv->ieee80211->ext_patch_ieee80211_rx_mgt_update_expire = 0; + priv->ieee80211->ext_patch_ieee80211_rx_on_rx = 0; + priv->ieee80211->ext_patch_get_beacon_get_probersp = 0; + priv->ieee80211->ext_patch_ieee80211_xmit = 0; + priv->ieee80211->ext_patch_ieee80211_rx_frame_get_hdrlen = 0; + priv->ieee80211->ext_patch_ieee80211_rx_is_valid_framectl = 0; + priv->ieee80211->ext_patch_ieee80211_rx_process_dataframe = 0; + // priv->ieee80211->ext_patch_is_duplicate_packet = 0; + priv->ieee80211->ext_patch_ieee80211_softmac_xmit_get_rate = 0; + free_mshobj(&priv->mshobj); + } +#endif // _RTL8187_EXT_PATCH_ + + rtl8180_proc_remove_one(dev); + + rtl8180_down(dev); + priv->rf_close(dev); + + //rtl8180_rtx_disable(dev); + rtl8187_usb_deleteendpoints(dev); +#ifdef LED + DeInitSwLeds(dev); +#endif + rtl8180_irq_disable(dev); + rtl8180_reset(dev); + mdelay(10); + + } + +#ifdef CPU_64BIT + if(priv->usb_buf) + kfree(priv->usb_buf); + if(priv->usb_pool) { + dma_pool_destroy(priv->usb_pool); + priv->usb_pool = NULL; + } +#endif + free_ieee80211(dev); + DMESG("wlan driver removed"); +} + +/* fun with the built-in ieee80211 stack... */ +extern int ieee80211_crypto_init(void); +extern void ieee80211_crypto_deinit(void); +extern int ieee80211_crypto_tkip_init(void); +extern void ieee80211_crypto_tkip_exit(void); +extern int ieee80211_crypto_ccmp_init(void); +extern void ieee80211_crypto_ccmp_exit(void); +extern int ieee80211_crypto_wep_init(void); +extern void ieee80211_crypto_wep_exit(void); + +static int __init rtl8187_usb_module_init(void) +{ + int ret; + + ret = ieee80211_crypto_init(); + if (ret) { + printk(KERN_ERR "ieee80211_crypto_init() failed %d\n", ret); + return ret; + } + ret = ieee80211_crypto_tkip_init(); + if (ret) { + printk(KERN_ERR "ieee80211_crypto_tkip_init() failed %d\n", ret); + return ret; + } + ret = ieee80211_crypto_ccmp_init(); + if (ret) { + printk(KERN_ERR "ieee80211_crypto_ccmp_init() failed %d\n", ret); + return ret; + } + ret = ieee80211_crypto_wep_init(); + if (ret) { + printk(KERN_ERR "ieee80211_crypto_wep_init() failed %d\n", ret); + return ret; + } + + printk("\nLinux kernel driver for RTL8187/RTL8187B based WLAN cards\n"); + printk("Copyright (c) 2004-2008, Realsil Wlan\n"); + DMESG("Initializing module"); + DMESG("Wireless extensions version %d", WIRELESS_EXT); + rtl8180_proc_module_init(); + return usb_register(&rtl8187_usb_driver); +} + +static void __exit rtl8187_usb_module_exit(void) +{ + r8187b_rfkill_exit(); + usb_deregister(&rtl8187_usb_driver); + rtl8180_proc_module_remove(); + ieee80211_crypto_tkip_exit(); + ieee80211_crypto_ccmp_exit(); + ieee80211_crypto_wep_exit(); + ieee80211_crypto_deinit(); + + DMESG("Exiting\n"); +} + + +void rtl8180_try_wake_queue(struct net_device *dev, int pri) +{ + unsigned long flags; + short enough_desc; + struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); + + spin_lock_irqsave(&priv->tx_lock,flags); + enough_desc = check_nic_enought_desc(dev,pri); + spin_unlock_irqrestore(&priv->tx_lock,flags); + + if(enough_desc) + ieee80211_wake_queue(priv->ieee80211); +} + +#ifdef JOHN_HWSEC +void EnableHWSecurityConfig8187(struct net_device *dev) +{ + u8 SECR_value = 0x0; + SECR_value = SCR_TxSecEnable | SCR_RxSecEnable; + { + write_nic_byte(dev, WPA_CONFIG, 0x7);//SECR_value | SCR_UseDK ); + } +} + +void setKey(struct net_device *dev, + u8 EntryNo, + u8 KeyIndex, + u16 KeyType, + u8 *MacAddr, + u8 DefaultKey, + u32 *KeyContent ) +{ + u32 TargetCommand = 0; + u32 TargetContent = 0; + u16 usConfig = 0; + int i; + usConfig |= BIT15 | (KeyType<<2) | (DefaultKey<<5) | KeyIndex; + + + for(i=0 ; i<6 ; i++){ + TargetCommand = i+6*EntryNo; + TargetCommand |= BIT31|BIT16; + + if(i==0){//MAC|Config + TargetContent = (u32)(*(MacAddr+0)) << 16| + (u32)(*(MacAddr+1)) << 24| + (u32)usConfig; + + write_nic_dword(dev, WCAMI, TargetContent); + write_nic_dword(dev, RWCAM, TargetCommand); + //printk("setkey cam =%8x\n", read_cam(dev, i+6*EntryNo)); + } else if(i==1){//MAC + TargetContent = (u32)(*(MacAddr+2)) | + (u32)(*(MacAddr+3)) << 8| + (u32)(*(MacAddr+4)) << 16| + (u32)(*(MacAddr+5)) << 24; + write_nic_dword(dev, WCAMI, TargetContent); + write_nic_dword(dev, RWCAM, TargetCommand); + } else { //Key Material + write_nic_dword(dev, WCAMI, (u32)(*(KeyContent+i-2)) ); + write_nic_dword(dev, RWCAM, TargetCommand); + } + } + +} +#endif + +/**************************************************************************** + --------------------------- RF power on/power off ----------------- +*****************************************************************************/ + +/* + * the interface for changing the rfkill state + * @dev: the device of r8187b + * @eRfPowerStateToSet: the state we need to change, + * eRfOn: power on + * eRfOff: power off + * + * This function should be called by the SCI interrupt handler when the + * KEY_WLAN event happen(or install to the notify function of the SCI + * interrupt) or called in the wifi_set function of the rfkill interface for + * user-space, and also, it can be called to initialize the wifi state, and + * called when suspend/resume. + */ + +void r8187b_wifi_change_rfkill_state(struct net_device *dev, RT_RF_POWER_STATE eRfPowerStateToSet) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + if (eRfPowerStateToSet == eRfOn) + priv->ieee80211->bHwRadioOff = false; + else + priv->ieee80211->bHwRadioOff = true; + +#ifdef CONFIG_RADIO_DEBUG + DMESG("SCI interrupt Methord Will Turn Radio %s", + (priv->ieee80211->bHwRadioOff == true) ? "Off" : "On"); +#endif + +#ifdef LED //by lizhaoming + if (priv->ieee80211->bHwRadioOff) + priv->ieee80211->ieee80211_led_contorl(dev, LED_CTL_POWER_OFF); + else + priv->ieee80211->ieee80211_led_contorl(dev, LED_CTL_POWER_ON); +#endif + + MgntActSet_RF_State(dev, eRfPowerStateToSet, RF_CHANGE_BY_HW); + + /* report the rfkill state to the user-space via uevent interface */ + r8187b_wifi_report_state(priv); +} + +/*************************************************************************** + ------------------- module init / exit stubs ---------------- +****************************************************************************/ +module_init(rtl8187_usb_module_init); +module_exit(rtl8187_usb_module_exit); diff --git a/drivers/net/wireless/rtl8187b/r8187_led.c b/drivers/net/wireless/rtl8187b/r8187_led.c new file mode 100644 index 0000000..dba3b5a --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8187_led.c @@ -0,0 +1,1629 @@ +/*++ +Copyright (c) Realtek Semiconductor Corp. All rights reserved. + +Module Name: + r8187_led.c + +Abstract: + RTL8187 LED control functions + +Major Change History: + When Who What + ---------- --------------- ------------------------------- + 2006-09-07 Xiong Created + +Notes: + +--*/ + +/*--------------------------Include File------------------------------------*/ +#include "ieee80211/ieee80211.h" +#include "r8180_hw.h" +#include "r8187.h" +#include "r8180_93cx6.h" +#include "r8187_led.h" + +/** +* +* Initialization function for Sw Leds controll. +* +* \param dev The net device for this driver. +* \return void. +* +* Note: +* +*/ + +void +InitSwLeds( + struct net_device *dev + ) +{ + + struct r8180_priv *priv = ieee80211_priv(dev); + u16 usValue; +// printk("========>%s()\n", __FUNCTION__); + +// priv->CustomerID = RT_CID_87B_DELL; //by lizhaoming for DELL 2008.6.3 + priv->CustomerID = RT_CID_DEFAULT; //just set to default now + priv->bEnableLedCtrl = 1; + priv->PsrValue = read_nic_byte(dev, PSR); + usValue = eprom_read(dev, EEPROM_SW_REVD_OFFSET >> 1); + priv->EEPROMCustomerID = (u8)( usValue & EEPROM_CID_MASK ); + DMESG("EEPROM Customer ID: %02X", priv->EEPROMCustomerID); + + if(priv->CustomerID == RT_CID_DEFAULT) + { // If we have not yet change priv->CustomerID in register, + // we initialzie it from that of EEPROM with proper translation, 2006.07.03, by rcnjko. + switch(priv->EEPROMCustomerID) + { + case EEPROM_CID_RSVD0: + case EEPROM_CID_RSVD1: + priv->CustomerID = RT_CID_DEFAULT; + break; + + case EEPROM_CID_ALPHA0: + priv->CustomerID = RT_CID_8187_ALPHA0; + break; + + case EEPROM_CID_SERCOMM_PS: + priv->CustomerID = RT_CID_8187_SERCOMM_PS; + break; + + case EEPROM_CID_HW_LED: + priv->CustomerID = RT_CID_8187_HW_LED; + break; + + case EEPROM_CID_QMI: + priv->CustomerID = RT_CID_87B_QMI; + break; + + case EEPROM_CID_DELL: + priv->CustomerID = RT_CID_87B_DELL; + break; + + default: + // Invalid value, so, we use default value instead. + priv->CustomerID = RT_CID_DEFAULT; + break; + } + } + switch(priv->CustomerID) + { + case RT_CID_DEFAULT: + priv->LedStrategy = SW_LED_MODE0; + break; + + case RT_CID_8187_ALPHA0: + priv->LedStrategy = SW_LED_MODE1; + break; + + case RT_CID_8187_SERCOMM_PS: + priv->LedStrategy = SW_LED_MODE3; + break; + + case RT_CID_87B_QMI: + priv->LedStrategy = SW_LED_MODE4; + break; + + case RT_CID_87B_DELL: + priv->LedStrategy = SW_LED_MODE5; + break; + + case RT_CID_8187_HW_LED: + priv->LedStrategy = HW_LED; + break; + + default: + priv->LedStrategy = SW_LED_MODE0; + break; + } + + InitLed8187(dev, + &(priv->Gpio0Led), + LED_PIN_GPIO0, + Gpio0LedBlinkTimerCallback); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&priv->Gpio0LedWorkItem, + (void(*)(void*))Gpio0LedWorkItemCallback, dev); + + InitLed8187(dev, + &(priv->SwLed0), + LED_PIN_LED0, + SwLed0BlinkTimerCallback); + INIT_WORK(&priv->SwLed0WorkItem, + (void(*)(void*))SwLed0WorkItemCallback, dev); + + InitLed8187(dev, + &(priv->SwLed1), + LED_PIN_LED1, + SwLed1BlinkTimerCallback); + INIT_WORK(&priv->SwLed1WorkItem, + (void(*)(void*))SwLed1WorkItemCallback, dev); +#else +INIT_WORK(&priv->Gpio0LedWorkItem, + Gpio0LedWorkItemCallback); + + InitLed8187(dev, + &(priv->SwLed0), + LED_PIN_LED0, + SwLed0BlinkTimerCallback); + INIT_WORK(&priv->SwLed0WorkItem, + SwLed0WorkItemCallback); + + InitLed8187(dev, + &(priv->SwLed1), + LED_PIN_LED1, + SwLed1BlinkTimerCallback); + INIT_WORK(&priv->SwLed1WorkItem, + SwLed1WorkItemCallback); +#endif +} + +void +DeInitSwLeds( + struct net_device *dev + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + +// printk("=========>%s In\n", __FUNCTION__); + DeInitLed8187(dev, &(priv->Gpio0Led)); + DeInitLed8187(dev, &(priv->SwLed0)); + DeInitLed8187(dev, &(priv->SwLed1)); +} + +void +InitLed8187( + struct net_device *dev, + PLED_8187 pLed, + LED_PIN_8187 LedPin, + void * BlinkCallBackFunc) +{ +// printk("=========>%s In\n", __FUNCTION__); + pLed->LedPin = LedPin; + + pLed->bLedOn = 0; + pLed->CurrLedState = LED_OFF; + + pLed->bLedBlinkInProgress = 0; + pLed->BlinkTimes = 0; + pLed->BlinkingLedState = LED_OFF; + + init_timer(&(pLed->BlinkTimer)); + pLed->BlinkTimer.data = (unsigned long)dev; + pLed->BlinkTimer.function = BlinkCallBackFunc; + //PlatformInitializeTimer(dev, &(pLed->BlinkTimer), BlinkCallBackFunc); +} + +void +DeInitLed8187( + struct net_device *dev, + PLED_8187 pLed) +{ + //printk("=========>%s In\n", __FUNCTION__); + //PlatformCancelTimer(dev, &(pLed->BlinkTimer)); + del_timer_sync(&(pLed->BlinkTimer)); + // We should reset bLedBlinkInProgress if we cancel the LedControlTimer, 2005.03.10, by rcnjko. + pLed->bLedBlinkInProgress = 0; +} + +void +LedControl8187( + struct net_device *dev, + LED_CTL_MODE LedAction +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); +// printk("=========>%s In\n", __FUNCTION__); + if( priv->bEnableLedCtrl == 0) + return; + + + if( priv->eRFPowerState != eRfOn && + (LedAction == LED_CTL_TX || LedAction == LED_CTL_RX || + LedAction == LED_CTL_SITE_SURVEY || + LedAction == LED_CTL_LINK || + LedAction == LED_CTL_NO_LINK) ) + { + return; + } + + + switch(priv->LedStrategy) + { + case SW_LED_MODE0: + SwLedControlMode0(dev, LedAction); + break; + + case SW_LED_MODE1: + SwLedControlMode1(dev, LedAction); + break; + + case SW_LED_MODE2: + SwLedControlMode2(dev, LedAction); + break; + + case SW_LED_MODE3: + SwLedControlMode3(dev, LedAction); + break; + case SW_LED_MODE4: + SwLedControlMode4(dev, LedAction); + break; + + case SW_LED_MODE5: + SwLedControlMode5(dev, LedAction); + break; + + default: + break; + } +} + + +// +// Description: +// Implement each led action for SW_LED_MODE0. +// This is default strategy. +// +void +SwLedControlMode0( + struct net_device *dev, + LED_CTL_MODE LedAction +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + PLED_8187 pLed = &(priv->Gpio0Led); + +// printk("===+++++++++++++++======>%s In\n", __FUNCTION__); + // Decide led state + switch(LedAction) + { + case LED_CTL_TX: + case LED_CTL_RX: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->CurrLedState = LED_BLINK_NORMAL; + pLed->BlinkTimes = 2; + // printk("===========>LED_CTL_TX/RX \n"); + } + else + { + return; + } + break; + + case LED_CTL_SITE_SURVEY: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->CurrLedState = LED_BLINK_SLOWLY; + // pLed->BlinkTimes = 10; + //printk("===========>LED_CTL_SURVEY \n"); + } + else + { + return; + } + break; + + case LED_CTL_LINK: + // printk("===========>associate commplite LED_CTL_LINK\n"); + pLed->CurrLedState = LED_ON; + break; + + case LED_CTL_NO_LINK: + pLed->CurrLedState = LED_OFF; + break; + + case LED_CTL_POWER_ON: + // printk("===========>LED_CTL_POWER_ON\n"); + pLed->CurrLedState = LED_POWER_ON_BLINK; + break; + + case LED_CTL_POWER_OFF: + pLed->CurrLedState = LED_OFF; + break; + + default: + return; + break; + } + + // Change led state. + switch(pLed->CurrLedState) + { + case LED_ON: + if( pLed->bLedBlinkInProgress == 0 ) + { + SwLedOn(dev, pLed); + } + break; + + case LED_OFF://modified by lizhaoming 2008.6.23 + // if( pLed->bLedBlinkInProgress == 0 ) + // { + // SwLedOff(dev, pLed); + // } + + if(pLed->bLedBlinkInProgress )/////////lizhaoming + { + del_timer_sync(&(pLed->BlinkTimer)); + pLed->bLedBlinkInProgress = FALSE; + } + SwLedOff(dev, pLed); + break; + + case LED_BLINK_NORMAL: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + if( pLed->bLedOn ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + } + break; + + case LED_BLINK_SLOWLY: + if( pLed->bLedBlinkInProgress == 0 ) + { + //printk("=======>%s SLOWLY\n", __func__); + pLed->bLedBlinkInProgress = 1; + // if( pLed->bLedOn ) + pLed->BlinkingLedState = LED_OFF;//for LED_SHIN is LED on + // else + // pLed->BlinkingLedState = LED_ON; + + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + } + break; + + case LED_POWER_ON_BLINK: + SwLedOn(dev, pLed); +#ifdef LED_SHIN + mdelay(100); + SwLedOff(dev, pLed); +#endif + break; + + default: + break; + } +} + +// +// Description: +// Implement each led action for SW_LED_MODE1. +// For example, this is applied by ALPHA. +// +void +SwLedControlMode1( + struct net_device *dev, + LED_CTL_MODE LedAction +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + PLED_8187 pLed0 = &(priv->SwLed0); + PLED_8187 pLed1 = &(priv->SwLed1); +// printk("=====++++++++++++++++++++++====>%s In\n", __FUNCTION__); + + switch(LedAction) + { + case LED_CTL_TX: + if( pLed0->bLedBlinkInProgress == 0 ) + { + pLed0->CurrLedState = LED_BLINK_NORMAL; + pLed0->BlinkTimes = 2; + pLed0->bLedBlinkInProgress = 1; + if( pLed0->bLedOn ) + pLed0->BlinkingLedState = LED_OFF; + else + pLed0->BlinkingLedState = LED_ON; + + //pLed0->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed0->BlinkTimer)); + mod_timer(&pLed0->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(dev, &(pLed0->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + } + break; + + case LED_CTL_LINK: + pLed0->CurrLedState = LED_ON; + if( pLed0->bLedBlinkInProgress == 0 ) + { + SwLedOn(dev, pLed0); + } + break; + + case LED_CTL_NO_LINK: + pLed0->CurrLedState = LED_OFF; + if( pLed0->bLedBlinkInProgress == 0 ) + { + SwLedOff(dev, pLed0); + } + break; + + case LED_CTL_POWER_ON: + pLed0->CurrLedState = LED_OFF; + SwLedOff(dev, pLed0); + + pLed1->CurrLedState = LED_ON; + SwLedOn(dev, pLed1); + + break; + + case LED_CTL_POWER_OFF: + pLed0->CurrLedState = LED_OFF; + SwLedOff(dev, pLed0); + + pLed1->CurrLedState = LED_OFF; + SwLedOff(dev, pLed1); + break; + + case LED_CTL_SITE_SURVEY: + if( pLed0->bLedBlinkInProgress == 0 ) + { + pLed0->CurrLedState = LED_BLINK_SLOWLY;; + pLed0->BlinkTimes = 10; + pLed0->bLedBlinkInProgress = 1; + if( pLed0->bLedOn ) + pLed0->BlinkingLedState = LED_OFF; + else + pLed0->BlinkingLedState = LED_ON; + + //pLed0->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL; + //add_timer(&(pLed0->BlinkTimer)); + mod_timer(&pLed0->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + //PlatformSetTimer(dev, &(pLed0->BlinkTimer), LED_BLINK_SLOWLY_INTERVAL); + } + break; + + default: + break; + } +} + +// +// Description: +// Implement each led action for SW_LED_MODE2, +// which is customized for AzWave 8187 minicard. +// 2006.04.03, by rcnjko. +// +void +SwLedControlMode2( + struct net_device *dev, + LED_CTL_MODE LedAction +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + PLED_8187 pLed = &(priv->Gpio0Led); + +// printk("====+++++++++++++++++++++=====>%s In\n", __FUNCTION__); + // Decide led state + switch(LedAction) + { + case LED_CTL_TX: + case LED_CTL_RX: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + + pLed->CurrLedState = LED_BLINK_NORMAL; + pLed->BlinkTimes = 2; + + if( pLed->bLedOn ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + } + break; + + case LED_CTL_SITE_SURVEY: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + + //if( dev->MgntInfo.mAssoc || + // dev->MgntInfo.mIbss ) + //{ + pLed->CurrLedState = LED_SCAN_BLINK; + pLed->BlinkTimes = 4; + //} + //else + //{ + // pLed->CurrLedState = LED_NO_LINK_BLINK; + // pLed->BlinkTimes = 24; + //} + + if( pLed->bLedOn ) + { + pLed->BlinkingLedState = LED_OFF; + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_ON_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM2_BLINK_ON_INTERVAL); + } + else + { + pLed->BlinkingLedState = LED_ON; + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_OFF_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_OFF_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM2_BLINK_OFF_INTERVAL); + } + } + else + { + if(pLed->CurrLedState != LED_NO_LINK_BLINK) + { + pLed->CurrLedState = LED_SCAN_BLINK; + /* + if( dev->MgntInfo.mAssoc || + dev->MgntInfo.mIbss ) + { + pLed->CurrLedState = LED_SCAN_BLINK; + } + else + { + pLed->CurrLedState = LED_NO_LINK_BLINK; + } + */ + } + } + break; + + case LED_CTL_NO_LINK: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + + pLed->CurrLedState = LED_NO_LINK_BLINK; + pLed->BlinkTimes = 24; + + if( pLed->bLedOn ) + { + pLed->BlinkingLedState = LED_OFF; + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_ON_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM2_BLINK_ON_INTERVAL); + } + else + { + pLed->BlinkingLedState = LED_ON; + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_OFF_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_OFF_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM2_BLINK_OFF_INTERVAL); + } + } + else + { + pLed->CurrLedState = LED_NO_LINK_BLINK; + } + break; + + case LED_CTL_LINK: + pLed->CurrLedState = LED_ON; + if( pLed->bLedBlinkInProgress == 0 ) + { + SwLedOn(dev, pLed); + } + break; + + case LED_CTL_POWER_OFF: + pLed->CurrLedState = LED_OFF; + if( pLed->bLedBlinkInProgress == 0 ) + { + SwLedOff(dev, pLed); + } + break; + + default: + break; + } +} + + +// +// Description: +// Implement each led action for SW_LED_MODE3, +// which is customized for Sercomm Printer Server case. +// 2006.04.21, by rcnjko. +// +void +SwLedControlMode3( + struct net_device *dev, + LED_CTL_MODE LedAction +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + PLED_8187 pLed = &(priv->Gpio0Led); + +// printk("=====+++++++++++++++++++====>%s In\n", __FUNCTION__); + // Decide led state + switch(LedAction) + { + case LED_CTL_TX: + case LED_CTL_RX: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + + pLed->CurrLedState = LED_BLINK_CM3; + pLed->BlinkTimes = 2; + + if( pLed->bLedOn ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_CM3_BLINK_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM3_BLINK_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM3_BLINK_INTERVAL); + } + break; + + case LED_CTL_SITE_SURVEY: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + + pLed->CurrLedState = LED_BLINK_CM3; + pLed->BlinkTimes = 10; + + if( pLed->bLedOn ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_CM3_BLINK_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM3_BLINK_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM3_BLINK_INTERVAL); + } + break; + + case LED_CTL_LINK: + pLed->CurrLedState = LED_ON; + if( pLed->bLedBlinkInProgress == 0 ) + { + SwLedOn(dev, pLed); + } + break; + + case LED_CTL_NO_LINK: + pLed->CurrLedState = LED_OFF; + if( pLed->bLedBlinkInProgress == 0 ) + { + SwLedOff(dev, pLed); + } + break; + + case LED_CTL_POWER_ON: + pLed->CurrLedState = LED_POWER_ON_BLINK; + SwLedOn(dev, pLed); + mdelay(100); + SwLedOff(dev, pLed); + break; + + case LED_CTL_POWER_OFF: + pLed->CurrLedState = LED_OFF; + if( pLed->bLedBlinkInProgress == 0 ) + { + SwLedOff(dev, pLed); + } + break; + + default: + break; + } +} + +// added by lizhaoming 2008.6.2 +// +// Description: +// Implement each led action for SW_LED_MODE4, +// which is customized for QMI 8187B minicard. +// 2008.04.21, by chiyokolin. +// +void +SwLedControlMode4( + struct net_device *dev, + LED_CTL_MODE LedAction + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + PLED_8187 pLed = &(priv->Gpio0Led); + + //printk("=====+++++++++++++++++++++====>%s In\n", __FUNCTION__); + // Decide led state + switch(LedAction) + { + case LED_CTL_TX: + case LED_CTL_RX: + //if( pLed->bLedBlinkInProgress == false && !priv->bScanInProgress)//????? + if( pLed->bLedBlinkInProgress == 0) + { + pLed->bLedBlinkInProgress = 1; + + pLed->CurrLedState = LED_BLINK_NORMAL; + pLed->BlinkTimes = 2; + + if( pLed->bLedOn ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + } + else + //printk("----->LED_CTL_RX/TX bLedBlinkInProgress\n"); + + break; + + case LED_CTL_SITE_SURVEY: + if( pLed->bLedBlinkInProgress == 0 ) + { + + pLed->bLedBlinkInProgress = 1; + //if( priv->MgntInfo.mAssoc || priv->MgntInfo.mIbss )//////////?????? + //{ + pLed->CurrLedState = LED_SCAN_BLINK; + pLed->BlinkTimes = 10; + + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + //} + //else + //{ + // pLed->CurrLedState = LED_NO_LINK_BLINK; + // pLed->BlinkTimes = 24; + // + // if( pLed->bLedOn ) + // { + // pLed->BlinkingLedState = LED_OFF; + // + // pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_ON_INTERVAL; + // add_timer(&(pLed->BlinkTimer)); + // //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_ON_INTERVAL); + // } + // else + // { + // pLed->BlinkingLedState = LED_ON; + + // pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_OFF_INTERVAL; + // add_timer(&(pLed->BlinkTimer)); + // //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_OFF_INTERVAL); + // } + //} + } + else + { + if(pLed->CurrLedState != LED_NO_LINK_BLINK) + { + //if( priv->MgntInfo.mAssoc || priv->MgntInfo.mIbss )//??????????? + //{ + //} + //else + //{ + // pLed->CurrLedState = LED_NO_LINK_BLINK; + //} + } + + //printk("----->LED_CTL_SITE_SURVEY bLedBlinkInProgress\n"); + } + break; + + case LED_CTL_NO_LINK: + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + + pLed->CurrLedState = LED_NO_LINK_BLINK; + pLed->BlinkTimes = 24; + + if( pLed->bLedOn ) + { + pLed->BlinkingLedState = LED_OFF; + + //pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM4_BLINK_ON_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_ON_INTERVAL); + } + else + { + pLed->BlinkingLedState = LED_ON; + + //pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_OFF_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM4_BLINK_OFF_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_OFF_INTERVAL); + } + } + else + { + pLed->CurrLedState = LED_NO_LINK_BLINK; + //printk("----->LED_CTL_NO_LINK bLedBlinkInProgress\n"); + } + break; + + case LED_CTL_LINK: + pLed->CurrLedState = LED_ON; + if( pLed->bLedBlinkInProgress == 0) + { + SwLedOn(dev, pLed); + } + else + ;//printk("----->LED_CTL_LINK bLedBlinkInProgress\n"); + + break; + + case LED_CTL_POWER_OFF: + pLed->CurrLedState = LED_OFF; + if(pLed->bLedBlinkInProgress) + { + printk("----->LED_CTL_POWER_OFF bLedBlinkInProgress\n"); + + //PlatformCancelTimer(Adapter, &(pLed->BlinkTimer)); + del_timer_sync(&(pLed->BlinkTimer)); + pLed->bLedBlinkInProgress = 0; + } + SwLedOff(dev, pLed); + break; + + default: + break; + } +} + + + +//added by lizhaoming 2008.6.3 +// +// Description: +// Implement each led action for SW_LED_MODE5, +// which is customized for DELL 8187B minicard. +// 2008.04.24, by chiyokolin. +// +void +SwLedControlMode5( + struct net_device *dev, + LED_CTL_MODE LedAction + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + PLED_8187 pLed = &(priv->Gpio0Led); + + // Decide led state + //printk("====++++++++++++++++++++++=====>%s In\n", __FUNCTION__); + switch(LedAction) + { + case LED_CTL_TX: + case LED_CTL_RX: + case LED_CTL_SITE_SURVEY: + case LED_CTL_POWER_ON: + case LED_CTL_NO_LINK: + case LED_CTL_LINK: + pLed->CurrLedState = LED_ON; + if( pLed->bLedBlinkInProgress == 0 ) + { + pLed->bLedBlinkInProgress = 1; + if(! pLed->bLedOn ) + pLed->BlinkingLedState = LED_ON; + else + break; + + //printk("====++++++++++++++++++++++=====>%s In LED:%d\n", __FUNCTION__, pLed->bLedOn); + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + // SwLedOn(dev, pLed); + } + else + ;//printk("----->LED_CTL_LINK bLedBlinkInProgress\n"); + + break; + + case LED_CTL_POWER_OFF: + pLed->CurrLedState = LED_OFF; + // printk("<====++++++++++++++++++++++=====%s In LED:%d\n", __FUNCTION__, pLed->bLedOn); + if(pLed->bLedBlinkInProgress) + { + // printk("----->LED_CTL_POWER_OFF bLedBlinkInProgress\n"); + + //PlatformCancelTimer(Adapter, &(pLed->BlinkTimer)); + del_timer_sync(&(pLed->BlinkTimer)); + pLed->bLedBlinkInProgress = 0; + } + SwLedOff(dev, pLed); + break; + + default: + break; + } +} + +// +// Callback fuction of the timer, Gpio0Led.BlinkTimer. +// +void +Gpio0LedBlinkTimerCallback( + unsigned long data + ) +{ + struct net_device *dev = (struct net_device *)data; + struct r8180_priv *priv = ieee80211_priv(dev); + +// printk("=========>%s In\n", __FUNCTION__); + PlatformSwLedBlink(dev, &(priv->Gpio0Led)); +} + + + +// +// Callback fuction of the timer, SwLed0.BlinkTimer. +// +void +SwLed0BlinkTimerCallback( + unsigned long data + ) +{ + struct net_device *dev = (struct net_device *)data; + struct r8180_priv *priv = ieee80211_priv(dev); + +// printk("=========>%s In\n", __FUNCTION__); + PlatformSwLedBlink(dev, &(priv->SwLed0)); +} + + + +// +// Callback fuction of the timer, SwLed1.BlinkTimer. +// +void +SwLed1BlinkTimerCallback( + unsigned long data + ) +{ + struct net_device *dev = (struct net_device *)data; + struct r8180_priv *priv = ieee80211_priv(dev); + +// printk("=========>%s In\n", __FUNCTION__); + PlatformSwLedBlink(dev, &(priv->SwLed1)); +} + +void +PlatformSwLedBlink( + struct net_device *dev, + PLED_8187 pLed + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + +// printk("=========>%s In\n", __FUNCTION__); + switch(pLed->LedPin) + { + case LED_PIN_GPIO0: + schedule_work(&(priv->Gpio0LedWorkItem)); + break; + + case LED_PIN_LED0: + schedule_work(&(priv->SwLed0WorkItem)); + break; + + case LED_PIN_LED1: + schedule_work(&(priv->SwLed1WorkItem)); + break; + + default: + break; + } +} + +// +// Callback fucntion of the workitem for SW LEDs. +// 2006.03.01, by rcnjko. +// + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void Gpio0LedWorkItemCallback(struct work_struct *work) +{ + struct r8180_priv *priv = container_of(work, struct r8180_priv,Gpio0LedWorkItem); + struct net_device *dev = priv->ieee80211->dev; +#else +void +Gpio0LedWorkItemCallback( + void * Context + ) +{ + struct net_device *dev = (struct net_device *)Context; + struct r8180_priv *priv = ieee80211_priv(dev); +#endif + PLED_8187 pLed = &(priv->Gpio0Led); + if (priv == NULL || dev == NULL){ +// printk("=========>%s In\n", __FUNCTION__); + //printk("ft=====================>%s()\n", __FUNCTION__); + } + +#if 0 // by lizahoming 2008.6.3 + if(priv->LedStrategy == SW_LED_MODE2) + SwLedCm2Blink(dev, pLed); + else + SwLedBlink(dev, pLed); +#endif + +#if 1 // by lizahoming 2008.6.3 + switch(priv->LedStrategy) + { + case SW_LED_MODE2: + SwLedCm2Blink(dev, pLed); + break; + case SW_LED_MODE4: + SwLedCm4Blink(dev, pLed); + break; + default: + SwLedBlink(dev, pLed); + break; + } +#endif + + //LeaveCallbackOfRtWorkItem( &(usbdevice->Gpio0LedWorkItem) ); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void SwLed0WorkItemCallback(struct work_struct *work) +{ + //struct r8180_priv *priv = container_of(work, struct r8180_priv, SwLed0WorkItem); + //struct net_device *dev = priv->dev; +#else +void SwLed0WorkItemCallback(void * Context) +{ + //struct net_device *dev = (struct net_device *)Context; + //struct r8180_priv *priv = ieee80211_priv(dev); +#endif + //SwLedBlink(dev, &(priv->SwLed0)); +// printk("=========>%s In\n", __FUNCTION__); + + //LeaveCallbackOfRtWorkItem( &(usbdevice->SwLed0WorkItem) ); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void SwLed1WorkItemCallback(struct work_struct *work) +{ + //struct r8180_priv *priv = container_of(work, struct r8180_priv, SwLed1WorkItem); +// struct net_device *dev = priv->dev; +#else +void +SwLed1WorkItemCallback( + void * Context + ) +{ + //struct net_device *dev = (struct net_device *)Context; + //struct r8180_priv *priv = ieee80211_priv(dev); +#endif +// printk("=========>%s In\n", __FUNCTION__); + //SwLedBlink(dev, &(priv->SwLed1)); + + //LeaveCallbackOfRtWorkItem( &(usbdevice->SwLed1WorkItem) ); +} + +// +// Implementation of LED blinking behavior. +// It toggle off LED and schedule corresponding timer if necessary. +// +void +SwLedBlink( + struct net_device *dev, + PLED_8187 pLed + ) +{ + u8 bStopBlinking = 0; + + //printk("=========>%s In state:%d\n", __FUNCTION__, pLed->CurrLedState); + // Change LED according to BlinkingLedState specified. + if( pLed->BlinkingLedState == LED_ON ) + { + SwLedOn(dev, pLed); +// printk("Blinktimes (%d): turn on\n", pLed->BlinkTimes); + } + else + { + SwLedOff(dev, pLed); +// printk("Blinktimes (%d): turn off\n", pLed->BlinkTimes); + } + + // Determine if we shall change LED state again. +//by lizhaoming for LED BLINK SLOWLY + if(pLed->CurrLedState == LED_BLINK_SLOWLY) + { + bStopBlinking = 0; + } else { + pLed->BlinkTimes--; + if( pLed->BlinkTimes == 0 ) + { + bStopBlinking = 1; + } + else + { + if( pLed->CurrLedState != LED_BLINK_NORMAL && + pLed->CurrLedState != LED_BLINK_SLOWLY && + pLed->CurrLedState != LED_BLINK_CM3 ) + { + bStopBlinking = 1; + } + } + } + + if(bStopBlinking) + { + if( pLed->CurrLedState == LED_ON && pLed->bLedOn == 0) + { + SwLedOn(dev, pLed); + } + else if(pLed->CurrLedState == LED_OFF && pLed->bLedOn == 1) + { + SwLedOff(dev, pLed); + } + + pLed->BlinkTimes = 0; + pLed->bLedBlinkInProgress = 0; + } + else + { + // Assign LED state to toggle. + if( pLed->BlinkingLedState == LED_ON ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + // Schedule a timer to toggle LED state. + switch( pLed->CurrLedState ) + { + case LED_BLINK_NORMAL: + //printk("LED_BLINK_NORMAL:Blinktimes (%d): turn off\n", pLed->BlinkTimes+1); + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + break; + + case LED_BLINK_SLOWLY: + if( pLed->bLedOn == 1 ) + { + //printk("LED_BLINK_SLOWLY:turn off\n"); + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL+50;//for pcie mini card spec page 33, 250ms + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL+50)); + pLed->BlinkingLedState = LED_OFF; + } else { + //printk("LED_BLINK_SLOWLY:turn on\n"); + //pLed->BlinkTimer.expires = jiffies + 5000;//for pcie mini card spec page 33, 5s + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(5000)); + pLed->BlinkingLedState = LED_ON; + } + break; + + case LED_BLINK_CM3: + //printk("LED_BLINK_CM3:Blinktimes (%d): turn off\n", pLed->BlinkTimes+1); + //pLed->BlinkTimer.expires = jiffies + LED_CM3_BLINK_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM3_BLINK_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM3_BLINK_INTERVAL); + break; + + default: + //printk("LED_BLINK_default:Blinktimes (%d): turn off\n", pLed->BlinkTimes+1); + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_SLOWLY_INTERVAL); + break; + } + } +} + + + +// +// Implementation of LED blinking behavior for SwLedControlMode2. +// +void +SwLedCm2Blink( + struct net_device *dev, + PLED_8187 pLed + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + //PMGNT_INFO priv = &(dev->MgntInfo); + u8 bStopBlinking = 0; + + //printk("========+++++++++++++=>%s In\n", __FUNCTION__); + //To avoid LED blinking when rf is off, add by lizhaoming 2008.6.2 + if((priv->eRFPowerState == eRfOff) && (priv->RfOffReason>RF_CHANGE_BY_IPS)) + { + SwLedOff(dev, pLed); + + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_ON_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM2_BLINK_ON_INTERVAL); + //printk(" Hw/Soft Radio Off, turn off Led\n"); + return; + } + + // Change LED according to BlinkingLedState specified. + if( pLed->BlinkingLedState == LED_ON ) + { + SwLedOn(dev, pLed); + //DMESG("Blinktimes (%d): turn on\n", pLed->BlinkTimes); + } + else + { + SwLedOff(dev, pLed); + //DMESG("Blinktimes (%d): turn off\n", pLed->BlinkTimes); + } + + //Add by lizhaoming for avoid BlinkTimers <0, 2008.6.2 + if(pLed->BlinkTimes > 0) + {//by lizhaoming 2008.6.2 + // Determine if we shall change LED state again. + pLed->BlinkTimes--; + }//by lizhaoming 2008.6.2 + + switch(pLed->CurrLedState) + { + case LED_BLINK_NORMAL: + if(pLed->BlinkTimes == 0) + { + bStopBlinking = 1; + } + break; +/* CM2 scan blink and no link blind now not be supported + case LED_SCAN_BLINK: + if( (priv->mAssoc || priv->mIbss) && // Linked. + (!priv->bScanInProgress) && // Not in scan stage. + (pLed->BlinkTimes % 2 == 0)) // Even + { + bStopBlinking = 1; + } + break; + + case LED_NO_LINK_BLINK: + //Revised miniCard Ad-hoc mode "Slow Blink" by Isaiah 2006-08-03 + //if( (priv->mAssoc || priv->mIbss) ) // Linked. + if( priv->mAssoc) + { + bStopBlinking = 1; + } + else if(priv->mIbss && priv->bMediaConnect ) + { + bStopBlinking = 1; + } + break; +*/ + default: + bStopBlinking = 1; + break; + } + + if(bStopBlinking) + { +/* + if( priv->eRFPowerState != eRfOn ) + { + SwLedOff(dev, pLed); + } + else if( priv->bMediaConnect == 1 && pLed->bLedOn == 0) + { + SwLedOn(dev, pLed); + } + else if( priv->bMediaConnect == 0 && pLed->bLedOn == 1) + { + SwLedOff(dev, pLed); + } +*/ + pLed->BlinkTimes = 0; + pLed->bLedBlinkInProgress = 0; + } + else + { + // Assign LED state to toggle. + if( pLed->BlinkingLedState == LED_ON ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + // Schedule a timer to toggle LED state. + switch( pLed->CurrLedState ) + { + case LED_BLINK_NORMAL: + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + break; + + case LED_BLINK_SLOWLY: + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_SLOWLY_INTERVAL); + break; + + case LED_SCAN_BLINK: + case LED_NO_LINK_BLINK: + if( pLed->bLedOn ) { + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_ON_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM2_BLINK_ON_INTERVAL); + } else { + //pLed->BlinkTimer.expires = jiffies + LED_CM2_BLINK_OFF_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM2_BLINK_OFF_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_CM2_BLINK_OFF_INTERVAL); + } + break; + + default: + //RT_ASSERT(0, ("SwLedCm2Blink(): unexpected state!\n")); + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + //PlatformSetTimer(dev, &(pLed->BlinkTimer), LED_BLINK_SLOWLY_INTERVAL); + break; + } + } +} + +// added by lizhaoming 2008.6.2 +// +// Description: +// Implement LED blinking behavior for SW_LED_MODE4. +// +void +SwLedCm4Blink( + struct net_device *dev, + PLED_8187 pLed + ) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + u8 bStopBlinking = 0; + + printk("======++++++++++++++++++======>%s In\n", __FUNCTION__); + //To avoid LED blinking when rf is off, add by Maddest 20080307 + if((priv->eRFPowerState == eRfOff) && (priv->RfOffReason>RF_CHANGE_BY_IPS)) + { + SwLedOff(dev, pLed); + + //pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM4_BLINK_ON_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_ON_INTERVAL); + printk(" Hw/Soft Radio Off, turn off Led\n"); + return; + } + // Change LED according to BlinkingLedState specified. + if( pLed->BlinkingLedState == LED_ON ) + { + if(!pLed->bLedOn) + { + SwLedOn(dev, pLed); + } + printk("Blinktimes (%d): turn on\n", pLed->BlinkTimes); + } + else + { + SwLedOff(dev, pLed); + printk("Blinktimes (%d): turn off\n", pLed->BlinkTimes); + } + + //Add by Maddest for avoid BlinkTimers <0, 20080307; + if(pLed->BlinkTimes > 0) + { + // Determine if we shall change LED state again. + pLed->BlinkTimes--; + } + printk("pLed->CurrLedState %d pLed->BlinkTimes %d\n", pLed->CurrLedState,pLed->BlinkTimes); + switch(pLed->CurrLedState) + { + case LED_BLINK_NORMAL: + if(pLed->BlinkTimes == 0) + { + bStopBlinking = 1; + } + break; + +/* CM2 scan blink and no link blind now not be supported + case LED_SCAN_BLINK: + if( (priv->mAssoc || priv->mIbss) && // Linked.//???????????? + (!priv->bScanInProgress) && // Not in scan stage.//???????????? + (pLed->BlinkTimes % 2 == 0)) // Even + { + bStopBlinking = 1; + } + break; + + case LED_NO_LINK_BLINK: + //Revised miniCard Ad-hoc mode "Slow Blink" by Isaiah 2006-08-03 + //if( (pMgntInfo->mAssoc || pMgntInfo->mIbss) ) // Linked. + if( priv->mAssoc) //???????????? + { + bStopBlinking = 1; + } + else if(priv->mIbss && priv->bMediaConnect )//???????????? + { + bStopBlinking = 1; + } + break; +*/ + + default: + bStopBlinking = 1; + break; + } + + if(bStopBlinking) + { + /* + if( priv->eRFPowerState != eRfOn ) + { + SwLedOff(dev, pLed); + } + else if( priv->bMediaConnect == true && pLed->bLedOn == false)//???????????? + { + SwLedOn(dev, pLed); + } + else if( priv->bMediaConnect == false && pLed->bLedOn == true)//???????????? + { + SwLedOff(dev, pLed); + } + */ + + pLed->BlinkTimes = 0; + pLed->bLedBlinkInProgress = 0; + } + else + { + // Assign LED state to toggle. + if( pLed->BlinkingLedState == LED_ON ) + pLed->BlinkingLedState = LED_OFF; + else + pLed->BlinkingLedState = LED_ON; + + // Schedule a timer to toggle LED state. + switch( pLed->CurrLedState ) + { + case LED_BLINK_NORMAL: + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + break; + + case LED_BLINK_SLOWLY: + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_BLINK_SLOWLY_INTERVAL); + break; + + case LED_SCAN_BLINK: + pLed->BlinkingLedState = LED_ON; + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_NORMAL_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_NORMAL_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_BLINK_NORMAL_INTERVAL); + + case LED_NO_LINK_BLINK: + if( pLed->bLedOn ){ + //pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_ON_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM4_BLINK_ON_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_ON_INTERVAL); + }else{ + //pLed->BlinkTimer.expires = jiffies + LED_CM4_BLINK_OFF_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_CM4_BLINK_OFF_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_CM4_BLINK_OFF_INTERVAL); + } + break; + + default: + printk("SwLedCm2Blink(): unexpected state!\n"); + //pLed->BlinkTimer.expires = jiffies + LED_BLINK_SLOWLY_INTERVAL; + //add_timer(&(pLed->BlinkTimer)); + mod_timer(&pLed->BlinkTimer, jiffies + MSECS(LED_BLINK_SLOWLY_INTERVAL)); + //PlatformSetTimer(Adapter, &(pLed->BlinkTimer), LED_BLINK_SLOWLY_INTERVAL); + break; + } + } +} + +void +SwLedOn( + struct net_device *dev, + PLED_8187 pLed +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); +// printk("=========>%s(), pin:%d\n", __FUNCTION__, pLed->LedPin); + switch(pLed->LedPin) + { + case LED_PIN_GPIO0: + write_nic_byte(dev,0x0091,0x01); + write_nic_byte(dev,0x0090,0x00); // write 0 : LED on + break; + + case LED_PIN_LED0: + priv->PsrValue &= ~(0x01 << 4); + write_nic_byte(dev, PSR, priv->PsrValue); + break; + + case LED_PIN_LED1: + priv->PsrValue &= ~(0x01 << 5); + write_nic_byte(dev, PSR, priv->PsrValue); + break; + + default: + break; + } + + pLed->bLedOn = 1; +} + +void +SwLedOff( + struct net_device *dev, + PLED_8187 pLed +) +{ + struct r8180_priv *priv = ieee80211_priv(dev); + + + //printk("=========>%s(), pin:%d\n", __FUNCTION__, pLed->LedPin); + switch(pLed->LedPin) + { + case LED_PIN_GPIO0: + write_nic_byte(dev,0x0091,0x01); + write_nic_byte(dev,0x0090,0x01); // write 1 : LED off + break; + + case LED_PIN_LED0: + priv->PsrValue |= (0x01 << 4); + write_nic_byte(dev, PSR, priv->PsrValue); + break; + + case LED_PIN_LED1: + priv->PsrValue |= (0x01 << 5); + write_nic_byte(dev, PSR, priv->PsrValue); + break; + + default: + break; + } + + pLed->bLedOn = 0; +} + diff --git a/drivers/net/wireless/rtl8187b/r8187_led.h b/drivers/net/wireless/rtl8187b/r8187_led.h new file mode 100644 index 0000000..83492db --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8187_led.h @@ -0,0 +1,276 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + r8187_led.h + +Abstract: + definitions and stuctures for rtl8187 led control. + +Major Change History: + When Who What + ---------- ------ ---------------------------------------------- + 2006-09-07 Xiong Created + +Notes: + +--*/ + +#ifndef R8187_LED_H +#define R8187_LED_H + +#include +#include + + +/*--------------------------Define -------------------------------------------*/ +// +// 0x7E-0x7F is reserved for SW customization. 2006.04.21, by rcnjko. +// +// BIT[0-7] is for CustomerID where value 0x00 and 0xFF is reserved for Realtek. +#define EEPROM_SW_REVD_OFFSET 0x7E + +#define EEPROM_CID_MASK 0x00FF +#define EEPROM_CID_RSVD0 0x00 +#define EEPROM_CID_RSVD1 0xFF +#define EEPROM_CID_ALPHA0 0x01 +#define EEPROM_CID_SERCOMM_PS 0x02 +#define EEPROM_CID_HW_LED 0x03 + +#define EEPROM_CID_QMI 0x07 //Added by lizhaoming 2008.6.3 +#define EEPROM_CID_DELL 0x08 //Added by lizhaoming 2008.6.3 + +#define LED_BLINK_NORMAL_INTERVAL 100 //by lizhaoming 50 -> 100 +#define LED_BLINK_SLOWLY_INTERVAL 200 + +// Customized for AzWave, 2006.04.03, by rcnjko. +#define LED_CM2_BLINK_ON_INTERVAL 250 +#define LED_CM2_BLINK_OFF_INTERVAL 4750 +// + +// Customized for Sercomm Printer Server case, 2006.04.21, by rcnjko. +#define LED_CM3_BLINK_INTERVAL 1500 + +// by lizhaoming 2008.6.3: Customized for QMI. +// +#define LED_CM4_BLINK_ON_INTERVAL 500 +#define LED_CM4_BLINK_OFF_INTERVAL 4500 + + +/*--------------------------Define MACRO--------------------------------------*/ + + +/*------------------------------Define Struct---------------------------------*/ +typedef enum _LED_STATE_8187{ + LED_UNKNOWN = 0, + LED_ON = 1, + LED_OFF = 2, + LED_BLINK_NORMAL = 3, + LED_BLINK_SLOWLY = 4, + LED_POWER_ON_BLINK = 5, + LED_SCAN_BLINK = 6, // LED is blinking during scanning period, the # of times to blink is depend on time for scanning. + LED_NO_LINK_BLINK = 7, // LED is blinking during no link state. + LED_BLINK_CM3 = 8, // Customzied for Sercomm Printer Server case +}LED_STATE_8187; + +typedef enum _RT_CID_TYPE { + RT_CID_DEFAULT, + RT_CID_8187_ALPHA0, + RT_CID_8187_SERCOMM_PS, + RT_CID_8187_HW_LED, + + RT_CID_87B_QMI , //Added by lizhaoming 2008.6.3 + RT_CID_87B_DELL, //Added by lizhaoming 2008.6.3 + +} RT_CID_TYPE; + +typedef enum _LED_STRATEGY_8187{ + SW_LED_MODE0, // SW control 1 LED via GPIO0. It is default option. + SW_LED_MODE1, // 2 LEDs, through LED0 and LED1. For ALPHA. + SW_LED_MODE2, // SW control 1 LED via GPIO0, customized for AzWave 8187 minicard. + SW_LED_MODE3, // SW control 1 LED via GPIO0, customized for Sercomm Printer Server case. + SW_LED_MODE4, //added by lizhaoming for bluetooth 2008.6.3 + SW_LED_MODE5, //added by lizhaoming for bluetooth 2008.6.3 + HW_LED, // HW control 2 LEDs, LED0 and LED1 (there are 4 different control modes, see MAC.CONFIG1 for details.) +}LED_STRATEGY_8187, *PLED_STRATEGY_8187; + +typedef enum _LED_PIN_8187{ + LED_PIN_GPIO0, + LED_PIN_LED0, + LED_PIN_LED1 +}LED_PIN_8187; + +//by lizhaoming for LED 2008.6.23 into ieee80211.h +//typedef enum _LED_CTL_MODE { +// LED_CTL_POWER_ON, +// LED_CTL_POWER_OFF, +// LED_CTL_LINK, +// LED_CTL_NO_LINK, +// LED_CTL_TX, +// LED_CTL_RX, +// LED_CTL_SITE_SURVEY, +//} LED_CTL_MODE; + +typedef struct _LED_8187{ + LED_PIN_8187 LedPin; // Identify how to implement this SW led. + + LED_STATE_8187 CurrLedState; // Current LED state. + u8 bLedOn; // TRUE if LED is ON, FALSE if LED is OFF. + + u8 bLedBlinkInProgress; // TRUE if it is blinking, FALSE o.w.. + u32 BlinkTimes; // Number of times to toggle led state for blinking. + LED_STATE_8187 BlinkingLedState; // Next state for blinking, either LED_ON or LED_OFF are. + struct timer_list BlinkTimer; // Timer object for led blinking. +} LED_8187, *PLED_8187; + + + +/*------------------------Export global variable------------------------------*/ + + +/*------------------------------Funciton declaration--------------------------*/ +void +InitSwLeds( + struct net_device *dev + ); + +void +DeInitSwLeds( + struct net_device *dev + ); + +void +InitLed8187( + struct net_device *dev, + PLED_8187 pLed, + LED_PIN_8187 LedPin, + void * BlinkCallBackFunc); + +void +DeInitLed8187( + struct net_device *dev, + PLED_8187 pLed); + +void +LedControl8187( + struct net_device *dev, + LED_CTL_MODE LedAction +); + +void +SwLedControlMode0( + struct net_device *dev, + LED_CTL_MODE LedAction +); + +void +SwLedControlMode1( + struct net_device *dev, + LED_CTL_MODE LedAction +); + +void +SwLedControlMode2( + struct net_device *dev, + LED_CTL_MODE LedAction +); + +void +SwLedControlMode3( + struct net_device *dev, + LED_CTL_MODE LedAction +); + + +void +SwLedControlMode4( + struct net_device *dev, + LED_CTL_MODE LedAction +); + + +void +SwLedControlMode5( + struct net_device *dev, + LED_CTL_MODE LedAction +); + +void +Gpio0LedBlinkTimerCallback( + unsigned long data + ); + +void +SwLed0BlinkTimerCallback( + unsigned long data + ); + +void +SwLed1BlinkTimerCallback( + unsigned long data + ); + +void +PlatformSwLedBlink( + struct net_device *dev, + PLED_8187 pLed + ); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +void +Gpio0LedWorkItemCallback( + void * Context + ); + +void +SwLed0WorkItemCallback( + void * Context + ); + +void +SwLed1WorkItemCallback( + void * Context + ); +#else +void +Gpio0LedWorkItemCallback(struct work_struct *work); + +void +SwLed0WorkItemCallback(struct work_struct *work); + +void +SwLed1WorkItemCallback(struct work_struct *work); + +#endif +void +SwLedBlink( + struct net_device *dev, + PLED_8187 pLed + ); + +void +SwLedCm2Blink( + struct net_device *dev, + PLED_8187 pLed + ); + +void +SwLedCm4Blink( + struct net_device *dev, + PLED_8187 pLed + ); + +void +SwLedOn( + struct net_device *dev, + PLED_8187 pLed +); + +void +SwLedOff( + struct net_device *dev, + PLED_8187 pLed +); + + +#endif diff --git a/drivers/net/wireless/rtl8187b/r8187_rfkill.c b/drivers/net/wireless/rtl8187b/r8187_rfkill.c new file mode 100644 index 0000000..c63877a --- /dev/null +++ b/drivers/net/wireless/rtl8187b/r8187_rfkill.c @@ -0,0 +1,157 @@ +/* + * rtl8187b specific rfkill support + * + * NOTE: we only concern about two states + * eRfOff: RFKILL_STATE_SOFT_BLOCKED + * eRfOn: RFKILL_STATE_UNBLOCKED + * TODO: move led controlling source code to rfkill framework + * + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin + */ + +#include +#include +#include + +/* LED macros are defined in r8187.h and rfkill.h, we not use any of them here + * just avoid compiling erros here. + */ +#undef LED + +#include "r8187.h" +#include "ieee80211/ieee80211.h" +#include "linux/netdevice.h" + +static struct rfkill *r8187b_rfkill; +static struct work_struct r8187b_rfkill_task; +static int initialized; +/* turn off by default */ +int r8187b_rfkill_state = RFKILL_USER_STATE_SOFT_BLOCKED; +struct net_device *r8187b_dev = NULL; +RT_RF_POWER_STATE eRfPowerStateToSet; + +/* These two mutexes are used to ensure the relative rfkill status are accessed + * by different tasks exclusively */ +DEFINE_MUTEX(statetoset_lock); +DEFINE_MUTEX(state_lock); + +static void r8187b_wifi_rfkill_task(struct work_struct *work) +{ + if (r8187b_dev) { + mutex_lock(&statetoset_lock); + r8187b_wifi_change_rfkill_state(r8187b_dev, eRfPowerStateToSet); + mutex_unlock(&statetoset_lock); + } +} + +static int r8187b_wifi_update_rfkill_state(int status) +{ + /* ensure r8187b_rfkill is initialized if dev is not initialized, means + * wifi driver is not start, the status is eRfOff be default. + */ + if (!r8187b_dev) + return eRfOff; + + if (initialized == 0) { + /* init the rfkill work task */ + INIT_WORK(&r8187b_rfkill_task, r8187b_wifi_rfkill_task); + initialized = 1; + } + + mutex_lock(&statetoset_lock); + if (status == 1) + eRfPowerStateToSet = eRfOn; + else if (status == 0) + eRfPowerStateToSet = eRfOff; + else if (status == 2) { + /* if the KEY_WLAN is pressed, just switch it! */ + mutex_lock(&state_lock); + if (r8187b_rfkill_state == RFKILL_USER_STATE_UNBLOCKED) + eRfPowerStateToSet = eRfOff; + else if (r8187b_rfkill_state == RFKILL_USER_STATE_SOFT_BLOCKED) + eRfPowerStateToSet = eRfOn; + mutex_unlock(&state_lock); + } + mutex_unlock(&statetoset_lock); + + schedule_work(&r8187b_rfkill_task); + + return eRfPowerStateToSet; +} + +static int r8187b_rfkill_set(void *data, bool blocked) +{ + r8187b_wifi_update_rfkill_state(!blocked); + + return 0; +} + +static void r8187b_rfkill_query(struct rfkill *rfkill, void *data) +{ + static bool blocked; + + mutex_lock(&state_lock); + if (r8187b_rfkill_state == RFKILL_USER_STATE_UNBLOCKED) + blocked = 0; + else if (r8187b_rfkill_state == RFKILL_USER_STATE_SOFT_BLOCKED) + blocked = 1; + mutex_unlock(&state_lock); + + rfkill_set_hw_state(rfkill, blocked); +} + +int r8187b_wifi_report_state(r8180_priv *priv) +{ + mutex_lock(&state_lock); + r8187b_rfkill_state = RFKILL_USER_STATE_UNBLOCKED; + if (priv->ieee80211->bHwRadioOff && priv->eRFPowerState == eRfOff) + r8187b_rfkill_state = RFKILL_USER_STATE_SOFT_BLOCKED; + mutex_unlock(&state_lock); + + r8187b_rfkill_query(r8187b_rfkill, NULL); + + return 0; +} + +static const struct rfkill_ops r8187b_rfkill_ops = { + .set_block = r8187b_rfkill_set, + .query = r8187b_rfkill_query, +}; + +int r8187b_rfkill_init(struct net_device *dev) +{ + int ret; + + /* init the r8187b device */ + r8187b_dev = dev; + + /* init the rfkill struct */ + r8187b_rfkill = rfkill_alloc("r8187b-wifi", &dev->dev, + RFKILL_TYPE_WLAN, &r8187b_rfkill_ops, + (void *)1); + + if (!r8187b_rfkill) { + rfkill_destroy(r8187b_rfkill); + printk(KERN_WARNING "r8187b: Unable to allocate rfkill\n"); + return -ENOMEM; + } + ret = rfkill_register(r8187b_rfkill); + if (ret) { + rfkill_destroy(r8187b_rfkill); + return ret; + } + + /* The default status is passed to the rfkill module */ + + return 0; +} + +void r8187b_rfkill_exit(void) +{ + if (r8187b_rfkill) { + rfkill_unregister(r8187b_rfkill); + rfkill_destroy(r8187b_rfkill); + } + r8187b_rfkill = NULL; +} diff --git a/drivers/net/wireless/rtl8187b/readme b/drivers/net/wireless/rtl8187b/readme new file mode 100644 index 0000000..4438d2a --- /dev/null +++ b/drivers/net/wireless/rtl8187b/readme @@ -0,0 +1,124 @@ +rtl8187 Linux kernel driver +Released under the terms of GNU General Public Licence (GPL) +Copyright(c) Andrea Merello - 2004,2005 + +Portions of this driver are based on other projects, please see the notes +in the source files for detail. +A special thanks go to Realtek corp for their support and to David Young +------------------------------------------------------------------------------ + +This is an attempt to write somethig that can make rtl8187 usb dongle wifi card +on Linux using only opensource stuff. +The rtl8225 radio is supported. + +It's in early development stage so don't expect too much from it +(also use it at your own risk!) +This should be considered just a fragment of code.. using it on your(any) +system is at your own risk! Please note that I never supported the idea to +use it in any way, so i cannot be considered responsible in any way for +anything deriving by it usage. + +Anyway for now we have monitor mode and managed mode +basically working! This isn't necessary stable, but seems to work.. + +This driver is still under development and very far from perfect. It should work on x86, +Other archs are untested.. + +To compile the driver simply run make. + +The driver contains also the ieee80211.h and ieee80211_crypt.h from the ieee stack. +Note that for some reasons this stack is NOT the same that will be included in newer +2.6 kernel. I will try to port to this stack as soon as it will have enought features +to support 8187 cards. +Please note that you will have to make sure the two .h files are the same of the ieee +stack. +In other words when you download from the CVS this driver and the ieee80211 stack a good +idea is to copy the ieee80211.h and ieee80211_crypt.h from the ieee directory to the drv +directory + +Warning during compile are OK + +To wake up the nic run: + + ifconfig up + +(where is your network device for wlan card). + +Please note that the default interface name is wlanX. + +Please note thet this will take several seconds.. + +If you would like to set the interface name to something else you may use the +'devname=' module parameter. For example: + + insmod r8187.ko ifname=eth%d + +will set the interface name of this device to something like eth0. + +Once the nic is up it can be put in a monitor mode by running: + + iwconfig mode monitor + +and channel number may be changed by running: + + iwconfig channel XX + + +In monitor mode a choice may be made via iwpriv if the nic should pass packets +with bad crc or drop them. + +To put the nic in managed mode run: + + iwconfig mode managed + +In managed mode there is support for + + iwlist scan + +that should report the currently available networks. +Please note that in managed mode channels cannot be changed manually. + +To associate with a network + + iwconfig essid XXXXX + +where XXXXX is the network essid (name) reported by 'iwlist scan'. Please +note that essid is case sensitive. + +If your network is not broadcasting the ESSID, then you need to specify *also* +the AP MAC address + + iwconfig ap XX:XX:XX:XX:XX:XX + +The driver accepts another boolean parameter: hwseqnum +If set to 1 it lets the card HW take care of the sequence number of the TXed +frames. Altought in managed mode I can't see an important reason to use HW to +do that, when we'll start to TX beacons in master (AP) and ad-hoc modes most +probably it will be extremely useful (since most probably we will use two HW +queues). + +I'm unsure if it will work correctly on all NICs.. reports are *VERY, VERY* apreciated.. + + + WEP + === + +WEP encryption should work. For now it's done by host, not by the nic. Key can be set with: +Key can be set with + + iwconfig key 12345... + +WEP is supported via software thanks to the ipw stack. + +Shared and open authentication are supported + + IWPRIV + ====== + +This driver supports some private handlers: +-badcrc: let you choose to kill or to pass to the upper layer frames with bad crc in monitor mode +-activescan: if 0 the driver will avoid to send probe requests, sanning will be only on beacon basis + + +If you have some question/comments please feel free to write me. + diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 1e0be14..b6f6f52 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1005,6 +1005,9 @@ static void rtl8187_stop(struct ieee80211_hw *dev) u32 reg; mutex_lock(&priv->conf_mutex); + + priv->vif = NULL; + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); reg = rtl818x_ioread8(priv, &priv->map->CMD); @@ -1065,10 +1068,7 @@ exit: static void rtl8187_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { - struct rtl8187_priv *priv = dev->priv; - mutex_lock(&priv->conf_mutex); - priv->vif = NULL; - mutex_unlock(&priv->conf_mutex); + /* Nothing to do */ } static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c index 3411671..4d252c1 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c +++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c @@ -22,6 +22,10 @@ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) { +#ifdef CONFIG_LEMOTE_MACH2F + /* Allow users to activate rfkill through only the /sys interface */ + return 1; +#else u8 gpio; gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); @@ -29,6 +33,7 @@ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); return gpio & priv->rfkill_mask; +#endif } void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 8390dca..4fa78d5 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -1,3 +1,7 @@ if X86 source "drivers/platform/x86/Kconfig" endif + +if MIPS +source "drivers/platform/mips/Kconfig" +endif diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 782953a..8bdc97c 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_X86) += x86/ +obj-$(CONFIG_MIPS) += mips/ diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig new file mode 100644 index 0000000..722d690 --- /dev/null +++ b/drivers/platform/mips/Kconfig @@ -0,0 +1,60 @@ +# +# MIPS Platform Specific Drivers +# + +menuconfig MIPS_PLATFORM_DEVICES + bool "MIPS Platform Specific Device Drivers" + default y + help + Say Y here to get to see options for device drivers of various + MIPS platforms, including vendor-specific netbook/laptop/pc extension + drivers. This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if MIPS_PLATFORM_DEVICES + +config LEMOTE_YEELOONG2F + tristate "Lemote YeeLoong Laptop" + depends on LEMOTE_MACH2F + select BACKLIGHT_LCD_SUPPORT + select LCD_CLASS_DEVICE + select BACKLIGHT_CLASS_DEVICE + select POWER_SUPPLY + select HWMON + select VIDEO_OUTPUT_CONTROL + select INPUT_SPARSEKMAP + select INPUT_EVDEV + depends on INPUT + default m + help + YeeLoong netbook is a mini laptop made by Lemote, which is basically + compatible to FuLoong2F mini PC, but it has an extra Embedded + Controller(kb3310b) for battery, hotkey, backlight, temperature and + fan management. + +config LEMOTE_LYNLOONG2F + tristate "Lemote LynLoong PC" + depends on LEMOTE_MACH2F + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + select VIDEO_OUTPUT_CONTROL + default m + help + LynLoong PC is an AllINONE machine made by Lemote, which is basically + compatible to FuLoong2F Mini PC, the only difference is that it has a + size-fixed screen: 1360x768 with sisfb video driver. and also, it has + its own specific suspend support. + +config GDIUM_LAPTOP + tristate "GDIUM laptop extras" + depends on DEXXON_GDIUM + select POWER_SUPPLY + select I2C + select INPUT_POLLDEV + default m + help + This mini-driver drives the ST7 chipset present in the Gdium laptops. + This gives battery support, wlan rfkill. + +endif # MIPS_PLATFORM_DEVICES diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile new file mode 100644 index 0000000..0eadf8e --- /dev/null +++ b/drivers/platform/mips/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for MIPS Platform-Specific Drivers +# + +obj-$(CONFIG_LEMOTE_YEELOONG2F) += yeeloong_laptop.o + +obj-$(CONFIG_LEMOTE_LYNLOONG2F) += lynloong_pc.o +obj-$(CONFIG_GDIUM_LAPTOP) += gdium_laptop.o diff --git a/drivers/platform/mips/gdium_laptop.c b/drivers/platform/mips/gdium_laptop.c new file mode 100644 index 0000000..41a65ad --- /dev/null +++ b/drivers/platform/mips/gdium_laptop.c @@ -0,0 +1,927 @@ +/* + * gdium_laptop -- Gdium laptop extras + * + * Arnaud Patard + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For input device */ +#define SCAN_INTERVAL 150 + +/* For battery status */ +#define BAT_SCAN_INTERVAL 500 + +#define EC_FIRM_VERSION 0 + +#if CONFIG_GDIUM_VERSION > 2 +#define EC_REG_BASE 1 +#else +#define EC_REG_BASE 0 +#endif + +#define EC_STATUS (EC_REG_BASE+0) +#define EC_STATUS_LID (1<<0) +#define EC_STATUS_PWRBUT (1<<1) +#define EC_STATUS_BATID (1<<2) /* this bit has no real meaning on v2. */ + /* Same as EC_STATUS_ADAPT */ + /* but on v3 it's BATID which mean bat present */ +#define EC_STATUS_SYS_POWER (1<<3) +#define EC_STATUS_WLAN (1<<4) +#define EC_STATUS_ADAPT (1<<5) + +#define EC_CTRL (EC_REG_BASE+1) +#define EC_CTRL_DDR_CLK (1<<0) +#define EC_CTRL_CHARGE_LED (1<<1) +#define EC_CTRL_BEEP (1<<2) +#define EC_CTRL_SUSB (1<<3) /* memory power */ +#define EC_CTRL_TRICKLE (1<<4) +#define EC_CTRL_WLAN_EN (1<<5) +#define EC_CTRL_SUSC (1<<6) /* main power */ +#define EC_CTRL_CHARGE_EN (1<<7) + +#define EC_BAT_LOW (EC_REG_BASE+2) +#define EC_BAT_HIGH (EC_REG_BASE+3) + +#define EC_SIGN (EC_REG_BASE+4) +#define EC_SIGN_OS 0xAE /* write 0xae to control pm stuff */ +#define EC_SIGN_EC 0x00 /* write 0x00 to let the st7 manage pm stuff */ + +#if 0 +#define EC_TEST (EC_REG_BASE+5) /* Depending on firmware version this register */ + /* may be the programmation register so don't play */ + /* with it */ +#endif + +#define BAT_VOLT_PRESENT 500000 /* Min voltage to consider battery present uV */ +#define BAT_MIN 7000000 /* Min battery voltage in uV */ +#define BAT_MIN_MV 7000 /* Min battery voltage in mV */ +#define BAT_TRICKLE_EN 8000000 /* Charging at 1.4A before 8.0V and then charging at 0.25A */ +#define BAT_MAX 7950000 /* Max battery voltage ~8V in V */ +#define BAT_MAX_MV 7950 /* Max battery voltage ~8V in V */ +#define BAT_READ_ERROR 300000 /* battery read error of 0.3V */ +#define BAT_READ_ERROR_MV 300 /* battery read error of 0.3V */ + +#define SM502_WLAN_ON (224+16)/* SM502 GPIO16 may be used on gdium v2 (v3?) as wlan_on */ + /* when R422 is connected */ + +static unsigned char verbose; +static unsigned char gpio16; +static unsigned char ec; +module_param(verbose, byte, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(verbose, "Add some debugging messages"); +module_param(gpio16, byte, S_IRUGO); +MODULE_PARM_DESC(gpio16, "Enable wlan_on signal on SM502"); +module_param(ec, byte, S_IRUGO); +MODULE_PARM_DESC(ec, "Let the ST7 handle the battery (default OS)"); + +struct gdium_laptop_data { + struct i2c_client *client; + struct input_polled_dev *input_polldev; + struct dentry *debugfs; + struct mutex mutex; + struct platform_device *bat_pdev; + struct power_supply gdium_ac; + struct power_supply gdium_battery; + struct workqueue_struct *workqueue; + struct delayed_work work; + char charge_cmd; + /* important registers value */ + char status; + char ctrl; + /* mV */ + int battery_level; + char version; +}; + +/**********************************************************************/ +/* Low level I2C functions */ +/* All are supposed to be called with mutex held */ +/**********************************************************************/ +/* + * Return battery voltage in mV + * >= 0 battery voltage + * < 0 error + */ +static s32 ec_read_battery(struct i2c_client *client) +{ + unsigned char bat_low, bat_high; + s32 data; + unsigned int ret; + + /* + * a = battery high + * b = battery low + * bat = a << 2 | b & 0x03; + * battery voltage = (bat / 1024) * 5 * 2 + */ + data = i2c_smbus_read_byte_data(client, EC_BAT_LOW); + if (data < 0) { + dev_err(&client->dev, "ec_read_bat: read bat_low failed\n"); + return data; + } + bat_low = data & 0xff; + if (verbose) + dev_info(&client->dev, "bat_low %x\n", bat_low); + + data = i2c_smbus_read_byte_data(client, EC_BAT_HIGH); + if (data < 0) { + dev_err(&client->dev, "ec_read_bat: read bat_high failed\n"); + return data; + } + bat_high = data & 0xff; + if (verbose) + dev_info(&client->dev, "bat_high %x\n", bat_high); + + ret = (bat_high << 2) | (bat_low & 3); + /* + * mV + */ + ret = (ret * 5 * 2) * 1000 / 1024; + + return ret; +} + +static s32 ec_read_version(struct i2c_client *client) +{ +#if CONFIG_GDIUM_VERSION > 2 + return i2c_smbus_read_byte_data(client, EC_FIRM_VERSION); +#else + return 0; +#endif +} + +static s32 ec_read_status(struct i2c_client *client) +{ + return i2c_smbus_read_byte_data(client, EC_STATUS); +} + +static s32 ec_read_ctrl(struct i2c_client *client) +{ + return i2c_smbus_read_byte_data(client, EC_CTRL); +} + +static s32 ec_write_ctrl(struct i2c_client *client, unsigned char newvalue) +{ + return i2c_smbus_write_byte_data(client, EC_CTRL, newvalue); +} + +static s32 ec_read_sign(struct i2c_client *client) +{ + return i2c_smbus_read_byte_data(client, EC_SIGN); +} + +static s32 ec_write_sign(struct i2c_client *client, unsigned char sign) +{ + unsigned char value; + s32 ret; + + ret = i2c_smbus_write_byte_data(client, EC_SIGN, sign); + if (ret < 0) { + dev_err(&client->dev, "ec_set_control: write failed\n"); + return ret; + } + + value = ec_read_sign(client); + if (value != sign) { + dev_err(&client->dev, "Failed to set control to %s\n", + sign == EC_SIGN_OS ? "OS" : "EC"); + return -EIO; + } + + return 0; +} + +#if 0 +static int ec_power_off(struct i2c_client *client) +{ + char value; + int ret; + + value = ec_read_ctrl(client); + if (value < 0) { + dev_err(&client->dev, "ec_power_off: read failed\n"); + return value; + } + value &= ~(EC_CTRL_SUSB | EC_CTRL_SUSC); + ret = ec_write_ctrl(client, value); + if (ret < 0) { + dev_err(&client->dev, "ec_power_off: write failed\n"); + return ret; + } + + return 0; +} +#endif + +static s32 ec_wlan_status(struct i2c_client *client) +{ + s32 value; + + value = ec_read_ctrl(client); + if (value < 0) + return value; + + return (value & EC_CTRL_WLAN_EN) ? 1 : 0; +} + +static s32 ec_wlan_en(struct i2c_client *client, int on) +{ + s32 value; + + value = ec_read_ctrl(client); + if (value < 0) + return value; + + value &= ~EC_CTRL_WLAN_EN; + if (on) + value |= EC_CTRL_WLAN_EN; + + return ec_write_ctrl(client, value&0xff); +} + +#if 0 +static s32 ec_led_status(struct i2c_client *client) +{ + s32 value; + + value = ec_read_ctrl(client); + if (value < 0) + return value; + + return (value & EC_CTRL_CHARGE_LED) ? 1 : 0; +} +#endif + +/* Changing the charging led status has never worked */ +static s32 ec_led_en(struct i2c_client *client, int on) +{ +#if 0 + s32 value; + + value = ec_read_ctrl(client); + if (value < 0) + return value; + + value &= ~EC_CTRL_CHARGE_LED; + if (on) + value |= EC_CTRL_CHARGE_LED; + return ec_write_ctrl(client, value&0xff); +#else + return 0; +#endif +} + +static s32 ec_charge_en(struct i2c_client *client, int on, int trickle) +{ + s32 value; + s32 set = 0; + + value = ec_read_ctrl(client); + if (value < 0) + return value; + + if (on) + set |= EC_CTRL_CHARGE_EN; + if (trickle) + set |= EC_CTRL_TRICKLE; + + /* Be clever : don't change values if you don't need to */ + if ((value & (EC_CTRL_CHARGE_EN | EC_CTRL_TRICKLE)) == set) + return 0; + + value &= ~(EC_CTRL_CHARGE_EN | EC_CTRL_TRICKLE); + value |= set; + ec_led_en(client, on); + return ec_write_ctrl(client, (unsigned char)(value&0xff)); + +} + +/**********************************************************************/ +/* Input functions */ +/**********************************************************************/ +struct gdium_keys { + int last_state; + int key_code; + int mask; + int type; +}; + +static struct gdium_keys gkeys[] = { + { + .key_code = KEY_WLAN, + .mask = EC_STATUS_WLAN, + .type = EV_KEY, + }, + { + .key_code = KEY_POWER, + .mask = EC_STATUS_PWRBUT, + .type = EV_KEY, /*EV_PWR,*/ + }, + { + .key_code = SW_LID, + .mask = EC_STATUS_LID, + .type = EV_SW, + }, +}; + +static void gdium_laptop_keys_poll(struct input_polled_dev *dev) +{ + int state, i; + struct gdium_laptop_data *data = dev->private; + struct i2c_client *client = data->client; + struct input_dev *input = dev->input; + s32 status; + + mutex_lock(&data->mutex); + status = ec_read_status(client); + mutex_unlock(&data->mutex); + + if (status < 0) { + /* + * Don't know exactly which version of the firmware + * has this bug but when the power button is pressed + * there are i2c read errors :( + */ + if ((data->version >= 0x13) && !gkeys[1].last_state) { + input_event(input, EV_KEY, KEY_POWER, 1); + input_sync(input); + gkeys[1].last_state = 1; + } + return; + } + + for (i = 0; i < ARRAY_SIZE(gkeys); i++) { + state = status & gkeys[i].mask; + if (state != gkeys[i].last_state) { + gkeys[i].last_state = state; + /* for power key, we want power & key press/release event */ + if (gkeys[i].type == EV_PWR) { + input_event(input, EV_KEY, gkeys[i].key_code, !!state); + input_sync(input); + } + /* Disable wifi on key press but not key release */ + /* + * On firmware >= 0x13 the EC_STATUS_WLAN has it's + * original meaning of Wifi status and no more the + * wifi button status so we have to ignore the event + * on theses versions + */ + if (state && (gkeys[i].key_code == KEY_WLAN) && (data->version < 0x13)) { + mutex_lock(&data->mutex); + ec_wlan_en(client, !ec_wlan_status(client)); + if (gpio16) + gpio_set_value(SM502_WLAN_ON, !ec_wlan_status(client)); + mutex_unlock(&data->mutex); + } + + input_event(input, gkeys[i].type, gkeys[i].key_code, !!state); + input_sync(input); + } + } +} + +static int gdium_laptop_input_init(struct gdium_laptop_data *data) +{ + struct i2c_client *client = data->client; + struct input_dev *input; + int ret, i; + + data->input_polldev = input_allocate_polled_device(); + if (!data->input_polldev) { + ret = -ENOMEM; + goto err; + } + + input = data->input_polldev->input; + input->evbit[0] = BIT(EV_KEY) | BIT_MASK(EV_PWR) | BIT_MASK(EV_SW); + data->input_polldev->poll = gdium_laptop_keys_poll; + data->input_polldev->poll_interval = SCAN_INTERVAL; + data->input_polldev->private = data; + input->name = "gdium-keys"; + input->dev.parent = &client->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < ARRAY_SIZE(gkeys); i++) + input_set_capability(input, gkeys[i].type, gkeys[i].key_code); + + ret = input_register_polled_device(data->input_polldev); + if (ret) { + dev_err(&client->dev, "Unable to register button device\n"); + goto err_poll_dev; + } + + return 0; + +err_poll_dev: + input_free_polled_device(data->input_polldev); +err: + return ret; +} + +static void gdium_laptop_input_exit(struct gdium_laptop_data *data) +{ + input_unregister_polled_device(data->input_polldev); + input_free_polled_device(data->input_polldev); +} + +/**********************************************************************/ +/* Battery management */ +/**********************************************************************/ +static int gdium_ac_get_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + char status; + struct gdium_laptop_data *data = container_of(psy, struct gdium_laptop_data, gdium_ac); + int ret = 0; + + if (!data) { + pr_err("gdium-ac: gdium_laptop_data not found\n"); + return -EINVAL; + } + + status = data->status; + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(status & EC_STATUS_ADAPT); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#undef RET +#define RET (val->intval) + +static int gdium_battery_get_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + char status, ctrl; + struct gdium_laptop_data *data = container_of(psy, struct gdium_laptop_data, gdium_battery); + int percentage_capacity = 0, charge_now = 0, time_to_empty = 0; + int ret = 0, tmp; + + if (!data) { + pr_err("gdium-battery: gdium_laptop_data not found\n"); + return -EINVAL; + } + + status = data->status; + ctrl = data->ctrl; + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + /* uAh */ + RET = 5000000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + /* This formula is gotten by gnuplot with the statistic data */ + time_to_empty = (data->battery_level - BAT_MIN_MV + BAT_READ_ERROR_MV) * 113 - 29870; + if (psp == POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW) { + /* seconds */ + RET = time_to_empty / 10; + break; + } + /* fall through */ + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CAPACITY: { + tmp = data->battery_level * 1000; + /* > BAT_MIN to avoid negative values */ + percentage_capacity = 0; + if ((status & EC_STATUS_BATID) && (tmp > BAT_MIN)) + percentage_capacity = (tmp-BAT_MIN)*100/(BAT_MAX-BAT_MIN); + + if (percentage_capacity > 100) + percentage_capacity = 100; + + if (psp == POWER_SUPPLY_PROP_CAPACITY) { + RET = percentage_capacity; + break; + } + charge_now = 50000 * percentage_capacity; + if (psp == POWER_SUPPLY_PROP_CHARGE_NOW) { + /* uAh */ + RET = charge_now; + break; + } + } /* fall through */ + case POWER_SUPPLY_PROP_STATUS: { + if (status & EC_STATUS_ADAPT) + if (ctrl & EC_CTRL_CHARGE_EN) + RET = POWER_SUPPLY_STATUS_CHARGING; + else + RET = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + RET = POWER_SUPPLY_STATUS_DISCHARGING; + + if (psp == POWER_SUPPLY_PROP_STATUS) + break; + /* mAh -> µA */ + switch (RET) { + case POWER_SUPPLY_STATUS_CHARGING: + RET = -(data->charge_cmd == 2) ? 1400000 : 250000; + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + RET = charge_now / time_to_empty * 36000; + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + default: + RET = 0; + break; + } + } break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + RET = BAT_MAX+BAT_READ_ERROR; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + RET = BAT_MIN-BAT_READ_ERROR; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* mV -> uV */ + RET = data->battery_level * 1000; + break; + case POWER_SUPPLY_PROP_PRESENT: +#if CONFIG_GDIUM_VERSION > 2 + RET = !!(status & EC_STATUS_BATID); +#else + RET = !!(data->battery_level > BAT_VOLT_PRESENT); +#endif + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + tmp = data->battery_level * 1000; + RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + if (status & EC_STATUS_BATID) { + if (tmp >= BAT_MAX) { + RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + if (tmp >= BAT_MAX+BAT_READ_ERROR) + RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + } else if (tmp <= BAT_MIN+BAT_READ_ERROR) { + RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + if (tmp <= BAT_MIN) + RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + } else + RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + } + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + if (ctrl & EC_CTRL_TRICKLE) + RET = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + else if (ctrl & EC_CTRL_CHARGE_EN) + RET = POWER_SUPPLY_CHARGE_TYPE_FAST; + else + RET = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + /* 1.4A ? */ + RET = 1400000; + break; + default: + break; + } + + return ret; +} +#undef RET + +static enum power_supply_property gdium_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property gdium_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_CHARGE_TYPE, +}; + +static void gdium_laptop_battery_work(struct work_struct *work) +{ + struct gdium_laptop_data *data = container_of(work, struct gdium_laptop_data, work.work); + struct i2c_client *client; + int ret; + char old_status, old_charge_cmd; + char present; + s32 status; + + mutex_lock(&data->mutex); + client = data->client; + status = ec_read_status(client); + ret = ec_read_battery(client); + + if ((status < 0) || (ret < 0)) + goto i2c_read_error; + + old_status = data->status; + old_charge_cmd = data->charge_cmd; + data->status = status; + + /* + * Charge only if : + * - battery present + * - ac adapter plugged in + * - battery not fully charged + */ +#if CONFIG_GDIUM_VERSION > 2 + present = !!(data->status & EC_STATUS_BATID); +#else + present = !!(ret > BAT_VOLT_PRESENT); +#endif + data->battery_level = 0; + if (present) { + data->battery_level = (unsigned int)ret; + if (data->status & EC_STATUS_ADAPT) + data->battery_level -= BAT_READ_ERROR_MV; + } + + data->charge_cmd = 0; + if ((data->status & EC_STATUS_ADAPT) && present && (data->battery_level <= BAT_MAX_MV)) + data->charge_cmd = (ret < BAT_TRICKLE_EN) ? 2 : 3; + + ec_charge_en(client, (data->charge_cmd >> 1) & 1, data->charge_cmd & 1); + + /* + * data->ctrl must be set _after_ calling ec_charge_en as this will change the + * control register content + */ + data->ctrl = ec_read_ctrl(client); + + if ((data->status & EC_STATUS_ADAPT) != (old_status & EC_STATUS_ADAPT)) { + power_supply_changed(&data->gdium_ac); + /* Send charging/discharging state change */ + power_supply_changed(&data->gdium_battery); + } else if ((data->status & EC_STATUS_ADAPT) && + ((old_charge_cmd&2) != (data->charge_cmd&2))) + power_supply_changed(&data->gdium_battery); + +i2c_read_error: + mutex_unlock(&data->mutex); + queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL)); +} + +static int gdium_laptop_battery_init(struct gdium_laptop_data *data) +{ + int ret; + + data->bat_pdev = platform_device_register_simple("gdium-battery", 0, NULL, 0); + if (IS_ERR(data->bat_pdev)) + return PTR_ERR(data->bat_pdev); + + data->gdium_battery.name = data->bat_pdev->name; + data->gdium_battery.properties = gdium_battery_props; + data->gdium_battery.num_properties = ARRAY_SIZE(gdium_battery_props); + data->gdium_battery.get_property = gdium_battery_get_props; + data->gdium_battery.use_for_apm = 1; + + ret = power_supply_register(&data->bat_pdev->dev, &data->gdium_battery); + if (ret) + goto err_platform; + + data->gdium_ac.name = "gdium-ac"; + data->gdium_ac.type = POWER_SUPPLY_TYPE_MAINS; + data->gdium_ac.properties = gdium_ac_props; + data->gdium_ac.num_properties = ARRAY_SIZE(gdium_ac_props); + data->gdium_ac.get_property = gdium_ac_get_props; +/* data->gdium_ac.use_for_apm_ac = 1, */ + + ret = power_supply_register(&data->bat_pdev->dev, &data->gdium_ac); + if (ret) + goto err_battery; + + if (!ec) { + INIT_DELAYED_WORK(&data->work, gdium_laptop_battery_work); + data->workqueue = create_singlethread_workqueue("gdium-battery-work"); + if (!data->workqueue) { + ret = -ESRCH; + goto err_work; + } + queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL)); + } + + return 0; + +err_work: +err_battery: + power_supply_unregister(&data->gdium_battery); +err_platform: + platform_device_unregister(data->bat_pdev); + + return ret; +} +static void gdium_laptop_battery_exit(struct gdium_laptop_data *data) +{ + if (!ec) { + cancel_rearming_delayed_workqueue(data->workqueue, &data->work); + destroy_workqueue(data->workqueue); + } + power_supply_unregister(&data->gdium_battery); + power_supply_unregister(&data->gdium_ac); + platform_device_unregister(data->bat_pdev); +} + +/* Debug fs */ +static int gdium_laptop_regs_show(struct seq_file *s, void *p) +{ + struct gdium_laptop_data *data = s->private; + struct i2c_client *client = data->client; + + mutex_lock(&data->mutex); + seq_printf(s, "Version : 0x%02x\n", (unsigned char)ec_read_version(client)); + seq_printf(s, "Status : 0x%02x\n", (unsigned char)ec_read_status(client)); + seq_printf(s, "Ctrl : 0x%02x\n", (unsigned char)ec_read_ctrl(client)); + seq_printf(s, "Sign : 0x%02x\n", (unsigned char)ec_read_sign(client)); + seq_printf(s, "Bat Lo : 0x%02x\n", (unsigned char)i2c_smbus_read_byte_data(client, EC_BAT_LOW)); + seq_printf(s, "Bat Hi : 0x%02x\n", (unsigned char)i2c_smbus_read_byte_data(client, EC_BAT_HIGH)); + seq_printf(s, "Battery : %d uV\n", (unsigned int)ec_read_battery(client) * 1000); + seq_printf(s, "Charge cmd : %s %s\n", data->charge_cmd & 2 ? "C" : " ", data->charge_cmd & 1 ? "T" : " "); + + mutex_unlock(&data->mutex); + return 0; +} + +static int gdium_laptop_regs_open(struct inode *inode, + struct file *file) +{ + return single_open(file, gdium_laptop_regs_show, inode->i_private); +} + +static const struct file_operations gdium_laptop_regs_fops = { + .open = gdium_laptop_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + + +static int gdium_laptop_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct gdium_laptop_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: no smbus_byte support !\n", __func__); + return -ENODEV; + } + + data = kzalloc(sizeof(struct gdium_laptop_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->mutex); + + ret = ec_read_version(client); + if (ret < 0) + goto err_alloc; + + data->version = (unsigned char)ret; + + ret = gdium_laptop_input_init(data); + if (ret) + goto err_alloc; + + ret = gdium_laptop_battery_init(data); + if (ret) + goto err_input; + + + if (!ec) { + ret = ec_write_sign(client, EC_SIGN_OS); + if (ret) + goto err_sign; + } + + if (gpio16) { + ret = gpio_request(SM502_WLAN_ON, "wlan-on"); + if (ret < 0) + goto err_sign; + gpio_set_value(SM502_WLAN_ON, ec_wlan_status(client)); + gpio_direction_output(SM502_WLAN_ON, 1); + } + + dev_info(&client->dev, "Found firmware 0x%02x\n", data->version); + data->debugfs = debugfs_create_file("gdium_laptop", S_IFREG | S_IRUGO, + NULL, data, &gdium_laptop_regs_fops); + + return 0; + +err_sign: + gdium_laptop_battery_exit(data); +err_input: + gdium_laptop_input_exit(data); +err_alloc: + kfree(data); + return ret; +} + +static int gdium_laptop_remove(struct i2c_client *client) +{ + struct gdium_laptop_data *data = i2c_get_clientdata(client); + + if (gpio16) + gpio_free(SM502_WLAN_ON); + ec_write_sign(client, EC_SIGN_EC); + if (data->debugfs) + debugfs_remove(data->debugfs); + + gdium_laptop_battery_exit(data); + gdium_laptop_input_exit(data); + + kfree(data); + return 0; +} + +#ifdef CONFIG_PM +static int gdium_laptop_suspend(struct i2c_client *client, pm_message_t msg) +{ + struct gdium_laptop_data *data = i2c_get_clientdata(client); + + if (!ec) + cancel_rearming_delayed_workqueue(data->workqueue, &data->work); + return 0; +} + +static int gdium_laptop_resume(struct i2c_client *client) +{ + struct gdium_laptop_data *data = i2c_get_clientdata(client); + + if (!ec) + queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL)); + return 0; +} +#else +#define gdium_laptop_suspend NULL +#define gdium_laptop_resume NULL +#endif +static const struct i2c_device_id gdium_id[] = { + { "gdium-laptop" }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, gdium_id); + +static struct i2c_driver gdium_laptop_driver = { + .driver = { + .name = "gdium-laptop", + .owner = THIS_MODULE, + }, + .probe = gdium_laptop_probe, + .remove = gdium_laptop_remove, + .shutdown = gdium_laptop_remove, + .suspend = gdium_laptop_suspend, + .resume = gdium_laptop_resume, + .id_table = gdium_id, +}; + +static int __init gdium_laptop_init(void) +{ + return i2c_add_driver(&gdium_laptop_driver); +} + +static void __exit gdium_laptop_exit(void) +{ + i2c_del_driver(&gdium_laptop_driver); +} + +module_init(gdium_laptop_init); +module_exit(gdium_laptop_exit); + +MODULE_AUTHOR("Arnaud Patard "); +MODULE_DESCRIPTION("Gdium laptop extras"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/lynloong_pc.c b/drivers/platform/mips/lynloong_pc.c new file mode 100644 index 0000000..e55fc63 --- /dev/null +++ b/drivers/platform/mips/lynloong_pc.c @@ -0,0 +1,514 @@ +/* + * Driver for LynLoong PC extras + * + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin , Xiang Yu + * + * 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 /* for backlight subdriver */ +#include +#include /* for video output subdriver */ +#include /* for suspend support */ + +#include +#include + +#include + +static u32 gpio_base, mfgpt_base; + +static void set_gpio_reg_high(int gpio, int reg) +{ + u32 val; + + val = inl(gpio_base + reg); + val |= (1 << gpio); + val &= ~(1 << (16 + gpio)); + outl(val, gpio_base + reg); + mmiowb(); +} + +static void set_gpio_reg_low(int gpio, int reg) +{ + u32 val; + + val = inl(gpio_base + reg); + val |= (1 << (16 + gpio)); + val &= ~(1 << gpio); + outl(val, gpio_base + reg); + mmiowb(); +} + +static void set_gpio_output_low(int gpio) +{ + set_gpio_reg_high(gpio, GPIOL_OUT_EN); + set_gpio_reg_low(gpio, GPIOL_OUT_VAL); +} + +static void set_gpio_output_high(int gpio) +{ + set_gpio_reg_high(gpio, GPIOL_OUT_EN); + set_gpio_reg_high(gpio, GPIOL_OUT_VAL); +} + +/* backlight subdriver */ + +#define MAX_BRIGHTNESS 100 +#define DEFAULT_BRIGHTNESS 50 +#define MIN_BRIGHTNESS 0 +static unsigned int level; + +DEFINE_SPINLOCK(backlight_lock); +/* Tune the brightness */ +static void setup_mfgpt2(void) +{ + unsigned long flags; + + spin_lock_irqsave(&backlight_lock, flags); + + /* Set MFGPT2 comparator 1,2 */ + outw(MAX_BRIGHTNESS-level, MFGPT2_CMP1); + outw(MAX_BRIGHTNESS, MFGPT2_CMP2); + /* Clear MFGPT2 UP COUNTER */ + outw(0, MFGPT2_CNT); + /* Enable counter, compare mode, 32k */ + outw(0x8280, MFGPT2_SETUP); + + spin_unlock_irqrestore(&backlight_lock, flags); +} + +static int lynloong_set_brightness(struct backlight_device *bd) +{ + level = (bd->props.fb_blank == FB_BLANK_UNBLANK && + bd->props.power == FB_BLANK_UNBLANK) ? + bd->props.brightness : 0; + + if (level > MAX_BRIGHTNESS) + level = MAX_BRIGHTNESS; + else if (level < MIN_BRIGHTNESS) + level = MIN_BRIGHTNESS; + + setup_mfgpt2(); + + return 0; +} + +static int lynloong_get_brightness(struct backlight_device *bd) +{ + return level; +} + +static struct backlight_ops backlight_ops = { + .get_brightness = lynloong_get_brightness, + .update_status = lynloong_set_brightness, +}; + +static struct backlight_device *lynloong_backlight_dev; + +static int lynloong_backlight_init(void) +{ + int ret; + u32 hi; + struct backlight_properties props; + + /* Get gpio_base */ + _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &gpio_base); + /* Get mfgpt_base */ + _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &hi, &mfgpt_base); + /* Get gpio_base */ + _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &gpio_base); + + /* Select for mfgpt */ + set_gpio_reg_high(7, GPIOL_OUT_AUX1_SEL); + /* Enable brightness controlling */ + set_gpio_output_high(7); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = MAX_BRIGHTNESS; + props.type = BACKLIGHT_PLATFORM; + lynloong_backlight_dev = backlight_device_register("backlight0", NULL, + NULL, &backlight_ops, &props); + + if (IS_ERR(lynloong_backlight_dev)) { + ret = PTR_ERR(lynloong_backlight_dev); + return ret; + } + + lynloong_backlight_dev->props.brightness = DEFAULT_BRIGHTNESS; + backlight_update_status(lynloong_backlight_dev); + + return 0; +} + +static void lynloong_backlight_exit(void) +{ + if (lynloong_backlight_dev) { + backlight_device_unregister(lynloong_backlight_dev); + lynloong_backlight_dev = NULL; + } + /* Disable brightness controlling */ + set_gpio_output_low(7); +} + +/* video output driver */ +static int vo_status = 1; + +static int lcd_video_output_get(struct output_device *od) +{ + return vo_status; +} + +static int lcd_video_output_set(struct output_device *od) +{ + int i; + unsigned long status; + + status = !!od->request_state; + + if (status == 0) { + /* Set the current status as off */ + vo_status = 0; + /* Turn off the backlight */ + set_gpio_output_low(11); + for (i = 0; i < 0x500; i++) + delay(); + /* Turn off the LCD */ + set_gpio_output_high(8); + } else { + /* Turn on the LCD */ + set_gpio_output_low(8); + for (i = 0; i < 0x500; i++) + delay(); + /* Turn on the backlight */ + set_gpio_output_high(11); + /* Set the current status as on */ + vo_status = 1; + } + + return 0; +} + +static struct output_properties lcd_output_properties = { + .set_state = lcd_video_output_set, + .get_status = lcd_video_output_get, +}; + +static struct output_device *lcd_output_dev; + +static void lynloong_lcd_vo_set(int status) +{ + lcd_output_dev->request_state = status; + lcd_video_output_set(lcd_output_dev); +} + +static int lynloong_vo_init(void) +{ + int ret; + + /* Register video output device: lcd */ + lcd_output_dev = video_output_register("LCD", NULL, NULL, + &lcd_output_properties); + + if (IS_ERR(lcd_output_dev)) { + ret = PTR_ERR(lcd_output_dev); + lcd_output_dev = NULL; + return ret; + } + /* Ensure LCD is on by default */ + lynloong_lcd_vo_set(1); + + return 0; +} + +static void lynloong_vo_exit(void) +{ + if (lcd_output_dev) { + video_output_unregister(lcd_output_dev); + lcd_output_dev = NULL; + } +} + +/* suspend support */ + +#ifdef CONFIG_PM + +static u32 smb_base; + +/* I2C operations */ + +static int i2c_wait(void) +{ + char c; + int i; + + udelay(1000); + for (i = 0; i < 20; i++) { + c = inb(smb_base | SMB_STS); + if (c & (SMB_STS_BER | SMB_STS_NEGACK)) + return -1; + if (c & SMB_STS_SDAST) + return 0; + udelay(100); + } + return -2; +} + +static void i2c_read_single(int addr, int regNo, char *value) +{ + unsigned char c; + + /* Start condition */ + c = inb(smb_base | SMB_CTRL1); + outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1); + i2c_wait(); + + /* Send slave address */ + outb(addr & 0xfe, smb_base | SMB_SDA); + i2c_wait(); + + /* Acknowledge smbus */ + c = inb(smb_base | SMB_CTRL1); + outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1); + + /* Send register index */ + outb(regNo, smb_base | SMB_SDA); + i2c_wait(); + + /* Acknowledge smbus */ + c = inb(smb_base | SMB_CTRL1); + outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1); + + /* Start condition again */ + c = inb(smb_base | SMB_CTRL1); + outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1); + i2c_wait(); + + /* Send salve address again */ + outb(1 | addr, smb_base | SMB_SDA); + i2c_wait(); + + /* Acknowledge smbus */ + c = inb(smb_base | SMB_CTRL1); + outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1); + + /* Read data */ + *value = inb(smb_base | SMB_SDA); + + /* Stop condition */ + outb(SMB_CTRL1_STOP, smb_base | SMB_CTRL1); + i2c_wait(); +} + +static void i2c_write_single(int addr, int regNo, char value) +{ + unsigned char c; + + /* Start condition */ + c = inb(smb_base | SMB_CTRL1); + outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1); + i2c_wait(); + /* Send slave address */ + outb(addr & 0xfe, smb_base | SMB_SDA); + i2c_wait();; + + /* Send register index */ + outb(regNo, smb_base | SMB_SDA); + i2c_wait(); + + /* Write data */ + outb(value, smb_base | SMB_SDA); + i2c_wait(); + /* Stop condition */ + outb(SMB_CTRL1_STOP, smb_base | SMB_CTRL1); + i2c_wait(); +} + +static void stop_clock(int clk_reg, int clk_sel) +{ + u8 value; + + i2c_read_single(0xd3, clk_reg, &value); + value &= ~(1 << clk_sel); + i2c_write_single(0xd2, clk_reg, value); +} + +static void enable_clock(int clk_reg, int clk_sel) +{ + u8 value; + + i2c_read_single(0xd3, clk_reg, &value); + value |= (1 << clk_sel); + i2c_write_single(0xd2, clk_reg, value); +} + +static char cached_clk_freq; +static char cached_pci_fixed_freq; + +static void decrease_clk_freq(void) +{ + char value; + + i2c_read_single(0xd3, 1, &value); + cached_clk_freq = value; + + /* Select frequency by software */ + value |= (1 << 1); + /* CPU, 3V66, PCI : 100, 66, 33(1) */ + value |= (1 << 2); + i2c_write_single(0xd2, 1, value); + + /* Cache the pci frequency */ + i2c_read_single(0xd3, 14, &value); + cached_pci_fixed_freq = value; + + /* Enable PCI fix mode */ + value |= (1 << 5); + /* 3V66, PCI : 64MHz, 32MHz */ + value |= (1 << 3); + i2c_write_single(0xd2, 14, value); + +} + +static void resume_clk_freq(void) +{ + i2c_write_single(0xd2, 1, cached_clk_freq); + i2c_write_single(0xd2, 14, cached_pci_fixed_freq); +} + +static void stop_clocks(void) +{ + /* CPU Clock Register */ + stop_clock(2, 5); /* not used */ + stop_clock(2, 6); /* not used */ + stop_clock(2, 7); /* not used */ + + /* PCI Clock Register */ + stop_clock(3, 1); /* 8100 */ + stop_clock(3, 5); /* SIS */ + stop_clock(3, 0); /* not used */ + stop_clock(3, 6); /* not used */ + + /* PCI 48M Clock Register */ + stop_clock(4, 6); /* USB grounding */ + stop_clock(4, 5); /* REF(5536_14M) */ + + /* 3V66 Control Register */ + stop_clock(5, 0); /* VCH_CLK..., grounding */ +} + +static void enable_clocks(void) +{ + enable_clock(3, 1); /* 8100 */ + enable_clock(3, 5); /* SIS */ + + enable_clock(4, 6); + enable_clock(4, 5); /* REF(5536_14M) */ + + enable_clock(5, 0); /* VCH_CLOCK, grounding */ +} + +static int lynloong_suspend(struct device *dev) +{ + /* Disable AMP */ + set_gpio_output_high(6); + /* Turn off LCD */ + lynloong_lcd_vo_set(0); + + /* Stop the clocks of some devices */ + stop_clocks(); + + /* Decrease the external clock frequency */ + decrease_clk_freq(); + + return 0; +} + +static int lynloong_resume(struct device *dev) +{ + /* Turn on the LCD */ + lynloong_lcd_vo_set(1); + + /* Resume clock frequency, enable the relative clocks */ + resume_clk_freq(); + enable_clocks(); + + /* Enable AMP */ + set_gpio_output_low(6); + + return 0; +} + +static const SIMPLE_DEV_PM_OPS(lynloong_pm_ops, lynloong_suspend, + lynloong_resume); +#endif /* !CONFIG_PM */ + +static struct platform_device_id platform_device_ids[] = { + { + .name = "lynloong_pc", + }, + {} +}; + +MODULE_DEVICE_TABLE(platform, platform_device_ids); + +static struct platform_driver platform_driver = { + .driver = { + .name = "lynloong_pc", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &lynloong_pm_ops, +#endif + }, + .id_table = platform_device_ids, +}; + +static int __init lynloong_init(void) +{ + int ret; + + pr_info("Load LynLoong Platform Specific Driver.\n"); + + /* Register platform stuff */ + ret = platform_driver_register(&platform_driver); + if (ret) { + pr_err("Fail to register lynloong platform driver.\n"); + return ret; + } + + ret = lynloong_backlight_init(); + if (ret) { + pr_err("Fail to register lynloong backlight driver.\n"); + return ret; + } + + ret = lynloong_vo_init(); + if (ret) { + pr_err("Fail to register lynloong backlight driver.\n"); + lynloong_vo_exit(); + return ret; + } + + return 0; +} + +static void __exit lynloong_exit(void) +{ + lynloong_vo_exit(); + lynloong_backlight_exit(); + platform_driver_unregister(&platform_driver); + + pr_info("Unload LynLoong Platform Specific Driver.\n"); +} + +module_init(lynloong_init); +module_exit(lynloong_exit); + +MODULE_AUTHOR("Wu Zhangjin ; Xiang Yu "); +MODULE_DESCRIPTION("LynLoong PC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/yeeloong_ecrom.c b/drivers/platform/mips/yeeloong_ecrom.c new file mode 100644 index 0000000..2f4d8fe --- /dev/null +++ b/drivers/platform/mips/yeeloong_ecrom.c @@ -0,0 +1,943 @@ +/* + * Driver for flushing/dumping ROM of EC on YeeLoong laptop + * + * Copyright (C) 2009 Lemote Inc. + * Author: liujl + * + * NOTE : + * The EC resources accessing and programming are supported. + */ + +#include +#include +#include +#include + +#include + +#define EC_MISC_DEV "ec_misc" +#define EC_IOC_MAGIC 'E' + +/* ec registers range */ +#define EC_MAX_REGADDR 0xFFFF +#define EC_MIN_REGADDR 0xF000 +#define EC_RAM_ADDR 0xF800 + +/* version burned address */ +#define VER_ADDR 0xf7a1 +#define VER_MAX_SIZE 7 +#define EC_ROM_MAX_SIZE 0x10000 + +/* ec internal register */ +#define REG_POWER_MODE 0xF710 +#define FLAG_NORMAL_MODE 0x00 +#define FLAG_IDLE_MODE 0x01 +#define FLAG_RESET_MODE 0x02 + +/* ec update program flag */ +#define PROGRAM_FLAG_NONE 0x00 +#define PROGRAM_FLAG_IE 0x01 +#define PROGRAM_FLAG_ROM 0x02 + +/* XBI relative registers */ +#define REG_XBISEG0 0xFEA0 +#define REG_XBISEG1 0xFEA1 +#define REG_XBIRSV2 0xFEA2 +#define REG_XBIRSV3 0xFEA3 +#define REG_XBIRSV4 0xFEA4 +#define REG_XBICFG 0xFEA5 +#define REG_XBICS 0xFEA6 +#define REG_XBIWE 0xFEA7 +#define REG_XBISPIA0 0xFEA8 +#define REG_XBISPIA1 0xFEA9 +#define REG_XBISPIA2 0xFEAA +#define REG_XBISPIDAT 0xFEAB +#define REG_XBISPICMD 0xFEAC +#define REG_XBISPICFG 0xFEAD +#define REG_XBISPIDATR 0xFEAE +#define REG_XBISPICFG2 0xFEAF + +/* commands definition for REG_XBISPICMD */ +#define SPICMD_WRITE_STATUS 0x01 +#define SPICMD_BYTE_PROGRAM 0x02 +#define SPICMD_READ_BYTE 0x03 +#define SPICMD_WRITE_DISABLE 0x04 +#define SPICMD_READ_STATUS 0x05 +#define SPICMD_WRITE_ENABLE 0x06 +#define SPICMD_HIGH_SPEED_READ 0x0B +#define SPICMD_POWER_DOWN 0xB9 +#define SPICMD_SST_EWSR 0x50 +#define SPICMD_SST_SEC_ERASE 0x20 +#define SPICMD_SST_BLK_ERASE 0x52 +#define SPICMD_SST_CHIP_ERASE 0x60 +#define SPICMD_FRDO 0x3B +#define SPICMD_SEC_ERASE 0xD7 +#define SPICMD_BLK_ERASE 0xD8 +#define SPICMD_CHIP_ERASE 0xC7 + +/* bits definition for REG_XBISPICFG */ +#define SPICFG_AUTO_CHECK 0x01 +#define SPICFG_SPI_BUSY 0x02 +#define SPICFG_DUMMY_READ 0x04 +#define SPICFG_EN_SPICMD 0x08 +#define SPICFG_LOW_SPICS 0x10 +#define SPICFG_EN_SHORT_READ 0x20 +#define SPICFG_EN_OFFSET_READ 0x40 +#define SPICFG_EN_FAST_READ 0x80 + +/* watchdog timer registers */ +#define REG_WDTCFG 0xfe80 +#define REG_WDTPF 0xfe81 +#define REG_WDT 0xfe82 + +/* lpc configure register */ +#define REG_LPCCFG 0xfe95 + +/* 8051 reg */ +#define REG_PXCFG 0xff14 + +/* Fan register in KB3310 */ +#define REG_ECFAN_SPEED_LEVEL 0xf4e4 +#define REG_ECFAN_SWITCH 0xf4d2 + +/* the ec flash rom id number */ +#define EC_ROM_PRODUCT_ID_SPANSION 0x01 +#define EC_ROM_PRODUCT_ID_MXIC 0xC2 +#define EC_ROM_PRODUCT_ID_AMIC 0x37 +#define EC_ROM_PRODUCT_ID_EONIC 0x1C + +/* misc ioctl operations */ +#define IOCTL_RDREG _IOR(EC_IOC_MAGIC, 1, int) +#define IOCTL_WRREG _IOW(EC_IOC_MAGIC, 2, int) +#define IOCTL_READ_EC _IOR(EC_IOC_MAGIC, 3, int) +#define IOCTL_PROGRAM_IE _IOW(EC_IOC_MAGIC, 4, int) +#define IOCTL_PROGRAM_EC _IOW(EC_IOC_MAGIC, 5, int) + +/* start address for programming of EC content or IE */ +/* ec running code start address */ +#define EC_START_ADDR 0x00000000 +/* ec information element storing address */ +#define IE_START_ADDR 0x00020000 + +/* EC state */ +#define EC_STATE_IDLE 0x00 /* ec in idle state */ +#define EC_STATE_BUSY 0x01 /* ec in busy state */ + +/* timeout value for programming */ +#define EC_FLASH_TIMEOUT 0x1000 /* ec program timeout */ +/* command checkout timeout including cmd to port or state flag check */ +#define EC_CMD_TIMEOUT 0x1000 +#define EC_SPICMD_STANDARD_TIMEOUT (4 * 1000) /* unit : us */ +#define EC_MAX_DELAY_UNIT (10) /* every time for polling */ +#define SPI_FINISH_WAIT_TIME 10 +/* EC content max size */ +#define EC_CONTENT_MAX_SIZE (64 * 1024) +#define IE_CONTENT_MAX_SIZE (0x100000 - IE_START_ADDR) + +/* the register operation access struct */ +struct ec_reg { + u32 addr; /* the address of kb3310 registers */ + u8 val; /* the register value */ +}; + +struct ec_info { + u32 start_addr; + u32 size; + u8 *buf; +}; + +/* open for using rom protection action */ +#define EC_ROM_PROTECTION + +/* enable the chip reset mode */ +static int ec_init_reset_mode(void) +{ + int timeout; + unsigned char status = 0; + int ret = 0; + + /* make chip goto reset mode */ + ret = ec_query_seq(CMD_INIT_RESET_MODE); + if (ret < 0) { + printk(KERN_ERR "ec init reset mode failed.\n"); + goto out; + } + + /* make the action take active */ + timeout = EC_CMD_TIMEOUT; + status = ec_read(REG_POWER_MODE) & FLAG_RESET_MODE; + while (timeout--) { + if (status) { + udelay(EC_REG_DELAY); + break; + } + status = ec_read(REG_POWER_MODE) & FLAG_RESET_MODE; + udelay(EC_REG_DELAY); + } + if (timeout <= 0) { + printk(KERN_ERR "ec rom fixup : can't check reset status.\n"); + ret = -EINVAL; + } else + printk(KERN_INFO "(%d/%d)reset 0xf710 : 0x%x\n", timeout, + EC_CMD_TIMEOUT - timeout, status); + + /* set MCU to reset mode */ + udelay(EC_REG_DELAY); + status = ec_read(REG_PXCFG); + status |= (1 << 0); + ec_write(REG_PXCFG, status); + udelay(EC_REG_DELAY); + + /* disable FWH/LPC */ + udelay(EC_REG_DELAY); + status = ec_read(REG_LPCCFG); + status &= ~(1 << 7); + ec_write(REG_LPCCFG, status); + udelay(EC_REG_DELAY); + + printk(KERN_INFO "entering reset mode ok..............\n"); + + out: + return ret; +} + +/* make ec exit from reset mode */ +static void ec_exit_reset_mode(void) +{ + unsigned char regval; + + udelay(EC_REG_DELAY); + regval = ec_read(REG_LPCCFG); + regval |= (1 << 7); + ec_write(REG_LPCCFG, regval); + regval = ec_read(REG_PXCFG); + regval &= ~(1 << 0); + ec_write(REG_PXCFG, regval); + printk(KERN_INFO "exit reset mode ok..................\n"); + + return; +} + +/* make ec disable WDD */ +static void ec_disable_WDD(void) +{ + unsigned char status; + + udelay(EC_REG_DELAY); + status = ec_read(REG_WDTCFG); + ec_write(REG_WDTPF, 0x03); + ec_write(REG_WDTCFG, (status & 0x80) | 0x48); + printk(KERN_INFO "Disable WDD ok..................\n"); + + return; +} + +/* make ec enable WDD */ +static void ec_enable_WDD(void) +{ + unsigned char status; + + udelay(EC_REG_DELAY); + status = ec_read(REG_WDTCFG); + ec_write(REG_WDT, 0x28); /* set WDT 5sec(0x28) */ + ec_write(REG_WDTCFG, (status & 0x80) | 0x03); + printk(KERN_INFO "Enable WDD ok..................\n"); + + return; +} + +/* make ec goto idle mode */ +static int ec_init_idle_mode(void) +{ + int timeout; + unsigned char status = 0; + int ret = 0; + + ec_query_seq(CMD_INIT_IDLE_MODE); + + /* make the action take active */ + timeout = EC_CMD_TIMEOUT; + status = ec_read(REG_POWER_MODE) & FLAG_IDLE_MODE; + while (timeout--) { + if (status) { + udelay(EC_REG_DELAY); + break; + } + status = ec_read(REG_POWER_MODE) & FLAG_IDLE_MODE; + udelay(EC_REG_DELAY); + } + if (timeout <= 0) { + printk(KERN_ERR "ec rom fixup : can't check out the status.\n"); + ret = -EINVAL; + } else + printk(KERN_INFO "(%d/%d)0xf710 : 0x%x\n", timeout, + EC_CMD_TIMEOUT - timeout, ec_read(REG_POWER_MODE)); + + printk(KERN_INFO "entering idle mode ok...................\n"); + + return ret; +} + +/* make ec exit from idle mode */ +static int ec_exit_idle_mode(void) +{ + + ec_query_seq(CMD_EXIT_IDLE_MODE); + + printk(KERN_INFO "exit idle mode ok...................\n"); + + return 0; +} + +static int ec_instruction_cycle(void) +{ + unsigned long timeout; + int ret = 0; + + timeout = EC_FLASH_TIMEOUT; + while (timeout-- >= 0) { + if (!(ec_read(REG_XBISPICFG) & SPICFG_SPI_BUSY)) + break; + } + if (timeout <= 0) { + printk(KERN_ERR + "EC_INSTRUCTION_CYCLE : timeout for check flag.\n"); + ret = -EINVAL; + goto out; + } + + out: + return ret; +} + +/* To see if the ec is in busy state or not. */ +static inline int ec_flash_busy(unsigned long timeout) +{ + /* assurance the first command be going to rom */ + if (ec_instruction_cycle() < 0) + return EC_STATE_BUSY; +#if 1 + timeout = timeout / EC_MAX_DELAY_UNIT; + while (timeout-- > 0) { + /* check the rom's status of busy flag */ + ec_write(REG_XBISPICMD, SPICMD_READ_STATUS); + if (ec_instruction_cycle() < 0) + return EC_STATE_BUSY; + if ((ec_read(REG_XBISPIDAT) & 0x01) == 0x00) + return EC_STATE_IDLE; + udelay(EC_MAX_DELAY_UNIT); + } + if (timeout <= 0) { + printk(KERN_ERR + "EC_FLASH_BUSY : timeout for check rom flag.\n"); + return EC_STATE_BUSY; + } +#else + /* check the rom's status of busy flag */ + ec_write(REG_XBISPICMD, SPICMD_READ_STATUS); + if (ec_instruction_cycle() < 0) + return EC_STATE_BUSY; + + timeout = timeout / EC_MAX_DELAY_UNIT; + while (timeout-- > 0) { + if ((ec_read(REG_XBISPIDAT) & 0x01) == 0x00) + return EC_STATE_IDLE; + udelay(EC_MAX_DELAY_UNIT); + } + if (timeout <= 0) { + printk(KERN_ERR + "EC_FLASH_BUSY : timeout for check rom flag.\n"); + return EC_STATE_BUSY; + } +#endif + + return EC_STATE_IDLE; +} + +static int rom_instruction_cycle(unsigned char cmd) +{ + unsigned long timeout = 0; + + switch (cmd) { + case SPICMD_READ_STATUS: + case SPICMD_WRITE_ENABLE: + case SPICMD_WRITE_DISABLE: + case SPICMD_READ_BYTE: + case SPICMD_HIGH_SPEED_READ: + timeout = 0; + break; + case SPICMD_WRITE_STATUS: + timeout = 300 * 1000; + break; + case SPICMD_BYTE_PROGRAM: + timeout = 5 * 1000; + break; + case SPICMD_SST_SEC_ERASE: + case SPICMD_SEC_ERASE: + timeout = 1000 * 1000; + break; + case SPICMD_SST_BLK_ERASE: + case SPICMD_BLK_ERASE: + timeout = 3 * 1000 * 1000; + break; + case SPICMD_SST_CHIP_ERASE: + case SPICMD_CHIP_ERASE: + timeout = 20 * 1000 * 1000; + break; + default: + timeout = EC_SPICMD_STANDARD_TIMEOUT; + } + if (timeout == 0) + return ec_instruction_cycle(); + if (timeout < EC_SPICMD_STANDARD_TIMEOUT) + timeout = EC_SPICMD_STANDARD_TIMEOUT; + + return ec_flash_busy(timeout); +} + +/* delay for start/stop action */ +static void delay_spi(int n) +{ + while (n--) + inb(EC_IO_PORT_HIGH); +} + +/* start the action to spi rom function */ +static void ec_start_spi(void) +{ + unsigned char val; + + delay_spi(SPI_FINISH_WAIT_TIME); + val = ec_read(REG_XBISPICFG) | SPICFG_EN_SPICMD | SPICFG_AUTO_CHECK; + ec_write(REG_XBISPICFG, val); + delay_spi(SPI_FINISH_WAIT_TIME); +} + +/* stop the action to spi rom function */ +static void ec_stop_spi(void) +{ + unsigned char val; + + delay_spi(SPI_FINISH_WAIT_TIME); + val = + ec_read(REG_XBISPICFG) & (~(SPICFG_EN_SPICMD | SPICFG_AUTO_CHECK)); + ec_write(REG_XBISPICFG, val); + delay_spi(SPI_FINISH_WAIT_TIME); +} + +/* read one byte from xbi interface */ +static int ec_read_byte(unsigned int addr, unsigned char *byte) +{ + int ret = 0; + + /* enable spicmd writing. */ + ec_start_spi(); + + /* enable write spi flash */ + ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE); + if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) { + printk(KERN_ERR "EC_READ_BYTE : SPICMD_WRITE_ENABLE failed.\n"); + ret = -EINVAL; + goto out; + } + + /* write the address */ + ec_write(REG_XBISPIA2, (addr & 0xff0000) >> 16); + ec_write(REG_XBISPIA1, (addr & 0x00ff00) >> 8); + ec_write(REG_XBISPIA0, (addr & 0x0000ff) >> 0); + /* start action */ + ec_write(REG_XBISPICMD, SPICMD_HIGH_SPEED_READ); + if (rom_instruction_cycle(SPICMD_HIGH_SPEED_READ) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_READ_BYTE : SPICMD_HIGH_SPEED_READ failed.\n"); + ret = -EINVAL; + goto out; + } + + *byte = ec_read(REG_XBISPIDAT); + + out: + /* disable spicmd writing. */ + ec_stop_spi(); + + return ret; +} + +/* write one byte to ec rom */ +static int ec_write_byte(unsigned int addr, unsigned char byte) +{ + int ret = 0; + + /* enable spicmd writing. */ + ec_start_spi(); + + /* enable write spi flash */ + ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE); + if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_WRITE_BYTE : SPICMD_WRITE_ENABLE failed.\n"); + ret = -EINVAL; + goto out; + } + + /* write the address */ + ec_write(REG_XBISPIA2, (addr & 0xff0000) >> 16); + ec_write(REG_XBISPIA1, (addr & 0x00ff00) >> 8); + ec_write(REG_XBISPIA0, (addr & 0x0000ff) >> 0); + ec_write(REG_XBISPIDAT, byte); + /* start action */ + ec_write(REG_XBISPICMD, SPICMD_BYTE_PROGRAM); + if (rom_instruction_cycle(SPICMD_BYTE_PROGRAM) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_WRITE_BYTE : SPICMD_BYTE_PROGRAM failed.\n"); + ret = -EINVAL; + goto out; + } + + out: + /* disable spicmd writing. */ + ec_stop_spi(); + + return ret; +} + +/* unprotect SPI ROM */ +/* EC_ROM_unprotect function code */ +static int EC_ROM_unprotect(void) +{ + unsigned char status; + + /* enable write spi flash */ + ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE); + if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_UNIT_ERASE : SPICMD_WRITE_ENABLE failed.\n"); + return 1; + } + + /* unprotect the status register of rom */ + ec_write(REG_XBISPICMD, SPICMD_READ_STATUS); + if (rom_instruction_cycle(SPICMD_READ_STATUS) == EC_STATE_BUSY) { + printk(KERN_ERR "EC_UNIT_ERASE : SPICMD_READ_STATUS failed.\n"); + return 1; + } + status = ec_read(REG_XBISPIDAT); + ec_write(REG_XBISPIDAT, status & 0x02); + if (ec_instruction_cycle() < 0) { + printk(KERN_ERR "EC_UNIT_ERASE : write status value failed.\n"); + return 1; + } + + ec_write(REG_XBISPICMD, SPICMD_WRITE_STATUS); + if (rom_instruction_cycle(SPICMD_WRITE_STATUS) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_UNIT_ERASE : SPICMD_WRITE_STATUS failed.\n"); + return 1; + } + + /* enable write spi flash */ + ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE); + if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_UNIT_ERASE : SPICMD_WRITE_ENABLE failed.\n"); + return 1; + } + + return 0; +} + +/* erase one block or chip or sector as needed */ +static int ec_unit_erase(unsigned char erase_cmd, unsigned int addr) +{ + unsigned char status; + int ret = 0, i = 0; + int unprotect_count = 3; + int check_flag = 0; + + /* enable spicmd writing. */ + ec_start_spi(); + +#ifdef EC_ROM_PROTECTION + /* added for re-check SPICMD_READ_STATUS */ + while (unprotect_count-- > 0) { + if (EC_ROM_unprotect()) { + ret = -EINVAL; + goto out; + } + + /* first time:500ms --> 5.5sec -->10.5sec */ + for (i = 0; i < ((2 - unprotect_count) * 100 + 10); i++) + udelay(50000); + ec_write(REG_XBISPICMD, SPICMD_READ_STATUS); + if (rom_instruction_cycle(SPICMD_READ_STATUS) + == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_PROGRAM_ROM : SPICMD_READ_STATUS failed.\n"); + } else { + status = ec_read(REG_XBISPIDAT); + printk(KERN_INFO "Read unprotect status : 0x%x\n", + status); + if ((status & 0x1C) == 0x00) { + printk(KERN_INFO + "Read unprotect status OK1 : 0x%x\n", + status & 0x1C); + check_flag = 1; + break; + } + } + } + + if (!check_flag) { + printk(KERN_INFO "SPI ROM unprotect fail.\n"); + return 1; + } +#endif + + /* block address fill */ + if (erase_cmd == SPICMD_BLK_ERASE) { + ec_write(REG_XBISPIA2, (addr & 0x00ff0000) >> 16); + ec_write(REG_XBISPIA1, (addr & 0x0000ff00) >> 8); + ec_write(REG_XBISPIA0, (addr & 0x000000ff) >> 0); + } + + /* erase the whole chip first */ + ec_write(REG_XBISPICMD, erase_cmd); + if (rom_instruction_cycle(erase_cmd) == EC_STATE_BUSY) { + printk(KERN_ERR "EC_UNIT_ERASE : erase failed.\n"); + ret = -EINVAL; + goto out; + } + + out: + /* disable spicmd writing. */ + ec_stop_spi(); + + return ret; +} + +/* update the whole rom content with H/W mode + * PLEASE USING ec_unit_erase() FIRSTLY + */ +static int ec_program_rom(struct ec_info *info, int flag) +{ + unsigned int addr = 0; + unsigned long size = 0; + unsigned char *ptr = NULL; + unsigned char data; + unsigned char val = 0; + int ret = 0; + int i, j; + unsigned char status; + + /* modify for program serial No. + * set IE_START_ADDR & use idle mode, + * disable WDD + */ + if (flag == PROGRAM_FLAG_ROM) { + ret = ec_init_reset_mode(); + addr = info->start_addr + EC_START_ADDR; + printk(KERN_INFO "PROGRAM_FLAG_ROM..............\n"); + } else if (flag == PROGRAM_FLAG_IE) { + ret = ec_init_idle_mode(); + ec_disable_WDD(); + addr = info->start_addr + IE_START_ADDR; + printk(KERN_INFO "PROGRAM_FLAG_IE..............\n"); + } else { + return 0; + } + + if (ret < 0) { + if (flag == PROGRAM_FLAG_IE) + ec_enable_WDD(); + return ret; + } + + size = info->size; + ptr = info->buf; + printk(KERN_INFO "starting update ec ROM..............\n"); + + ret = ec_unit_erase(SPICMD_BLK_ERASE, addr); + if (ret) { + printk(KERN_ERR "program ec : erase block failed.\n"); + goto out; + } + printk(KERN_ERR "program ec : erase block OK.\n"); + + i = 0; + while (i < size) { + data = *(ptr + i); + ec_write_byte(addr, data); + ec_read_byte(addr, &val); + if (val != data) { + ec_write_byte(addr, data); + ec_read_byte(addr, &val); + if (val != data) { + printk(KERN_INFO + "EC : Second flash program failed at:\t"); + printk(KERN_INFO + "addr : 0x%x, source : 0x%x, dest: 0x%x\n", + addr, data, val); + printk(KERN_INFO "This should not happen... STOP\n"); + break; + } + } + i++; + addr++; + } + +#ifdef EC_ROM_PROTECTION + /* we should start spi access firstly */ + ec_start_spi(); + + /* enable write spi flash */ + ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE); + if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_PROGRAM_ROM : SPICMD_WRITE_ENABLE failed.\n"); + goto out1; + } + + /* protect the status register of rom */ + ec_write(REG_XBISPICMD, SPICMD_READ_STATUS); + if (rom_instruction_cycle(SPICMD_READ_STATUS) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_PROGRAM_ROM : SPICMD_READ_STATUS failed.\n"); + goto out1; + } + status = ec_read(REG_XBISPIDAT); + + ec_write(REG_XBISPIDAT, status | 0x1C); + if (ec_instruction_cycle() < 0) { + printk(KERN_ERR + "EC_PROGRAM_ROM : write status value failed.\n"); + goto out1; + } + + ec_write(REG_XBISPICMD, SPICMD_WRITE_STATUS); + if (rom_instruction_cycle(SPICMD_WRITE_STATUS) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_PROGRAM_ROM : SPICMD_WRITE_STATUS failed.\n"); + goto out1; + } +#endif + + /* disable the write action to spi rom */ + ec_write(REG_XBISPICMD, SPICMD_WRITE_DISABLE); + if (rom_instruction_cycle(SPICMD_WRITE_DISABLE) == EC_STATE_BUSY) { + printk(KERN_ERR + "EC_PROGRAM_ROM : SPICMD_WRITE_DISABLE failed.\n"); + goto out1; + } + + out1: + /* we should stop spi access firstly */ + ec_stop_spi(); + out: + /* for security */ + for (j = 0; j < 2000; j++) + udelay(1000); + + /* modify for program serial No. + * after program No exit idle mode + * and enable WDD + */ + if (flag == PROGRAM_FLAG_ROM) { + /* exit from the reset mode */ + ec_exit_reset_mode(); + } else { + /* ec exit from idle mode */ + ret = ec_exit_idle_mode(); + ec_enable_WDD(); + if (ret < 0) + return ret; + } + + return 0; +} + +/* ioctl */ +static int misc_ioctl(struct inode *inode, struct file *filp, u_int cmd, + u_long arg) +{ + struct ec_info ecinfo; + void __user *ptr = (void __user *)arg; + struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data); + int ret = 0; + + switch (cmd) { + case IOCTL_RDREG: + ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "reg read : copy from user error.\n"); + return -EFAULT; + } + if ((ecreg->addr > EC_MAX_REGADDR) + || (ecreg->addr < EC_MIN_REGADDR)) { + printk(KERN_ERR + "reg read : out of register address range.\n"); + return -EINVAL; + } + ecreg->val = ec_read(ecreg->addr); + ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "reg read : copy to user error.\n"); + return -EFAULT; + } + break; + case IOCTL_WRREG: + ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "reg write : copy from user error.\n"); + return -EFAULT; + } + if ((ecreg->addr > EC_MAX_REGADDR) + || (ecreg->addr < EC_MIN_REGADDR)) { + printk(KERN_ERR + "reg write : out of register address range.\n"); + return -EINVAL; + } + ec_write(ecreg->addr, ecreg->val); + break; + case IOCTL_READ_EC: + ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "spi read : copy from user error.\n"); + return -EFAULT; + } + if ((ecreg->addr > EC_RAM_ADDR) + && (ecreg->addr < EC_MAX_REGADDR)) { + printk(KERN_ERR + "spi read : out of register address range.\n"); + return -EINVAL; + } + ec_read_byte(ecreg->addr, &(ecreg->val)); + ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "spi read : copy to user error.\n"); + return -EFAULT; + } + break; + case IOCTL_PROGRAM_IE: + ecinfo.start_addr = EC_START_ADDR; + ecinfo.size = EC_CONTENT_MAX_SIZE; + ecinfo.buf = (u8 *) kmalloc(ecinfo.size, GFP_KERNEL); + if (ecinfo.buf == NULL) { + printk(KERN_ERR "program ie : kmalloc failed.\n"); + return -ENOMEM; + } + ret = copy_from_user(ecinfo.buf, (u8 *) ptr, ecinfo.size); + if (ret) { + printk(KERN_ERR "program ie : copy from user error.\n"); + kfree(ecinfo.buf); + ecinfo.buf = NULL; + return -EFAULT; + } + + /* use ec_program_rom to write serial No */ + ec_program_rom(&ecinfo, PROGRAM_FLAG_IE); + + kfree(ecinfo.buf); + ecinfo.buf = NULL; + break; + case IOCTL_PROGRAM_EC: + ecinfo.start_addr = EC_START_ADDR; + if (get_user((ecinfo.size), (u32 *) ptr)) { + printk(KERN_ERR "program ec : get user error.\n"); + return -EFAULT; + } + if ((ecinfo.size) > EC_CONTENT_MAX_SIZE) { + printk(KERN_ERR "program ec : size out of limited.\n"); + return -EINVAL; + } + ecinfo.buf = (u8 *) kmalloc(ecinfo.size, GFP_KERNEL); + if (ecinfo.buf == NULL) { + printk(KERN_ERR "program ec : kmalloc failed.\n"); + return -ENOMEM; + } + ret = copy_from_user(ecinfo.buf, ((u8 *) ptr + 4), ecinfo.size); + if (ret) { + printk(KERN_ERR "program ec : copy from user error.\n"); + kfree(ecinfo.buf); + ecinfo.buf = NULL; + return -EFAULT; + } + + ec_program_rom(&ecinfo, PROGRAM_FLAG_ROM); + + kfree(ecinfo.buf); + ecinfo.buf = NULL; + break; + + default: + break; + } + + return 0; +} + +static long misc_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return misc_ioctl(file->f_dentry->d_inode, file, cmd, arg); +} + +static int misc_open(struct inode *inode, struct file *filp) +{ + struct ec_reg *ecreg = NULL; + ecreg = kmalloc(sizeof(struct ec_reg), GFP_KERNEL); + if (ecreg) + filp->private_data = ecreg; + + return ecreg ? 0 : -ENOMEM; +} + +static int misc_release(struct inode *inode, struct file *filp) +{ + struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data); + + filp->private_data = NULL; + kfree(ecreg); + + return 0; +} + +static const struct file_operations ecmisc_fops = { + .open = misc_open, + .release = misc_release, + .read = NULL, + .write = NULL, +#ifdef CONFIG_64BIT + .compat_ioctl = misc_compat_ioctl, +#else + .ioctl = misc_ioctl, +#endif +}; + +static struct miscdevice ecmisc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = EC_MISC_DEV, + .fops = &ecmisc_fops +}; + +static int __init ecmisc_init(void) +{ + int ret; + + printk(KERN_INFO "EC misc device init.\n"); + ret = misc_register(&ecmisc_device); + + return ret; +} + +static void __exit ecmisc_exit(void) +{ + printk(KERN_INFO "EC misc device exit.\n"); + misc_deregister(&ecmisc_device); +} + +module_init(ecmisc_init); +module_exit(ecmisc_exit); + +MODULE_AUTHOR("liujl "); +MODULE_DESCRIPTION("Driver for flushing/dumping ROM of EC on YeeLoong laptop"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c new file mode 100644 index 0000000..c0fd248 --- /dev/null +++ b/drivers/platform/mips/yeeloong_laptop.c @@ -0,0 +1,1349 @@ +/* + * Driver for YeeLoong laptop extras + * + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin , Liu Junliang + * + * 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 /* for backlight subdriver */ +#include +#include /* for hwmon subdriver */ +#include +#include /* for video output subdriver */ +#include /* for lcd output subdriver */ +#include /* for hotkey subdriver */ +#include +#include +#include +#include /* for AC & Battery subdriver */ +#include /* for register_reboot_notifier */ +#include /* for register_pm_notifier */ + +#include + +#include /* for loongson_cmdline */ +#include + +/* common function */ +#define EC_VER_LEN 64 + +static int ec_version_before(char *version) +{ + char *p, ec_ver[EC_VER_LEN]; + + p = strstr(loongson_cmdline, "EC_VER="); + if (!p) + memset(ec_ver, 0, EC_VER_LEN); + else { + strncpy(ec_ver, p, EC_VER_LEN); + p = strstr(ec_ver, " "); + if (p) + *p = '\0'; + } + + return (strncasecmp(ec_ver, version, 64) < 0); +} + +/* backlight subdriver */ +#define MIN_BRIGHTNESS 1 +#define MAX_BRIGHTNESS 8 + +static int yeeloong_set_brightness(struct backlight_device *bd) +{ + unsigned char level; + static unsigned char old_level; + + level = (bd->props.fb_blank == FB_BLANK_UNBLANK && + bd->props.power == FB_BLANK_UNBLANK) ? + bd->props.brightness : 0; + + level = SENSORS_LIMIT(level, MIN_BRIGHTNESS, MAX_BRIGHTNESS); + + /* Avoid to modify the brightness when EC is tuning it */ + if (old_level != level) { + if (ec_read(REG_DISPLAY_BRIGHTNESS) == old_level) + ec_write(REG_DISPLAY_BRIGHTNESS, level); + old_level = level; + } + + return 0; +} + +static int yeeloong_get_brightness(struct backlight_device *bd) +{ + return ec_read(REG_DISPLAY_BRIGHTNESS); +} + +static struct backlight_ops backlight_ops = { + .get_brightness = yeeloong_get_brightness, + .update_status = yeeloong_set_brightness, +}; + +static struct backlight_device *yeeloong_backlight_dev; + +static int yeeloong_backlight_init(void) +{ + int ret; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = MAX_BRIGHTNESS; + props.type = BACKLIGHT_PLATFORM; + yeeloong_backlight_dev = backlight_device_register("backlight0", NULL, + NULL, &backlight_ops, &props); + + if (IS_ERR(yeeloong_backlight_dev)) { + ret = PTR_ERR(yeeloong_backlight_dev); + yeeloong_backlight_dev = NULL; + return ret; + } + + yeeloong_backlight_dev->props.brightness = + yeeloong_get_brightness(yeeloong_backlight_dev); + backlight_update_status(yeeloong_backlight_dev); + + return 0; +} + +static void yeeloong_backlight_exit(void) +{ + if (yeeloong_backlight_dev) { + backlight_device_unregister(yeeloong_backlight_dev); + yeeloong_backlight_dev = NULL; + } +} + +/* AC & Battery subdriver */ + +static struct power_supply yeeloong_ac, yeeloong_bat; + +#define RET (val->intval) + +#define BAT_CAP_CRITICAL 5 +#define BAT_CAP_HIGH 95 + +#define get_bat(type) \ + ec_read(REG_BAT_##type) + +#define get_bat_l(type) \ + ((get_bat(type##_HIGH) << 8) | get_bat(type##_LOW)) + +static int yeeloong_get_ac_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + if (psp == POWER_SUPPLY_PROP_ONLINE) + RET = !!(get_bat(POWER) & BIT_BAT_POWER_ACIN); + + return 0; +} + +static enum power_supply_property yeeloong_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply yeeloong_ac = { + .name = "yeeloong-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = yeeloong_ac_props, + .num_properties = ARRAY_SIZE(yeeloong_ac_props), + .get_property = yeeloong_get_ac_props, +}; + +static inline bool is_bat_in(void) +{ + return !!(get_bat(STATUS) & BIT_BAT_STATUS_IN); +} + +static int get_bat_temp(void) +{ + return get_bat_l(TEMPERATURE) * 1000; +} + +static int get_bat_current(void) +{ + return -(s16)get_bat_l(CURRENT); +} + +static int get_bat_voltage(void) +{ + return get_bat_l(VOLTAGE); +} + +static char *get_manufacturer(void) +{ + return (get_bat(VENDOR) == FLAG_BAT_VENDOR_SANYO) ? "SANYO" : "SIMPLO"; +} + +static int get_relative_cap(void) +{ + /* + * When the relative capacity becomes 2, the hardware is observed to + * have been turned off forcely. so, we must tune it be suitable to + * make the software do related actions. + */ + int tmp = get_bat_l(RELATIVE_CAP); + + if (tmp <= (BAT_CAP_CRITICAL * 2)) + tmp -= 3; + + return tmp; +} + +static int yeeloong_get_bat_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + /* Fixed information */ + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + /* mV -> µV */ + RET = get_bat_l(DESIGN_VOL) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + /* mAh->µAh */ + RET = get_bat_l(DESIGN_CAP) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + /* µAh */ + RET = get_bat_l(FULLCHG_CAP) * 1000; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = get_manufacturer(); + break; + /* Dynamic information */ + case POWER_SUPPLY_PROP_PRESENT: + RET = is_bat_in(); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + /* mA -> µA */ + RET = is_bat_in() ? get_bat_current() * 1000 : 0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* mV -> µV */ + RET = is_bat_in() ? get_bat_voltage() * 1000 : 0; + break; + case POWER_SUPPLY_PROP_TEMP: + /* Celcius */ + RET = is_bat_in() ? get_bat_temp() : 0; + break; + case POWER_SUPPLY_PROP_CAPACITY: + RET = is_bat_in() ? get_relative_cap() : 0; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: { + int status; + + if (!is_bat_in()) { + RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + break; + } + + status = get_bat(STATUS); + RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + + if (unlikely(status & BIT_BAT_STATUS_DESTROY)) { + RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + break; + } + + if (status & BIT_BAT_STATUS_FULL) + RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else { + int curr_cap = get_relative_cap(); + + if (status & BIT_BAT_STATUS_LOW) { + RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + if (curr_cap <= BAT_CAP_CRITICAL) + RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + } else if (curr_cap >= BAT_CAP_HIGH) + RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + } + } break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + /* seconds */ + RET = is_bat_in() ? (get_relative_cap() - 3) * 54 + 142 : 0; + break; + case POWER_SUPPLY_PROP_STATUS: { + int charge = get_bat(CHARGE); + + RET = POWER_SUPPLY_STATUS_UNKNOWN; + if (charge & FLAG_BAT_CHARGE_DISCHARGE) + RET = POWER_SUPPLY_STATUS_DISCHARGING; + else if (charge & FLAG_BAT_CHARGE_CHARGE) + RET = POWER_SUPPLY_STATUS_CHARGING; + } break; + case POWER_SUPPLY_PROP_HEALTH: { + int status; + + if (!is_bat_in()) { + RET = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + status = get_bat(STATUS); + RET = POWER_SUPPLY_HEALTH_GOOD; + + if (status & (BIT_BAT_STATUS_DESTROY | + BIT_BAT_STATUS_LOW)) + RET = POWER_SUPPLY_HEALTH_DEAD; + if (get_bat(CHARGE_STATUS) & + BIT_BAT_CHARGE_STATUS_OVERTEMP) + RET = POWER_SUPPLY_HEALTH_OVERHEAT; + } break; + case POWER_SUPPLY_PROP_CHARGE_NOW: /* 1/100(%)*1000 µAh */ + RET = get_relative_cap() * get_bat_l(FULLCHG_CAP) * 10; + break; + default: + return -EINVAL; + } + return 0; +} +#undef RET + +static enum power_supply_property yeeloong_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static struct power_supply yeeloong_bat = { + .name = "yeeloong-bat", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = yeeloong_bat_props, + .num_properties = ARRAY_SIZE(yeeloong_bat_props), + .get_property = yeeloong_get_bat_props, +}; + +static int ac_bat_initialized; + +static int yeeloong_bat_init(void) +{ + int ret; + + ret = power_supply_register(NULL, &yeeloong_ac); + if (ret) + return ret; + ret = power_supply_register(NULL, &yeeloong_bat); + if (ret) { + power_supply_unregister(&yeeloong_ac); + return ret; + } + ac_bat_initialized = 1; + + return 0; +} + +static void yeeloong_bat_exit(void) +{ + ac_bat_initialized = 0; + + power_supply_unregister(&yeeloong_ac); + power_supply_unregister(&yeeloong_bat); +} +/* hwmon subdriver */ + +#define MIN_FAN_SPEED 0 +#define MAX_FAN_SPEED 3 + +#define get_fan(type) \ + ec_read(REG_FAN_##type) + +#define set_fan(type, val) \ + ec_write(REG_FAN_##type, val) + +static inline int get_fan_speed_level(void) +{ + return get_fan(SPEED_LEVEL); +} +static inline void set_fan_speed_level(int speed) +{ + set_fan(SPEED_LEVEL, speed); +} + +static inline int get_fan_mode(void) +{ + return get_fan(AUTO_MAN_SWITCH); +} +static inline void set_fan_mode(int mode) +{ + set_fan(AUTO_MAN_SWITCH, mode); +} + +/* + * 3 different modes: Full speed(0); manual mode(1); auto mode(2) + */ +static int get_fan_pwm_enable(void) +{ + return (get_fan_mode() == BIT_FAN_AUTO) ? 2 : + (get_fan_speed_level() == MAX_FAN_SPEED) ? 0 : 1; +} + +static void set_fan_pwm_enable(int mode) +{ + set_fan_mode((mode == 2) ? BIT_FAN_AUTO : BIT_FAN_MANUAL); + if (mode == 0) + set_fan_speed_level(MAX_FAN_SPEED); +} + +static int get_fan_pwm(void) +{ + return get_fan_speed_level(); +} + +static void set_fan_pwm(int value) +{ + if (get_fan_mode() != BIT_FAN_MANUAL) + return; + + value = SENSORS_LIMIT(value, MIN_FAN_SPEED, MAX_FAN_SPEED); + + /* We must ensure the fan is on */ + if (value > 0) + set_fan(CONTROL, ON); + + set_fan_speed_level(value); +} + +static inline int get_fan_speed(void) +{ + return ((get_fan(SPEED_HIGH) & 0x0f) << 8) | get_fan(SPEED_LOW); +} + +static int get_fan_rpm(void) +{ + return FAN_SPEED_DIVIDER / get_fan_speed(); +} + +static int get_cpu_temp(void) +{ + return (s8)ec_read(REG_TEMPERATURE_VALUE) * 1000; +} + +static int get_cpu_temp_max(void) +{ + return 60 * 1000; +} + +static int get_bat_temp_alarm(void) +{ + return !!(get_bat(CHARGE_STATUS) & BIT_BAT_CHARGE_STATUS_OVERTEMP); +} + +static ssize_t store_sys_hwmon(void (*set) (int), const char *buf, size_t count) +{ + int ret; + unsigned long value; + + if (!count) + return 0; + + ret = strict_strtoul(buf, 10, &value); + if (ret) + return ret; + + set(value); + + return count; +} + +static ssize_t show_sys_hwmon(int (*get) (void), char *buf) +{ + return sprintf(buf, "%d\n", get()); +} + +#define CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \ + static ssize_t show_##_name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return show_sys_hwmon(_set, buf); \ + } \ + static ssize_t store_##_name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return store_sys_hwmon(_get, buf, count); \ + } \ + static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0); + +CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, get_fan_rpm, NULL); +CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR, get_fan_pwm, set_fan_pwm); +CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, get_fan_pwm_enable, + set_fan_pwm_enable); +CREATE_SENSOR_ATTR(temp1_input, S_IRUGO, get_cpu_temp, NULL); +CREATE_SENSOR_ATTR(temp1_max, S_IRUGO, get_cpu_temp_max, NULL); +CREATE_SENSOR_ATTR(temp2_input, S_IRUGO, get_bat_temp, NULL); +CREATE_SENSOR_ATTR(temp2_max_alarm, S_IRUGO, get_bat_temp_alarm, NULL); +CREATE_SENSOR_ATTR(curr1_input, S_IRUGO, get_bat_current, NULL); +CREATE_SENSOR_ATTR(in1_input, S_IRUGO, get_bat_voltage, NULL); + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "yeeloong\n"); +} + +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); + +static struct attribute *hwmon_attributes[] = { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +static struct attribute_group hwmon_attribute_group = { + .attrs = hwmon_attributes +}; + +static struct device *yeeloong_hwmon_dev; + +static int yeeloong_hwmon_init(void) +{ + int ret; + + yeeloong_hwmon_dev = hwmon_device_register(NULL); + if (IS_ERR(yeeloong_hwmon_dev)) { + yeeloong_hwmon_dev = NULL; + return PTR_ERR(yeeloong_hwmon_dev); + } + ret = sysfs_create_group(&yeeloong_hwmon_dev->kobj, + &hwmon_attribute_group); + if (ret) { + hwmon_device_unregister(yeeloong_hwmon_dev); + yeeloong_hwmon_dev = NULL; + return ret; + } + /* ensure fan is set to auto mode */ + set_fan_pwm_enable(2); + + return 0; +} + +static void yeeloong_hwmon_exit(void) +{ + if (yeeloong_hwmon_dev) { + sysfs_remove_group(&yeeloong_hwmon_dev->kobj, + &hwmon_attribute_group); + hwmon_device_unregister(yeeloong_hwmon_dev); + yeeloong_hwmon_dev = NULL; + } +} + +/* video output subdriver */ + +#define LCD 0 +#define CRT 1 +#define VOD_NUM 2 /* The total number of video output device*/ + +static struct output_device *vod[VOD_NUM]; + +static int vor[] = {REG_DISPLAY_LCD, REG_CRT_DETECT}; + +static int get_vo_dev(struct output_device *od) +{ + int i, dev; + + dev = -1; + for (i = 0; i < VOD_NUM; i++) + if (od == vod[i]) + dev = i; + + return dev; +} + +static int vo_get_status(int dev) +{ + return ec_read(vor[dev]); +} + +static int yeeloong_vo_get_status(struct output_device *od) +{ + int vd; + + vd = get_vo_dev(od); + if (vd != -1) + return vo_get_status(vd); + + return -ENODEV; +} + +static void vo_set_state(int dev, int state) +{ + int addr; + unsigned long value; + + switch (dev) { + case LCD: + addr = 0x31; + break; + case CRT: + addr = 0x21; + break; + default: + /* return directly if the wrong video output device */ + return; + } + + outb(addr, 0x3c4); + value = inb(0x3c5); + + switch (dev) { + case LCD: + value |= (state ? 0x03 : 0x02); + break; + case CRT: + if (state) + clear_bit(7, &value); + else + set_bit(7, &value); + break; + default: + break; + } + + outb(addr, 0x3c4); + outb(value, 0x3c5); + + if (dev == LCD) + ec_write(REG_BACKLIGHT_CTRL, state); +} + +static int yeeloong_vo_set_state(struct output_device *od) +{ + int vd; + + vd = get_vo_dev(od); + if (vd == -1) + return -ENODEV; + + if (vd == CRT && !vo_get_status(vd)) + return 0; + + vo_set_state(vd, !!od->request_state); + + return 0; +} + +static struct output_properties vop = { + .set_state = yeeloong_vo_set_state, + .get_status = yeeloong_vo_get_status, +}; + +static int yeeloong_vo_init(void) +{ + int ret, i; + char dev_name[VOD_NUM][4] = {"LCD", "CRT"}; + + /* Register video output device: lcd, crt */ + for (i = 0; i < VOD_NUM; i++) { + vod[i] = video_output_register(dev_name[i], NULL, NULL, &vop); + if (IS_ERR(vod[i])) { + if (i != 0) + video_output_unregister(vod[i-1]); + ret = PTR_ERR(vod[i]); + vod[i] = NULL; + return ret; + } + } + /* Ensure LCD is on by default */ + vo_set_state(LCD, ON); + + /* + * Turn off CRT by default, and will be enabled when the CRT + * connectting event reported by SCI + */ + vo_set_state(CRT, OFF); + + return 0; +} + +static void yeeloong_vo_exit(void) +{ + int i; + + for (i = 0; i < VOD_NUM; i++) { + if (vod[i]) { + video_output_unregister(vod[i]); + vod[i] = NULL; + } + } +} + +/* lcd subdriver */ + +struct lcd_device *lcd[VOD_NUM]; + +static int get_lcd_dev(struct lcd_device *ld) +{ + int i, dev; + + dev = -1; + for (i = 0; i < VOD_NUM; i++) + if (ld == lcd[i]) + dev = i; + + return dev; +} + +static int yeeloong_lcd_set_power(struct lcd_device *ld, int power) +{ + int dev = get_lcd_dev(ld); + + if (power == FB_BLANK_UNBLANK) + vo_set_state(dev, ON); + if (power == FB_BLANK_POWERDOWN) + vo_set_state(dev, OFF); + + return 0; +} + +static int yeeloong_lcd_get_power(struct lcd_device *ld) +{ + return vo_get_status(get_lcd_dev(ld)); +} + +static struct lcd_ops lcd_ops = { + .set_power = yeeloong_lcd_set_power, + .get_power = yeeloong_lcd_get_power, +}; + +static int yeeloong_lcd_init(void) +{ + int ret, i; + char dev_name[VOD_NUM][4] = {"LCD", "CRT"}; + + /* Register video output device: lcd, crt */ + for (i = 0; i < VOD_NUM; i++) { + lcd[i] = lcd_device_register(dev_name[i], NULL, NULL, &lcd_ops); + if (IS_ERR(lcd[i])) { + if (i != 0) + lcd_device_unregister(lcd[i-1]); + ret = PTR_ERR(lcd[i]); + lcd[i] = NULL; + return ret; + } + } +#if 0 + /* This has been done by the vide output driver */ + + /* Ensure LCD is on by default */ + vo_set_state(LCD, ON); + + /* + * Turn off CRT by default, and will be enabled when the CRT + * connectting event reported by SCI + */ + vo_set_state(CRT, OFF); +#endif + return 0; +} + +static void yeeloong_lcd_exit(void) +{ + int i; + + for (i = 0; i < VOD_NUM; i++) { + if (lcd[i]) { + lcd_device_unregister(lcd[i]); + lcd[i] = NULL; + } + } +} + +/* hotkey subdriver */ + +static struct input_dev *yeeloong_hotkey_dev; + +static atomic_t reboot_flag, sleep_flag; +#define in_sleep() (&sleep_flag) +#define in_reboot() (&reboot_flag) + +static const struct key_entry yeeloong_keymap[] = { + {KE_SW, EVENT_LID, { SW_LID } }, + {KE_KEY, EVENT_CAMERA, { KEY_CAMERA } }, /* Fn + ESC */ + {KE_KEY, EVENT_SLEEP, { KEY_SLEEP } }, /* Fn + F1 */ + {KE_KEY, EVENT_DISPLAYTOGGLE, { KEY_DISPLAYTOGGLE } }, /* Fn + F2 */ + {KE_KEY, EVENT_SWITCHVIDEOMODE, { KEY_SWITCHVIDEOMODE } }, /* Fn + F3 */ + {KE_KEY, EVENT_AUDIO_MUTE, { KEY_MUTE } }, /* Fn + F4 */ + {KE_KEY, EVENT_WLAN, { KEY_WLAN } }, /* Fn + F5 */ + {KE_KEY, EVENT_DISPLAY_BRIGHTNESS, { KEY_BRIGHTNESSUP } }, /* Fn + up */ + {KE_KEY, EVENT_DISPLAY_BRIGHTNESS, { KEY_BRIGHTNESSDOWN } }, /* Fn + down */ + {KE_KEY, EVENT_AUDIO_VOLUME, { KEY_VOLUMEUP } }, /* Fn + right */ + {KE_KEY, EVENT_AUDIO_VOLUME, { KEY_VOLUMEDOWN } }, /* Fn + left */ + {KE_END, 0} +}; + +static int is_fake_event(u16 keycode) +{ + switch (keycode) { + case KEY_SLEEP: + case SW_LID: + return atomic_read(in_sleep()) | atomic_read(in_reboot()); + break; + default: + break; + } + return 0; +} + +static struct key_entry *get_event_key_entry(int event, int status) +{ + struct key_entry *ke; + static int old_brightness_status = -1; + static int old_volume_status = -1; + + ke = sparse_keymap_entry_from_scancode(yeeloong_hotkey_dev, event); + if (!ke) + return NULL; + + switch (event) { + case EVENT_DISPLAY_BRIGHTNESS: + /* current status > old one, means up */ + if ((status < old_brightness_status) || (0 == status)) + ke++; + old_brightness_status = status; + break; + case EVENT_AUDIO_VOLUME: + if ((status < old_volume_status) || (0 == status)) + ke++; + old_volume_status = status; + break; + default: + break; + } + + return ke; +} + +static int report_lid_switch(int status) +{ + static int old_status; + + /* + * LID is a switch button, so, two continuous same status should be + * ignored + */ + if (old_status != status) { + input_report_switch(yeeloong_hotkey_dev, SW_LID, !status); + input_sync(yeeloong_hotkey_dev); + } + old_status = status; + + return status; +} + +static int crt_detect_handler(int status) +{ + /* + * When CRT is inserted, enable its output and disable the LCD output, + * otherwise, do reversely. + */ + vo_set_state(CRT, status); + vo_set_state(LCD, !status); + + return status; +} + +static int displaytoggle_handler(int status) +{ + /* EC(>=PQ1D26) does this job for us, we can not do it again, + * otherwise, the brightness will not resume to the normal level! */ + if (ec_version_before("EC_VER=PQ1D26")) + vo_set_state(LCD, status); + + return status; +} + +static int mypow(int x, int y) +{ + int i, j = x; + + for (i = 1; i < y; i++) + j *= j; + + return j; +} + +static int switchvideomode_handler(int status) +{ + /* Default status: CRT|LCD = 0|1 = 1 */ + static int bin_state = 1; + int i; + + /* + * Only enable switch video output button + * when CRT is connected + */ + if (!vo_get_status(CRT)) + return 0; + /* + * 2. no CRT connected: LCD on, CRT off + * 3. BOTH on + * 0. BOTH off + * 1. LCD off, CRT on + */ + + bin_state++; + if (bin_state > mypow(2, VOD_NUM) - 1) + bin_state = 0; + + for (i = 0; i < VOD_NUM; i++) + vo_set_state(i, bin_state & (1 << i)); + + return bin_state; +} + +static int camera_handler(int status) +{ + int value; + + value = ec_read(REG_CAMERA_CONTROL); + ec_write(REG_CAMERA_CONTROL, value | (1 << 1)); + + return status; +} + +static int usb2_handler(int status) +{ + pr_emerg("USB2 Over Current occurred\n"); + + return status; +} + +static int usb0_handler(int status) +{ + pr_emerg("USB0 Over Current occurred\n"); + + return status; +} + +static int ac_bat_handler(int status) +{ + if (ac_bat_initialized) { + power_supply_changed(&yeeloong_ac); + power_supply_changed(&yeeloong_bat); + } + + return status; +} + +struct sci_event { + int reg; + sci_handler handler; +}; + +static const struct sci_event se[] = { + [EVENT_AC_BAT] = {0, ac_bat_handler}, + [EVENT_AUDIO_MUTE] = {REG_AUDIO_MUTE, NULL}, + [EVENT_AUDIO_VOLUME] = {REG_AUDIO_VOLUME, NULL}, + [EVENT_CRT_DETECT] = {REG_CRT_DETECT, crt_detect_handler}, + [EVENT_CAMERA] = {REG_CAMERA_STATUS, camera_handler}, + [EVENT_DISPLAYTOGGLE] = {REG_DISPLAY_LCD, displaytoggle_handler}, + [EVENT_DISPLAY_BRIGHTNESS] = {REG_DISPLAY_BRIGHTNESS, NULL}, + [EVENT_LID] = {REG_LID_DETECT, NULL}, + [EVENT_SWITCHVIDEOMODE] = {0, switchvideomode_handler}, + [EVENT_USB_OC0] = {REG_USB2_FLAG, usb0_handler}, + [EVENT_USB_OC2] = {REG_USB2_FLAG, usb2_handler}, +}; + +static void do_event_action(int event) +{ + int status; + struct key_entry *ke; + struct sci_event *sep; + + sep = (struct sci_event *)&se[event]; + + if (sep->reg != 0) + status = ec_read(sep->reg); + + if (sep->handler != NULL) + status = sep->handler(status); + + pr_debug("%s: event: %d status: %d\n", __func__, event, status); + + /* Report current key to user-space */ + ke = get_event_key_entry(event, status); + + /* + * Ignore the LID and SLEEP event when we are already in sleep or + * reboot state, this will avoid the recursive pm operations. but note: + * the report_lid_switch() called in arch/mips/loongson/lemote-2f/pm.c + * is necessary, because it is used to wake the system from sleep + * state. In the future, perhaps SW_LID should works like SLEEP, no + * need to function as a SWITCH, just report the state when the LID is + * closed is enough, this event can tell the software to "SLEEP", no + * need to tell the softwares when we are resuming from "SLEEP". + */ + if (ke && !is_fake_event(ke->keycode)) { + if (ke->keycode == SW_LID) + report_lid_switch(status); + else + sparse_keymap_report_entry(yeeloong_hotkey_dev, ke, 1, + true); + } +} + +/* + * SCI(system control interrupt) main interrupt routine + * + * We will do the query and get event number together so the interrupt routine + * should be longer than 120us now at least 3ms elpase for it. + */ +static irqreturn_t sci_irq_handler(int irq, void *dev_id) +{ + int ret, event; + + if (SCI_IRQ_NUM != irq) + return IRQ_NONE; + + /* Query the event number */ + ret = ec_query_event_num(); + if (ret < 0) + return IRQ_NONE; + + event = ec_get_event_num(); + if (event < EVENT_START || event > EVENT_END) + return IRQ_NONE; + + /* Execute corresponding actions */ + do_event_action(event); + + return IRQ_HANDLED; +} + +/* + * Config and init some msr and gpio register properly. + */ +static int sci_irq_init(void) +{ + u32 hi, lo; + u32 gpio_base; + unsigned long flags; + int ret; + + /* Get gpio base */ + _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo); + gpio_base = lo & 0xff00; + + /* Filter the former kb3310 interrupt for security */ + ret = ec_query_event_num(); + if (ret) + return ret; + + /* For filtering next number interrupt */ + udelay(10000); + + /* Set gpio native registers and msrs for GPIO27 SCI EVENT PIN + * gpio : + * input, pull-up, no-invert, event-count and value 0, + * no-filter, no edge mode + * gpio27 map to Virtual gpio0 + * msr : + * no primary and lpc + * Unrestricted Z input to IG10 from Virtual gpio 0. + */ + local_irq_save(flags); + _rdmsr(0x80000024, &hi, &lo); + lo &= ~(1 << 10); + _wrmsr(0x80000024, hi, lo); + _rdmsr(0x80000025, &hi, &lo); + lo &= ~(1 << 10); + _wrmsr(0x80000025, hi, lo); + _rdmsr(0x80000023, &hi, &lo); + lo |= (0x0a << 0); + _wrmsr(0x80000023, hi, lo); + local_irq_restore(flags); + + /* Set gpio27 as sci interrupt + * + * input, pull-up, no-fliter, no-negedge, invert + * the sci event is just about 120us + */ + asm(".set noreorder\n"); + /* input enable */ + outl(0x00000800, (gpio_base | 0xA0)); + /* revert the input */ + outl(0x00000800, (gpio_base | 0xA4)); + /* event-int enable */ + outl(0x00000800, (gpio_base | 0xB8)); + asm(".set reorder\n"); + + return 0; +} + +static int notify_reboot(struct notifier_block *nb, unsigned long event, void *buf) +{ + switch (event) { + case SYS_RESTART: + case SYS_HALT: + case SYS_POWER_OFF: + atomic_set(in_reboot(), 1); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int notify_pm(struct notifier_block *nb, unsigned long event, void *buf) +{ + switch (event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + atomic_inc(in_sleep()); + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + case PM_RESTORE_PREPARE: /* do we need this ?? */ + atomic_dec(in_sleep()); + break; + default: + return NOTIFY_DONE; + } + + pr_debug("%s: event = %lu, in_sleep() = %d\n", __func__, event, + atomic_read(in_sleep())); + + return NOTIFY_OK; +} + +static struct notifier_block reboot_notifier = { + .notifier_call = notify_reboot, +}; + +static struct notifier_block pm_notifier = { + .notifier_call = notify_pm, +}; + +static int yeeloong_hotkey_init(void) +{ + int ret = 0; + + ret = register_reboot_notifier(&reboot_notifier); + if (ret) { + pr_err("can't register reboot notifier\n"); + goto end; + } + + ret = register_pm_notifier(&pm_notifier); + if (ret) { + pr_err("can't register pm notifier\n"); + goto free_reboot_notifier; + } + + ret = sci_irq_init(); + if (ret) { + pr_err("can't init SCI interrupt\n"); + goto free_pm_notifier; + } + + ret = request_threaded_irq(SCI_IRQ_NUM, NULL, &sci_irq_handler, + IRQF_ONESHOT, "sci", NULL); + if (ret) { + pr_err("can't thread SCI interrupt handler\n"); + goto free_pm_notifier; + } + + yeeloong_hotkey_dev = input_allocate_device(); + + if (!yeeloong_hotkey_dev) { + ret = -ENOMEM; + goto free_irq; + } + + yeeloong_hotkey_dev->name = "HotKeys"; + yeeloong_hotkey_dev->phys = "button/input0"; + yeeloong_hotkey_dev->id.bustype = BUS_HOST; + yeeloong_hotkey_dev->dev.parent = NULL; + + ret = sparse_keymap_setup(yeeloong_hotkey_dev, yeeloong_keymap, NULL); + if (ret) { + pr_err("Fail to setup input device keymap\n"); + goto free_dev; + } + + ret = input_register_device(yeeloong_hotkey_dev); + if (ret) + goto free_keymap; + + /* Update the current status of LID */ + report_lid_switch(ON); + +#ifdef CONFIG_LOONGSON_SUSPEND + /* Install the real yeeloong_report_lid_status for pm.c */ + yeeloong_report_lid_status = report_lid_switch; +#endif + return 0; + +free_keymap: + sparse_keymap_free(yeeloong_hotkey_dev); +free_dev: + input_free_device(yeeloong_hotkey_dev); +free_irq: + free_irq(SCI_IRQ_NUM, NULL); +free_pm_notifier: + unregister_pm_notifier(&pm_notifier); +free_reboot_notifier: + unregister_reboot_notifier(&reboot_notifier); +end: + return ret; +} + +static void yeeloong_hotkey_exit(void) +{ + /* Free irq */ + free_irq(SCI_IRQ_NUM, NULL); + +#ifdef CONFIG_LOONGSON_SUSPEND + /* Uninstall yeeloong_report_lid_status for pm.c */ + if (yeeloong_report_lid_status == report_lid_switch) + yeeloong_report_lid_status = NULL; +#endif + + if (yeeloong_hotkey_dev) { + sparse_keymap_free(yeeloong_hotkey_dev); + input_unregister_device(yeeloong_hotkey_dev); + yeeloong_hotkey_dev = NULL; + } +} + +#ifdef CONFIG_PM +static void usb_ports_set(int status) +{ + status = !!status; + + ec_write(REG_USB0_FLAG, status); + ec_write(REG_USB1_FLAG, status); + ec_write(REG_USB2_FLAG, status); +} + +static int yeeloong_suspend(struct device *dev) + +{ + if (ec_version_before("EC_VER=PQ1D27")) + vo_set_state(LCD, OFF); + vo_set_state(CRT, OFF); + usb_ports_set(OFF); + + return 0; +} + +static int yeeloong_resume(struct device *dev) +{ + int ret; + + if (ec_version_before("EC_VER=PQ1D27")) + vo_set_state(LCD, ON); + vo_set_state(CRT, ON); + usb_ports_set(ON); + + ret = sci_irq_init(); + if (ret) + return -EFAULT; + + return 0; +} + +static const SIMPLE_DEV_PM_OPS(yeeloong_pm_ops, yeeloong_suspend, + yeeloong_resume); +#endif + +static struct platform_device_id platform_device_ids[] = { + { + .name = "yeeloong_laptop", + }, + {} +}; + +MODULE_DEVICE_TABLE(platform, platform_device_ids); + +static struct platform_driver platform_driver = { + .driver = { + .name = "yeeloong_laptop", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &yeeloong_pm_ops, +#endif + }, + .id_table = platform_device_ids, +}; + +static int __init yeeloong_init(void) +{ + int ret; + + pr_info("Load YeeLoong Laptop Platform Specific Driver.\n"); + + /* Register platform stuff */ + ret = platform_driver_register(&platform_driver); + if (ret) { + pr_err("Fail to register yeeloong platform driver.\n"); + return ret; + } + +#define yeeloong_init_drv(drv, alias) do { \ + pr_info("Register yeeloong " alias " driver.\n"); \ + ret = yeeloong_ ## drv ## _init(); \ + if (ret) { \ + pr_err("Fail to register yeeloong " alias " driver.\n"); \ + yeeloong_ ## drv ## _exit(); \ + return ret; \ + } \ +} while (0) + + yeeloong_init_drv(backlight, "backlight"); + yeeloong_init_drv(bat, "battery and AC"); + yeeloong_init_drv(hwmon, "hardware monitor"); + yeeloong_init_drv(vo, "video output"); + yeeloong_init_drv(lcd, "lcd output"); + yeeloong_init_drv(hotkey, "hotkey input"); + + return 0; +} + +static void __exit yeeloong_exit(void) +{ + yeeloong_hotkey_exit(); + yeeloong_lcd_exit(); + yeeloong_vo_exit(); + yeeloong_hwmon_exit(); + yeeloong_bat_exit(); + yeeloong_backlight_exit(); + platform_driver_unregister(&platform_driver); + + pr_info("Unload YeeLoong Platform Specific Driver.\n"); +} + +module_init(yeeloong_init); +module_exit(yeeloong_exit); + +MODULE_AUTHOR("Wu Zhangjin ; Liu Junliang "); +MODULE_DESCRIPTION("YeeLoong laptop driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 5a538fc..1b45006 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -481,6 +481,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64 + depends on !DEXXON_GDIUM default y if X86 help Say "yes" here to get direct support for the real time clock diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 05beb6c..3bf1f21 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -691,8 +691,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) /* FIXME: * doesn't know 12-hour mode either. */ - if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { - dev_warn(dev, "only 24-hr supported\n"); + if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { + dev_dbg(dev, "only 24-hr supported\n"); retval = -ENXIO; goto cleanup1; } diff --git a/drivers/staging/sm7xx/smtcfb.c b/drivers/staging/sm7xx/smtcfb.c index a164fc4..f3043cc 100644 --- a/drivers/staging/sm7xx/smtcfb.c +++ b/drivers/staging/sm7xx/smtcfb.c @@ -15,6 +15,8 @@ * License. See the file COPYING in the main directory of this archive for * more details. * + * - Remove the buggy 2D support for Lynx, 2010/01/06, Wu Zhangjin + * * Version 0.10.26192.21.01 * - Add PowerPC/Big endian support * - Verified on 2.6.19.2 @@ -106,6 +108,7 @@ static struct vesa_mode_table vesa_mode[] = { {"0x307", 1280, 1024, 8}, {"0x311", 640, 480, 16}, + {"0x313", 800, 480, 16}, {"0x314", 800, 600, 16}, {"0x317", 1024, 768, 16}, {"0x31A", 1280, 1024, 16}, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f9cf3f0..5fc952d 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -833,9 +833,13 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } if (ints & OHCI_INTR_WDH) { - spin_lock (&ohci->lock); - dl_done_list (ohci); - spin_unlock (&ohci->lock); + if (ohci->hcca->done_head == 0) { + ints &= ~OHCI_INTR_WDH; + } else { + spin_lock (&ohci->lock); + dl_done_list (ohci); + spin_unlock (&ohci->lock); + } } if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) { diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 629a968..dc80087 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -55,6 +55,16 @@ #define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ #define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */ + +static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) +{ + u16 cmd; + return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask); +} + +#define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO) +#define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY) + /* AMD quirk use */ #define AB_REG_BAR_LOW 0xe0 #define AB_REG_BAR_HIGH 0xe1 @@ -348,6 +358,12 @@ void usb_amd_dev_put(void) } EXPORT_SYMBOL_GPL(usb_amd_dev_put); +static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx) +{ + return pci_resource_start(pdev, idx) && mmio_enabled(pdev); +} + +#if defined(CONFIG_USB_UHCI_HCD) || defined(CONFIG_USB_UHCI_HCD_MODULE) /* * Make sure the controller is completely inactive, unable to * generate interrupts or do DMA. @@ -429,15 +445,6 @@ reset_needed: } EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); -static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) -{ - u16 cmd; - return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask); -} - -#define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO) -#define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY) - static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) { unsigned long base = 0; @@ -455,12 +462,11 @@ static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) if (base) uhci_check_and_reset_hc(pdev, base); } +#else +#define quirk_usb_handoff_uhci(x) do { } while (0) +#endif /* CONFIG_USB_UHCI_HCD* */ -static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx) -{ - return pci_resource_start(pdev, idx) && mmio_enabled(pdev); -} - +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) { void __iomem *base; @@ -534,7 +540,11 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) iounmap(base); } +#else +#define quirk_usb_handoff_ohci(x) do { } while(0) +#endif /* CONFIG_USB_OHCI_HCD* */ +#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE) static const struct dmi_system_id __devinitconst ehci_dmi_nohandoff_table[] = { { /* Pegatron Lucid (ExoPC) */ @@ -688,6 +698,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) iounmap(base); } +#else +#define quirk_usb_disable_ehci(x) do { } while (0) +#endif /* CONFIG_USB_EHCI_HCD* */ /* * handshake - spin reading a register until handshake completes @@ -776,6 +789,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) } EXPORT_SYMBOL_GPL(usb_enable_xhci_ports); +#if defined(CONFIG_USB_XHCI_HCD) || defined(CONFIG_USB_XHCI_HCD_MODULE) /** * PCI Quirks for xHCI. * @@ -870,6 +884,9 @@ hc_init: iounmap(base); } +#else +#define quirk_usb_handoff_xhci(x) do { } while (0) +#endif /* CONFIG_USB_UHCI_HCD* */ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) { diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fe22e90..5534ed6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -78,6 +78,9 @@ static void option_instat_callback(struct urb *urb); #define OPTION_PRODUCT_ETNA_KOI_MODEM 0x7100 #define OPTION_PRODUCT_GTM380_MODEM 0x7201 +#define HUAWO_VENDOR_ID 0x21F5 +#define HUAWO_PRODUCT_E1621 0x2008 + #define HUAWEI_VENDOR_ID 0x12D1 #define HUAWEI_PRODUCT_E600 0x1001 #define HUAWEI_PRODUCT_E220 0x1003 @@ -533,6 +536,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) }, { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) }, { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) }, + { USB_DEVICE(HUAWO_VENDOR_ID, HUAWO_PRODUCT_E1621) }, /* QUANTA 6500 chips, Unicom extensive use of this card */ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) }, diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8b83129..a19e44e 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -133,7 +133,7 @@ obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o obj-$(CONFIG_FB_SH_MOBILE_MERAM) += sh_mobile_meram.o obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o obj-$(CONFIG_FB_OMAP) += omap/ -obj-y += omap2/ +obj-$(CONFIG_FB_OMAP2) += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 02fde50..a8677f0 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -27,6 +27,9 @@ extern unsigned long sm501_set_clock(struct device *dev, extern unsigned long sm501_find_clock(struct device *dev, int clksrc, unsigned long req_freq); +extern void sm501_configure_gpio(struct device *dev, + unsigned int gpio, unsigned char mode); + /* sm501_misc_control * * Modify the SM501's MISC_CONTROL register @@ -122,6 +125,7 @@ struct sm501_reg_init { #define SM501_USE_AC97 (1<<7) #define SM501_USE_I2S (1<<8) #define SM501_USE_GPIO (1<<9) +#define SM501_USE_PWM (1<<10) #define SM501_USE_ALL (0xffffffff) diff --git a/init/calibrate.c b/init/calibrate.c index 24df79768..fb1d404 100644 --- a/init/calibrate.c +++ b/init/calibrate.c @@ -21,6 +21,7 @@ static int __init lpj_setup(char *str) __setup("lpj=", lpj_setup); +#ifndef ARCH_HAS_PREPARED_LPJ #ifdef ARCH_HAS_READ_CURRENT_TIMER /* This routine uses the read_current_timer() routine and gets the @@ -168,6 +169,7 @@ static unsigned long __cpuinit calibrate_delay_direct(void) #else static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;} #endif +#endif /* ARCH_HAS_PREPARED_LPJ */ /* * This is the number of bits of precision for the loops_per_jiffy. Each @@ -265,6 +267,7 @@ void __cpuinit calibrate_delay(void) lpj = lpj_fine; pr_info("Calibrating delay loop (skipped), " "value calculated using timer frequency.. "); +#ifndef ARCH_HAS_PREPARED_LPJ } else if ((lpj = calibrate_delay_direct()) != 0) { if (!printed) pr_info("Calibrating delay using timer " @@ -273,7 +276,9 @@ void __cpuinit calibrate_delay(void) if (!printed) pr_info("Calibrating delay loop... "); lpj = calibrate_delay_converge(); +#endif /* ARCH_HAS_PREPARED_LPJ */ } + per_cpu(cpu_loops_per_jiffy, this_cpu) = lpj; if (!printed) pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n", diff --git a/net/rfkill/core.c b/net/rfkill/core.c index be90640..6157b67 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -112,7 +112,7 @@ static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_global_mutex); static LIST_HEAD(rfkill_fds); /* list of open fds of /dev/rfkill */ -static unsigned int rfkill_default_state = 1; +static unsigned int rfkill_default_state; /* default: 0 = radio off */ module_param_named(default_state, rfkill_default_state, uint, 0444); MODULE_PARM_DESC(default_state, "Default initial state for all radio types, 0 = radio off"); diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 858966a..537470e 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -307,14 +307,33 @@ if ($arch eq "x86_64") { $cc .= " -m64"; $objcopy .= " -O elf64-sparc"; } elsif ($arch eq "mips") { - # To enable module support, we need to enable the -mlong-calls option - # of gcc for module, after using this option, we can not get the real - # offset of the calling to _mcount, but the offset of the lui - # instruction or the addiu one. herein, we record the address of the - # first one, and then we can replace this instruction by a branch - # instruction to jump over the profiling function to filter the - # indicated functions, or swith back to the lui instruction to trace - # them, which means dynamic tracing. + # + # To disable tracing, just replace "jal _mcount" with nop; + # to enable tracing, replace back. so, the offset 14 is + # needed to be recorded. + # + # 10: 03e0082d move at,ra + # 14: 0c000000 jal 0 + # 14: R_MIPS_26 _mcount + # 14: R_MIPS_NONE *ABS* + # 14: R_MIPS_NONE *ABS* + # 18: 00020021 nop + # + # + # + # If no long call(-mlong-calls), the same to kernel. + # + # If the module space differs from the kernel space, long + # call is needed, as a result, the address of _mcount is + # needed to be recorded in a register and then jump from + # module space to kernel space via "jalr ". To + # disable tracing, "jalr " can be replaced by + # nop; to enable tracing, replace it back. Since the + # offset of "jalr " is not easy to be matched, + # the offset of the 1st _mcount below is recorded and to + # disable tracing, "lui v1, 0x0" is substituted with "b + # label", which jumps over "jalr "; to enable + # tracing, replace it back. # # c: 3c030000 lui v1,0x0 # c: R_MIPS_HI16 _mcount @@ -326,19 +345,12 @@ if ($arch eq "x86_64") { # 10: R_MIPS_NONE *ABS* # 14: 03e0082d move at,ra # 18: 0060f809 jalr v1 + # label: # - # for the kernel: - # - # 10: 03e0082d move at,ra - # 14: 0c000000 jal 0 - # 14: R_MIPS_26 _mcount - # 14: R_MIPS_NONE *ABS* - # 14: R_MIPS_NONE *ABS* - # 18: 00020021 nop if ($is_module eq "0") { $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_26\\s+_mcount\$"; } else { - $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_HI16\\s+_mcount\$"; + $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_(HI16|26)\\s+_mcount\$"; } $objdump .= " -Melf-trad".$endian."mips "; diff --git a/scripts/sstrip.sh b/scripts/sstrip.sh new file mode 100755 index 0000000..49b973a --- /dev/null +++ b/scripts/sstrip.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# sstrip.sh -- strip the section table of an elf file +# +# Copyright (C) 2010 Wu Zhangjin, wuzhangjin@gmail.com +# Licensed under the GPLv2 +# +# Since the section table is useless for the embedded device, it can be +# stripped out. +# +# Note: Some bootloader may check the section table but most of the time, it +# may be not really used, If it really need the section table, it may need the +# decompressed kernel image. + +# Usage + +function usage +{ +cat </dev/null` +[ "xELF" != "x${FILE_TYPE}" ] && echo "$0: ${IMAGE} is not an ELF file" && exit -1 + +[ "x${V}" == "x1" ] && orig_filesz=`wc -c ${IMAGE} | cut -d' ' -f1` + +# Get the offset of the section table, here get the end of the program section +filesz=$((`${OBJDUMP} -p ${IMAGE} | grep -m1 filesz | tr -s ' ' | cut -d' ' -f3`)) + +# Truncate it via the dd tool +dd if=/dev/null bs=1 of=${IMAGE} seek=${filesz} 2>/dev/null + +# Clear the section table information in the ELF header +# The last 6 bytes of the ELF header are the section table information +echo -ne "\x00\x00\x00\x00\x00\x00" | dd of=${IMAGE} bs=1 seek=46 count=6 conv=notrunc 2>/dev/null + +# Debug +if [ "x${V}" == "x1" ]; then + echo "----------------------------------------------------------------" + echo "Strip the section table at ${filesz} of ${IMAGE}" + echo "----------------------------------------------------------------" + echo " sstrip: $0" + echo " objdump: ${OBJDUMP}" + echo "original size: ${orig_filesz}" + echo "current size: ${filesz}" + echo "reduced size: $((${orig_filesz} - ${filesz}))" +fi