summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/linux/linux-h6300-omap1-2.6.14-rc5/.mtn2git_empty0
-rw-r--r--packages/linux/linux-h6300-omap1-2.6.14-rc5/defconfig1538
-rw-r--r--packages/linux/linux-h6300-omap1-2.6.14-rc5/h6300_omap1_2614rc5.patch30881
-rw-r--r--packages/linux/linux-h6300-omap1_2.6.14-rc5.bb18
4 files changed, 32437 insertions, 0 deletions
diff --git a/packages/linux/linux-h6300-omap1-2.6.14-rc5/.mtn2git_empty b/packages/linux/linux-h6300-omap1-2.6.14-rc5/.mtn2git_empty
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/packages/linux/linux-h6300-omap1-2.6.14-rc5/.mtn2git_empty
diff --git a/packages/linux/linux-h6300-omap1-2.6.14-rc5/defconfig b/packages/linux/linux-h6300-omap1-2.6.14-rc5/defconfig
new file mode 100644
index 0000000000..8e76562fb3
--- /dev/null
+++ b/packages/linux/linux-h6300-omap1-2.6.14-rc5/defconfig
@@ -0,0 +1,1538 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.14-rc5-omap-h6300
+# Sun Oct 30 09:16:57 2005
+#
+CONFIG_ARM=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_CLEAN_COMPILE is not set
+CONFIG_BROKEN=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_HOTPLUG=y
+CONFIG_KOBJECT_UEVENT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_CAMELOT is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_IOP3XX is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+CONFIG_ARCH_OMAP=y
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_AAEC2000 is not set
+
+#
+# TI OMAP Implementations
+#
+CONFIG_ARCH_OMAP1=y
+# CONFIG_ARCH_OMAP2 is not set
+
+#
+# OMAP Feature Selections
+#
+# CONFIG_OMAP_RESET_CLOCKS is not set
+# CONFIG_OMAP_BOOT_TAG is not set
+CONFIG_OMAP_MUX=y
+# CONFIG_OMAP_MUX_DEBUG is not set
+CONFIG_OMAP_MUX_WARNINGS=y
+CONFIG_OMAP_MPU_TIMER=y
+# CONFIG_OMAP_32K_TIMER is not set
+CONFIG_OMAP_LL_DEBUG_UART1=y
+# CONFIG_OMAP_LL_DEBUG_UART2 is not set
+# CONFIG_OMAP_LL_DEBUG_UART3 is not set
+CONFIG_OMAP_SERIAL_WAKE=y
+
+#
+# OMAP Core Type
+#
+# CONFIG_ARCH_OMAP730 is not set
+CONFIG_ARCH_OMAP15XX=y
+# CONFIG_ARCH_OMAP16XX is not set
+
+#
+# OMAP Board Type
+#
+# CONFIG_MACH_OMAP_INNOVATOR is not set
+CONFIG_MACH_OMAP_H6300=y
+# CONFIG_MACH_VOICEBLUE is not set
+# CONFIG_MACH_NETSTAR is not set
+# CONFIG_MACH_OMAP_PALMTE is not set
+# CONFIG_MACH_OMAP_GENERIC is not set
+
+#
+# OMAP CPU Speed
+#
+# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set
+CONFIG_OMAP_ARM_168MHZ=y
+CONFIG_OMAP_ARM_150MHZ=y
+CONFIG_OMAP_ARM_120MHZ=y
+CONFIG_OMAP_ARM_60MHZ=y
+CONFIG_OMAP_ARM_30MHZ=y
+CONFIG_OMAP_DSP=y
+CONFIG_OMAP_DSP_MBCMD_VERBOSE=y
+CONFIG_OMAP_DSP_TASK_MULTIOPEN=y
+CONFIG_OMAP_DSP_FBEXPORT=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM925T=y
+CONFIG_CPU_32v4=y
+CONFIG_CPU_ABRT_EV4T=y
+CONFIG_CPU_CACHE_V4WT=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+CONFIG_CPU_DCACHE_WRITETHROUGH=y
+
+#
+# Bus support
+#
+CONFIG_ISA_DMA_API=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+CONFIG_PCCARD=m
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=m
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+
+#
+# Kernel Features
+#
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_NO_IDLE_HZ is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+CONFIG_LEDS_CPU=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=tty0 root=/dev/nfs nfsroot=192.168.2.1:/opt/h6300/rootfs,rsize=8192,wsize=8192 ip=192.168.2.2:192.168.2.1:192.168.2.1:255.0.0.0:ipaq:"
+# CONFIG_XIP_KERNEL is not set
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+CONFIG_FPE_NWFPE_XP=y
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_MISC=y
+# CONFIG_ARTHUR is not set
+
+#
+# Power management options
+#
+CONFIG_PM=y
+CONFIG_APM=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_BIC=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+# CONFIG_NETFILTER_NETLINK is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_CT_ACCT=y
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CONNTRACK_EVENTS is not set
+CONFIG_IP_NF_CT_PROTO_SCTP=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+# CONFIG_IP_NF_NETBIOS_NS is not set
+CONFIG_IP_NF_TFTP=m
+# CONFIG_IP_NF_AMANDA is not set
+# CONFIG_IP_NF_PPTP is not set
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+# CONFIG_IP_NF_MATCH_MARK is not set
+# CONFIG_IP_NF_MATCH_MULTIPORT is not set
+# CONFIG_IP_NF_MATCH_TOS is not set
+# CONFIG_IP_NF_MATCH_RECENT is not set
+# CONFIG_IP_NF_MATCH_ECN is not set
+# CONFIG_IP_NF_MATCH_DSCP is not set
+# CONFIG_IP_NF_MATCH_AH_ESP is not set
+# CONFIG_IP_NF_MATCH_LENGTH is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+# CONFIG_IP_NF_MATCH_TCPMSS is not set
+# CONFIG_IP_NF_MATCH_HELPER is not set
+# CONFIG_IP_NF_MATCH_STATE is not set
+# CONFIG_IP_NF_MATCH_CONNTRACK is not set
+# CONFIG_IP_NF_MATCH_OWNER is not set
+# CONFIG_IP_NF_MATCH_PHYSDEV is not set
+# CONFIG_IP_NF_MATCH_ADDRTYPE is not set
+# CONFIG_IP_NF_MATCH_REALM is not set
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_DCCP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+# CONFIG_IP_NF_MATCH_CONNMARK is not set
+# CONFIG_IP_NF_MATCH_CONNBYTES is not set
+# CONFIG_IP_NF_MATCH_HASHLIMIT is not set
+# CONFIG_IP_NF_MATCH_STRING is not set
+# CONFIG_IP_NF_FILTER is not set
+# CONFIG_IP_NF_TARGET_LOG is not set
+# CONFIG_IP_NF_TARGET_ULOG is not set
+# CONFIG_IP_NF_TARGET_TCPMSS is not set
+# CONFIG_IP_NF_TARGET_NFQUEUE is not set
+# CONFIG_IP_NF_NAT is not set
+CONFIG_IP_NF_MANGLE=m
+# CONFIG_IP_NF_TARGET_TOS is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_DSCP is not set
+# CONFIG_IP_NF_TARGET_MARK is not set
+# CONFIG_IP_NF_TARGET_CLASSIFY is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+# CONFIG_IP_NF_TARGET_CONNMARK is not set
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+# CONFIG_IP_NF_RAW is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+# CONFIG_IP6_NF_MATCH_LIMIT is not set
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_MULTIPORT is not set
+# CONFIG_IP6_NF_MATCH_OWNER is not set
+# CONFIG_IP6_NF_MATCH_MARK is not set
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+# CONFIG_IP6_NF_MATCH_AHESP is not set
+# CONFIG_IP6_NF_MATCH_LENGTH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_PHYSDEV is not set
+CONFIG_IP6_NF_FILTER=m
+# CONFIG_IP6_NF_TARGET_LOG is not set
+# CONFIG_IP6_NF_TARGET_REJECT is not set
+# CONFIG_IP6_NF_TARGET_NFQUEUE is not set
+CONFIG_IP6_NF_MANGLE=m
+# CONFIG_IP6_NF_TARGET_MARK is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+CONFIG_IP6_NF_RAW=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+# CONFIG_BRIDGE_EBT_BROUTE is not set
+# CONFIG_BRIDGE_EBT_T_FILTER is not set
+# CONFIG_BRIDGE_EBT_T_NAT is not set
+# CONFIG_BRIDGE_EBT_802_3 is not set
+# CONFIG_BRIDGE_EBT_AMONG is not set
+# CONFIG_BRIDGE_EBT_ARP is not set
+# CONFIG_BRIDGE_EBT_IP is not set
+# CONFIG_BRIDGE_EBT_LIMIT is not set
+# CONFIG_BRIDGE_EBT_MARK is not set
+# CONFIG_BRIDGE_EBT_PKTTYPE is not set
+# CONFIG_BRIDGE_EBT_STP is not set
+# CONFIG_BRIDGE_EBT_VLAN is not set
+# CONFIG_BRIDGE_EBT_ARPREPLY is not set
+# CONFIG_BRIDGE_EBT_DNAT is not set
+# CONFIG_BRIDGE_EBT_MARK_T is not set
+# CONFIG_BRIDGE_EBT_REDIRECT is not set
+# CONFIG_BRIDGE_EBT_SNAT is not set
+# CONFIG_BRIDGE_EBT_LOG is not set
+# CONFIG_BRIDGE_EBT_ULOG is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+CONFIG_BRIDGE=y
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+CONFIG_IRDA_ULTRA=y
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+
+#
+# Old SIR device drivers
+#
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+CONFIG_OMAP1610_IR=m
+CONFIG_SMC_IRCC_FIR=m
+# CONFIG_ALI_FIR is not set
+# CONFIG_VIA_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+# CONFIG_BT_SCO is not set
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+# CONFIG_BT_HCIUSB is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_BCSP_TXCRC is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIDTL1 is not set
+# CONFIG_BT_HCIBT3C is not set
+# CONFIG_BT_HCIBLUECARD is not set
+# CONFIG_BT_HCIBTUART is not set
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_H6300=m
+# CONFIG_IEEE80211 is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=m
+CONFIG_MTD_JEDECPROBE=m
+CONFIG_MTD_GEN_PROBE=m
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_GEOMETRY is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=m
+CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=1
+CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+CONFIG_MTD_ABSENT=m
+# CONFIG_MTD_OBSOLETE_CHIPS is not set
+# CONFIG_MTD_XIP is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_PHYSMAP=m
+CONFIG_MTD_PHYSMAP_START=0x8000000
+CONFIG_MTD_PHYSMAP_LEN=0x4000000
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_EDB7312 is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_OMAP_NOR is not set
+# CONFIG_MTD_PCMCIA is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+CONFIG_MTD_SLRAM=m
+CONFIG_MTD_PHRAM=m
+CONFIG_MTD_MTDRAM=m
+CONFIG_MTDRAM_TOTAL_SIZE=4096
+CONFIG_MTDRAM_ERASE_SIZE=128
+CONFIG_MTD_BLKMTD=m
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+CONFIG_MTD_DOC2000=m
+CONFIG_MTD_DOC2001=m
+CONFIG_MTD_DOC2001PLUS=m
+CONFIG_MTD_DOCPROBE=m
+CONFIG_MTD_DOCECC=m
+CONFIG_MTD_DOCPROBE_ADVANCED=y
+CONFIG_MTD_DOCPROBE_ADDRESS=0x0000
+CONFIG_MTD_DOCPROBE_HIGH=y
+CONFIG_MTD_DOCPROBE_55AA=y
+
+#
+# NAND Flash Device Drivers
+#
+CONFIG_MTD_NAND=m
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_TOTO is not set
+CONFIG_MTD_NAND_IDS=m
+CONFIG_MTD_NAND_DISKONCHIP=m
+CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED=y
+CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0x0
+# CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH is not set
+CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE=y
+# CONFIG_MTD_NAND_NANDSIM is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# PHY device support
+#
+# CONFIG_PHYLIB is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_SMC91X=y
+# CONFIG_DM9000 is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+
+#
+# Obsolete Wireless cards support (pre-802.11)
+#
+CONFIG_STRIP=y
+# CONFIG_PCMCIA_WAVELAN is not set
+# CONFIG_PCMCIA_NETWAVE is not set
+
+#
+# Wireless 802.11 Frequency Hopping cards support
+#
+# CONFIG_PCMCIA_RAYCS is not set
+
+#
+# Wireless 802.11b ISA/PCI cards support
+#
+# CONFIG_AIRO is not set
+# CONFIG_HERMES is not set
+CONFIG_ATMEL=y
+
+#
+# Wireless 802.11b Pcmcia/Cardbus cards support
+#
+# CONFIG_AIRO_CS is not set
+# CONFIG_PCMCIA_ATMEL is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_HOSTAP is not set
+CONFIG_NET_WIRELESS=y
+CONFIG_ACX=m
+# CONFIG_ACX_USB is not set
+CONFIG_ACX_CFI=y
+
+#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPPOE=y
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_TSDEV=y
+CONFIG_INPUT_TSDEV_SCREEN_X=240
+CONFIG_INPUT_TSDEV_SCREEN_Y=320
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+CONFIG_KEYBOARD_OMAP=m
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_SERIAL=y
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_INPUT_JOYSTICK is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_OMAP=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIO_RAW=y
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+CONFIG_SOFT_WATCHDOG=m
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_NVRAM=y
+# CONFIG_RTC is not set
+CONFIG_OMAP_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+CONFIG_I2C_CHARDEV=m
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_PCA_ISA is not set
+CONFIG_I2C_OMAP=m
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+CONFIG_PCA9535=m
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 is not set
+# CONFIG_TPS65010 is not set
+CONFIG_SENSORS_TLV320AIC23=m
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# Hardware Monitoring support
+#
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_FSCHER is not set
+# CONFIG_SENSORS_FSCPOS is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia Capabilities Port drivers
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+CONFIG_FB=y
+# CONFIG_FB_CFB_FILLRECT is not set
+# CONFIG_FB_CFB_COPYAREA is not set
+# CONFIG_FB_CFB_IMAGEBLIT is not set
+CONFIG_FB_SOFT_CURSOR=y
+# CONFIG_FB_MACMODES is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_OMAP=y
+CONFIG_FB_OMAP_LCDC_INTERNAL=y
+# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set
+CONFIG_FB_OMAP_DMA_TUNE=y
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FONT_MINI_4x6=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+CONFIG_LOGO_LINUX_VGA16=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_DEVICE=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_DEVICE=y
+
+#
+# Telephony Support
+#
+CONFIG_PHONE=m
+# CONFIG_PHONE_IXJ is not set
+CONFIG_GSM_H6300=m
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+# CONFIG_SND is not set
+
+#
+# Open Sound System
+#
+CONFIG_SOUND_PRIME=m
+CONFIG_SOUND_OMAP=m
+CONFIG_SOUND_OMAP_TSC2101=m
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+# CONFIG_SOUND_AD1980 is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_OBSOLETE_OSS_USB_DRIVER is not set
+
+#
+# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem
+#
+CONFIG_USB_ACM=y
+# CONFIG_USB_PRINTER is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+# CONFIG_USB_STORAGE is not set
+
+#
+# USB Input Devices
+#
+# CONFIG_USB_HID is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+# CONFIG_USB_AIPTEK is not set
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_ACECAD is not set
+# CONFIG_USB_KBTAB is not set
+# CONFIG_USB_POWERMATE is not set
+# CONFIG_USB_MTOUCH is not set
+# CONFIG_USB_ITMTOUCH is not set
+# CONFIG_USB_EGALAX is not set
+# CONFIG_USB_YEALINK is not set
+# CONFIG_USB_XPAD is not set
+# CONFIG_USB_ATI_REMOTE is not set
+# CONFIG_USB_KEYSPAN_REMOTE is not set
+# CONFIG_USB_APPLETOUCH is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+
+#
+# USB Multimedia devices
+#
+# CONFIG_USB_DABUSB is not set
+
+#
+# Video4Linux support is needed for USB Multimedia device support
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=y
+CONFIG_USB_NET_CDCETHER=y
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=y
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+CONFIG_USB_NET_ZAURUS=y
+# CONFIG_USB_ZD1201 is not set
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CONSOLE=y
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_CP2101 is not set
+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+CONFIG_USB_SERIAL_VISOR=y
+CONFIG_USB_SERIAL_IPAQ=y
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_GARMIN is not set
+# CONFIG_USB_SERIAL_IPW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+# CONFIG_USB_SERIAL_TI is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_OPTION is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGETKIT is not set
+# CONFIG_USB_PHIDGETSERVO is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+CONFIG_USB_GADGET_OMAP=y
+CONFIG_USB_OMAP=y
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+# CONFIG_USB_ETH_RNDIS is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+
+#
+# MMC/SD Card support
+#
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BROKEN_RFD=y
+CONFIG_MMC_BULKTRANSFER=y
+CONFIG_MMC_OMAP=y
+# CONFIG_MMC_WBSD is not set
+
+#
+# Synchronous Serial Interfaces (SSI)
+#
+CONFIG_OMAP_UWIRE=y
+CONFIG_OMAP_TSC2101=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_ROMFS_FS=y
+CONFIG_INOTIFY=y
+# CONFIG_QUOTA is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+# CONFIG_RELAYFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS_FS=y
+CONFIG_JFFS_FS_VERBOSE=0
+CONFIG_JFFS_PROC_FS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=4
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+CONFIG_CRAMFS=y
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=y
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_FS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_WAITQ is not set
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_LL=y
+# CONFIG_DEBUG_ICEDCC is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_DEFLATE=m
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_REED_SOLOMON=m
+CONFIG_REED_SOLOMON_DEC16=y
diff --git a/packages/linux/linux-h6300-omap1-2.6.14-rc5/h6300_omap1_2614rc5.patch b/packages/linux/linux-h6300-omap1-2.6.14-rc5/h6300_omap1_2614rc5.patch
new file mode 100644
index 0000000000..435a5f12a2
--- /dev/null
+++ b/packages/linux/linux-h6300-omap1-2.6.14-rc5/h6300_omap1_2614rc5.patch
@@ -0,0 +1,30881 @@
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/Kconfig bt_kernel/arch/arm/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/Kconfig 2005-10-30 17:44:14.719521158 +0200
++++ bt_kernel/arch/arm/Kconfig 2005-10-14 18:55:31.156317000 +0300
+@@ -725,6 +725,8 @@
+
+ source "drivers/video/Kconfig"
+
++source "drivers/telephony/Kconfig"
++
+ source "sound/Kconfig"
+
+ source "drivers/usb/Kconfig"
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap1/board-h6300.c bt_kernel/arch/arm/mach-omap1/board-h6300.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap1/board-h6300.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/arch/arm/mach-omap1/board-h6300.c 2005-10-22 03:52:45.687256000 +0300
+@@ -0,0 +1,317 @@
++/*
++ * Modified from board-h6300.c
++ *
++ * Code for generic OMAP board. Should work on many OMAP systems where
++ * the device drivers take care of all the necessary hardware initialization.
++ * Do not put any board specific code to this file; create a new machine
++ * type if you need custom low-level initializations.
++ *
++ * 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 <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/mach/arch.h>
++#include <asm/mach/flash.h>
++#include <asm/mach/map.h>
++
++#include <asm/arch/gpio.h>
++
++#include <asm/arch/tc.h>
++#include <asm/arch/usb.h>
++
++#include <asm/arch/common.h>
++
++#include <asm/arch/h6300_uart_info.h>
++
++/*
++ * Bluetooth - Relies on h6300_bt module,
++ * so make the calls indirectly through pointers. Requires that the
++ * h6300_bt bluetooth module be loaded before any attempt to use
++ * bluetooth (obviously).
++ */
++
++static struct h6300_uart_funcs bt_funcs;
++static struct h6300_uart_funcs gsm_funcs;
++
++static void
++h6300_bt_configure(struct uart_omap_port *up, int enable)
++{
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_configure() started\n");
++ if (bt_funcs.configure != NULL)
++ bt_funcs.configure(up, enable);
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_configure() done\n");
++}
++
++static void
++h6300_bt_set_txrx(struct uart_omap_port *up, int txrx)
++{
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_set_txrx() started\n");
++ if (bt_funcs.set_txrx != NULL)
++ {
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_set_txrx(), bt_funcs.set_txrx != NULL\n");
++ bt_funcs.set_txrx(up, txrx);
++ }
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_set_txrx() done\n");
++}
++
++static int
++h6300_bt_get_txrx(struct uart_omap_port *up)
++{
++ int retVal;
++
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_get_txrx() started\n");
++ if (bt_funcs.get_txrx != NULL)
++ {
++ retVal = bt_funcs.get_txrx(up);
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_get_txrx(), bt_funcs.get_trx != null, done, retVal %d\n", retVal);
++ return retVal;
++ }
++ else
++ {
++ printk(KERN_NOTICE "board-h6300.c, h6300_bt_get_txrx() done, returning 0\n");
++ return 0;
++ }
++}
++
++static struct platform_omap_serial_funcs h6300_omap_bt_funcs = {
++ .configure = h6300_bt_configure,
++ .set_txrx = h6300_bt_set_txrx,
++ .get_txrx = h6300_bt_get_txrx,
++};
++
++struct platform_device btuart_device = {
++ .name = "h6300_bt",
++ .id = 1,
++};
++EXPORT_SYMBOL(btuart_device);
++
++static void
++h6300_gsm_configure(struct uart_omap_port *up, int enable)
++{
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_configure() started\n");
++ if (gsm_funcs.configure != NULL)
++ gsm_funcs.configure(up, enable);
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_configure() done\n");
++}
++
++static void
++h6300_gsm_set_txrx(struct uart_omap_port *up, int txrx)
++{
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_set_txrx() started\n");
++ if (bt_funcs.set_txrx != NULL)
++ {
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_set_txrx(), bt_funcs.set_txrx != NULL\n");
++ gsm_funcs.set_txrx(up, txrx);
++ }
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_set_txrx() done\n");
++}
++
++static int
++h6300_gsm_get_txrx(struct uart_omap_port *up)
++{
++ int retVal;
++
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_get_txrx() started\n");
++ if (bt_funcs.get_txrx != NULL)
++ {
++ retVal = gsm_funcs.get_txrx(up);
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_get_txrx(), bt_funcs.get_trx != null, done, retVal %d\n", retVal);
++ return retVal;
++ }
++ else
++ {
++ printk(KERN_NOTICE "board-h6300.c, h6300_gsm_get_txrx() done, returning 0\n");
++ return 0;
++ }
++}
++
++static struct platform_omap_serial_funcs h6300_omap_gsm_funcs = {
++ .configure = h6300_gsm_configure,
++ .set_txrx = h6300_gsm_set_txrx,
++ .get_txrx = h6300_gsm_get_txrx,
++};
++
++struct platform_device gsmuart_device = {
++ .name = "h6300_gsm",
++ .id = 1,
++};
++EXPORT_SYMBOL(gsmuart_device);
++
++#if 0
++static struct mtd_partition h6300_partitions[] = {
++ /* bootloader (U-Boot, etc) in first sector */
++ {
++ .name = "bootloader",
++ .offset = 0,
++ .size = SZ_128K,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ },
++ /* bootloader params in the next sector */
++ {
++ .name = "params",
++ .offset = MTDPART_OFS_APPEND,
++ .size = SZ_128K,
++ .mask_flags = 0,
++ },
++ /* kernel */
++ {
++ .name = "kernel",
++ .offset = MTDPART_OFS_APPEND,
++ .size = SZ_2M,
++ .mask_flags = 0
++ },
++ /* rest of flash1 is a file system */
++ {
++ .name = "rootfs",
++ .offset = MTDPART_OFS_APPEND,
++ .size = SZ_16M - SZ_2M - 2 * SZ_128K,
++ .mask_flags = 0
++ },
++ /* file system */
++ {
++ .name = "filesystem",
++ .offset = MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL,
++ .mask_flags = 0
++ }
++};
++
++static struct flash_platform_data h6300_flash_data = {
++ .map_name = "cfi_probe",
++ .width = 2,
++ .parts = h6300_partitions,
++ .nr_parts = ARRAY_SIZE(h6300_partitions),
++};
++
++static struct resource h6300_flash_resource = {
++ .start = OMAP_CS0_PHYS,
++ .end = OMAP_CS0_PHYS + SZ_32M - 1,
++ .flags = IORESOURCE_MEM,
++};
++
++static struct platform_device h6300_flash_device = {
++ .name = "omapflash",
++ .id = 0,
++ .dev = {
++ .platform_data = &h6300_flash_data,
++ },
++ .num_resources = 1,
++ .resource = &h6300_flash_resource,
++};
++#endif
++
++static struct resource h6300_wlan_resource[] = {
++ [0] = {
++ .start = OMAP_CS1_PHYS,
++ .end = OMAP_CS1_PHYS + SZ_32M -1,
++ .flags = IORESOURCE_MEM,
++ },
++
++ [1] = {
++ .start = OMAP_GPIO_IRQ(11),
++ .end = OMAP_GPIO_IRQ(11),
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device h6300_wlan_device = {
++ .name = "tnetw1100b",
++ .id = 0,
++ .num_resources = 2,
++ .resource = h6300_wlan_resource,
++};
++
++static struct platform_device *h6300_devices[] __initdata = {
++ &btuart_device,
++ &gsmuart_device,
++ &h6300_wlan_device,
++ //&h6300_flash_device,
++};
++
++static void __init h6300_init_irq(void)
++{
++ omap_init_irq();
++ omap_gpio_init();
++
++ omap_request_gpio (2);
++ omap_set_gpio_direction (2, 0);
++ omap_set_gpio_dataout (2, 1);
++}
++
++/* assume no Mini-AB port */
++
++static struct omap_usb_config h6300_usb_config __initdata = {
++ .hmc_mode = 0,
++ .register_dev = 1,
++ .pins[0] = 0,
++};
++
++static struct omap_lcd_config h6300_lcd_config __initdata = {
++ .panel_name = "h6300",
++ .ctrl_name = "internal",
++};
++
++static struct omap_mmc_config h6300_mmc_config __initdata = {
++ .mmc [0] = {
++ .enabled = 1,
++ .wire4 = 1,
++ .wp_pin = OMAP_GPIO_IRQ(13),
++ .power_pin = -1, /* FPGA F3 UIO42 */
++ .switch_pin = -1, /* FPGA F4 UIO43 */
++ },
++};
++
++static struct omap_uart_config h6300_uart_config __initdata = {
++ .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
++};
++
++static struct omap_board_config_kernel h6300_config[] = {
++ { OMAP_TAG_USB, &h6300_usb_config },
++ { OMAP_TAG_MMC, &h6300_mmc_config },
++ { OMAP_TAG_UART, &h6300_uart_config },
++ { OMAP_TAG_LCD, &h6300_lcd_config },
++};
++
++static void __init h6300_init(void)
++{
++ int ret;
++
++ ret = platform_add_devices(h6300_devices, ARRAY_SIZE(h6300_devices));
++ if (ret)
++ {
++ printk(KERN_WARNING "Unable to add h6300 platform devices like bluetooth");
++ }
++ omap_board_config = h6300_config;
++ omap_board_config_size = ARRAY_SIZE(h6300_config);
++ omap_serial_init();
++}
++
++static void __init h6300_map_io(void)
++{
++ omap_map_common_io();
++
++ btuart_device.dev.platform_data = &h6300_omap_bt_funcs;
++ gsmuart_device.dev.platform_data = &h6300_omap_gsm_funcs;
++}
++
++MACHINE_START(H6300, "HP iPAQ H6300")
++ /* MAINTAINER("Everett Coleman II <gcc80x86@fuzzyneural.net>") */
++ .phys_ram = 0x10000000,
++ .phys_io = 0xfff00000,
++ .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc,
++ .boot_params = 0x10000100,
++ .map_io = h6300_map_io,
++ .init_irq = h6300_init_irq,
++ .init_machine = h6300_init,
++ .timer = &omap_timer,
++MACHINE_END
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap1/Kconfig bt_kernel/arch/arm/mach-omap1/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap1/Kconfig 2005-10-30 17:44:15.310427101 +0200
++++ bt_kernel/arch/arm/mach-omap1/Kconfig 2005-10-22 03:52:45.687256000 +0300
+@@ -26,6 +26,12 @@
+ TI OMAP 1510 or 1610 Innovator board support. Say Y here if you
+ have such a board.
+
++config MACH_OMAP_H6300
++ bool "HP IPaq H6300"
++ depends on ARCH_OMAP1 && ARCH_OMAP15XX
++ help
++ HP IPaq H6300 series.
++
+ config MACH_OMAP_H2
+ bool "TI H2 Support"
+ depends on ARCH_OMAP1 && ARCH_OMAP16XX
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap1/Makefile bt_kernel/arch/arm/mach-omap1/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap1/Makefile 2005-10-30 17:44:15.334423281 +0200
++++ bt_kernel/arch/arm/mach-omap1/Makefile 2005-10-22 03:52:45.687256000 +0300
+@@ -15,7 +15,8 @@
+ obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o
+ obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.o
+ obj-$(CONFIG_MACH_NETSTAR) += board-netstar.o
+-obj-$(CONFIG_MACH_OMAP_PALMTE) += board-palmte.o
++obj-$(CONFIG_MACH_OMAP_PALMTE) += board-palmte.o
++obj-$(CONFIG_MACH_OMAP_H6300) += board-h6300.o
+
+ ifeq ($(CONFIG_ARCH_OMAP15XX),y)
+ # Innovator-1510 FPGA
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap2/clock.h bt_kernel/arch/arm/mach-omap2/clock.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/mach-omap2/clock.h 2005-10-30 17:44:15.870337978 +0200
++++ bt_kernel/arch/arm/mach-omap2/clock.h 2005-10-30 16:32:39.609796000 +0200
+@@ -1469,7 +1469,7 @@
+ .parent = &l4_ck,
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .enable_reg = (void __iomem *)&CM_ICLKEN2_CORE,
+- .enable_bit = 3,
++ .enable_bit = 2,
+ .recalc = &omap2_followparent_recalc,
+ };
+
+@@ -1478,7 +1478,7 @@
+ .parent = &func_48m_ck,
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .enable_reg = (void __iomem *)&CM_FCLKEN2_CORE,
+- .enable_bit = 3,
++ .enable_bit = 2,
+ .recalc = &omap2_followparent_recalc,
+ };
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/plat-omap/devices.c bt_kernel/arch/arm/plat-omap/devices.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/plat-omap/devices.c 2005-10-30 17:44:16.144294371 +0200
++++ bt_kernel/arch/arm/plat-omap/devices.c 2005-10-22 03:52:45.687256000 +0300
+@@ -93,6 +93,9 @@
+ (void) platform_device_register(&omap_i2c_device1);
+ }
+
++#else
++static inline void omap_init_i2c(void) {}
++
+ #endif
+
+ /*-------------------------------------------------------------------------*/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/plat-omap/dma.c bt_kernel/arch/arm/plat-omap/dma.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/plat-omap/dma.c 2005-10-30 17:44:16.167290711 +0200
++++ bt_kernel/arch/arm/plat-omap/dma.c 2005-10-30 16:32:39.609796000 +0200
+@@ -28,6 +28,7 @@
+ #include <asm/hardware.h>
+ #include <asm/dma.h>
+ #include <asm/io.h>
++#include <asm/mach-types.h>
+
+ #include <asm/arch/tc.h>
+
+@@ -121,7 +122,8 @@
+ }
+
+ void omap_set_dma_transfer_params(int lch, int data_type, int elem_count,
+- int frame_count, int sync_mode)
++ int frame_count, int sync_mode,
++ int dma_trigger, int src_or_dst_synch)
+ {
+ u16 w;
+
+@@ -179,7 +181,8 @@
+
+
+ void omap_set_dma_src_params(int lch, int src_port, int src_amode,
+- unsigned long src_start)
++ unsigned long src_start,
++ int src_ei, int src_fi)
+ {
+ u16 w;
+
+@@ -221,7 +224,14 @@
+ case OMAP_DMA_DATA_BURST_DIS:
+ break;
+ case OMAP_DMA_DATA_BURST_4:
+- w |= (0x01 << 7);
++ if (machine_is_h6300())
++ {
++ w |= (0x01 << 7);
++ }
++ else
++ {
++ w |= (0x02 << 7);
++ }
+ break;
+ case OMAP_DMA_DATA_BURST_8:
+ /* not supported by current hardware
+@@ -235,7 +245,8 @@
+ }
+
+ void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
+- unsigned long dest_start)
++ unsigned long dest_start,
++ int dst_ei, int dst_fi)
+ {
+ u16 w;
+
+@@ -277,7 +288,14 @@
+ case OMAP_DMA_DATA_BURST_DIS:
+ break;
+ case OMAP_DMA_DATA_BURST_4:
+- w |= (0x01 << 14);
++ if (machine_is_h6300())
++ {
++ w |= (0x01 << 14);
++ }
++ else
++ {
++ w |= (0x02 << 14);
++ }
+ break;
+ case OMAP_DMA_DATA_BURST_8:
+ w |= (0x03 << 14);
+@@ -769,6 +787,10 @@
+ }
+
+ if (omap_dma_in_1510_mode()) {
++ u16 l = omap_readw(OMAP1510_DMA_LCD_CTRL);
++ l &= ~(1 << 6);
++ omap_writew (l, OMAP1510_DMA_LCD_CTRL);
++
+ omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U);
+ omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L);
+ omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U);
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/plat-omap/mcbsp.c bt_kernel/arch/arm/plat-omap/mcbsp.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/plat-omap/mcbsp.c 2005-10-30 17:44:16.477241375 +0200
++++ bt_kernel/arch/arm/plat-omap/mcbsp.c 2005-10-30 16:32:39.609796000 +0200
+@@ -493,17 +493,20 @@
+ omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch,
+ OMAP_DMA_DATA_TYPE_S16,
+ length >> 1, 1,
+- OMAP_DMA_SYNC_ELEMENT);
++ OMAP_DMA_SYNC_ELEMENT,
++ 0, 0);
+
+ omap_set_dma_dest_params(mcbsp[id].dma_tx_lch,
+ OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1);
++ mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1,
++ 0, 0);
+
+ omap_set_dma_src_params(mcbsp[id].dma_tx_lch,
+ OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+- buffer);
++ buffer,
++ 0, 0);
+
+ omap_start_dma(mcbsp[id].dma_tx_lch);
+ wait_for_completion(&(mcbsp[id].tx_dma_completion));
+@@ -533,17 +536,20 @@
+ omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch,
+ OMAP_DMA_DATA_TYPE_S16,
+ length >> 1, 1,
+- OMAP_DMA_SYNC_ELEMENT);
++ OMAP_DMA_SYNC_ELEMENT,
++ 0, 0);
+
+ omap_set_dma_src_params(mcbsp[id].dma_rx_lch,
+ OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1);
++ mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1,
++ 0, 0);
+
+ omap_set_dma_dest_params(mcbsp[id].dma_rx_lch,
+ OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+- buffer);
++ buffer,
++ 0, 0);
+
+ omap_start_dma(mcbsp[id].dma_rx_lch);
+ wait_for_completion(&(mcbsp[id].rx_dma_completion));
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/tools/mach-types bt_kernel/arch/arm/tools/mach-types
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/arch/arm/tools/mach-types 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/arch/arm/tools/mach-types 2005-10-22 03:52:45.687256000 +0300
+@@ -576,7 +576,7 @@
+ s3c2460 MACH_S3C2460 S3C2460 560
+ pdm MACH_PDM PDM 561
+ h4700 MACH_H4700 H4700 562
+-h6300 MACH_H6300 H6300 563
++h6300 MACH_OMAP_H6300 H6300 563
+ rz1700 MACH_RZ1700 RZ1700 564
+ a716 MACH_A716 A716 565
+ estk2440a MACH_ESTK2440A ESTK2440A 566
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/Kconfig bt_kernel/drivers/bluetooth/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/Kconfig 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/bluetooth/Kconfig 2005-10-04 00:58:34.589442000 +0300
+@@ -163,6 +163,16 @@
+
+ Say Y here to compile support for virtual HCI devices into the
+ kernel or say M to compile it as module (hci_vhci).
++
++config BT_H6300
++ tristate "H6300 BRF6100 BT DRIVER"
++ help
++ Bluetooth H6300 BRF6100 driver.
++ This driver provides the firmware loading mechanism for the BRF6100
++ bt hardware in iPAQ h6300.
++
++ Say Y here to compile support for BRF6100 BT devices into the
++ kernel or say M to compile it as module (h6300_BT).
+
+ endmenu
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/Makefile bt_kernel/drivers/bluetooth/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/Makefile 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/bluetooth/Makefile 2005-09-28 01:04:13.052737000 +0300
+@@ -10,10 +10,11 @@
+ obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
+ obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
+ obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
+-obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
++obj-$(CONFIG_BT_HCIBLUECARD)+= bluecard_cs.o
+ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
++obj-$(CONFIG_BT_H6300) += omap/
+
+-hci_uart-y := hci_ldisc.o
++hci_uart-y := hci_ldisc.o
+ hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
+ hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
+-hci_uart-objs := $(hci_uart-y)
++hci_uart-objs := $(hci_uart-y)
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/h6300_bt_brf6100.c bt_kernel/drivers/bluetooth/omap/h6300_bt_brf6100.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/h6300_bt_brf6100.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/bluetooth/omap/h6300_bt_brf6100.c 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,153 @@
++/*
++ * Bluetooth interface driver for TI BRF6100 on h6300
++ *
++ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi>
++ * Ideas taken from the brf6150 bt driver made by Todd Blumer for the pxa hx4700.
++ *
++ * 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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gpio.h>
++
++#include <asm/arch/h6300_uart_info.h>
++#include "h6300_bt_led.h"
++
++static void
++h6300_bt_configure(struct uart_omap_port *up, int enable)
++{
++ printk(KERN_NOTICE "h6300_bt_brf6100.c h6300_bt_configure() started, enable = %d\n", enable);
++
++ // printk( KERN_NOTICE "h6300 configure bluetooth: %d\n", enable );
++ if (enable == 0) {
++ omap_set_gpio_dataout(GPIO_N_BT_RST, 0); // turn off gpio
++ mdelay(5);
++ h6300_clear_led(INDEX_BT_LED);
++ }
++ else if (enable == 1) {
++ omap_set_gpio_dataout(GPIO_N_BT_RST, 1); // turn on gpio
++ mdelay(5);
++ }
++ else if (enable == 2) {
++ /*
++ * BRF6150's RTS goes low when firmware is ready
++ * so check for CTS=1 (nCTS=0 -> CTS=1). Typical 150ms
++ */
++/*
++ int tries = 0;
++ do
++ {
++ mdelay(10);
++ }
++ while ((BTMSR & MSR_CTS) == 0 && tries++ < 50);
++*/
++ h6300_set_led(INDEX_BT_LED, 16, 16);
++ }
++ printk(KERN_NOTICE "h6300_bt_brf6100.c h6300_bt_configure() done\n");
++}
++
++static void
++h6300_bt_set_txrx(struct uart_omap_port *up, int txrx)
++{
++ printk(KERN_NOTICE "h6300_bt_brf6100.c h6300_bt_set_txrx(), txrx = %d done\n", txrx);
++ /* do nothing */
++}
++
++static int
++h6300_bt_get_txrx(struct uart_omap_port *up)
++{
++ printk(KERN_NOTICE "h6300_bt_brf6100.c h6300_bt_get_txrx() done\n");
++ /* do nothing */
++ return 0;
++}
++
++static int
++h6300_bt_probe(struct device *dev)
++{
++ struct h6300_uart_funcs *funcs = (struct h6300_uart_funcs *)dev->platform_data;
++
++ omap_request_gpio(GPIO_BT_PWR_EN); // ask bt_power_en gpio, remember to release in remove_function
++ omap_set_gpio_direction(GPIO_BT_PWR_EN, 1); // set gpio direction to be output
++ omap_set_gpio_dataout(GPIO_BT_PWR_EN, 1); // turn on gpio
++
++ mdelay(200);
++
++ omap_request_gpio(GPIO_N_BT_RST); // ask bt_reset gpio, remember to release in remove_function
++ omap_set_gpio_direction(GPIO_N_BT_RST, 1); // set gpio direction to be output
++ omap_set_gpio_dataout(GPIO_N_BT_RST, 1); // turn on gpio
++
++ /* configure bluetooth UART */
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_RXD_MD);
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_TXD_MD);
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_UART_CTS_MD);
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_UART_RTS_MD);
++
++ funcs->configure = h6300_bt_configure;
++ funcs->set_txrx = h6300_bt_set_txrx;
++ funcs->get_txrx = h6300_bt_get_txrx;
++
++ /* Make sure the LED is off */
++ h6300_clear_led(INDEX_BT_LED);
++
++ printk(KERN_NOTICE "h6300_bt_brf6100.c h6300_bt_probe() done\n");
++
++ return 0;
++}
++
++static int
++h6300_bt_remove(struct device *dev)
++{
++ struct h6300_uart_funcs *funcs = (struct h6300_uart_funcs *)dev->platform_data;
++
++ printk(KERN_NOTICE "h6300_bt_brf6100.c h6300_bt_remove() started\n");
++
++ omap_free_gpio(GPIO_BT_PWR_EN);
++ omap_free_gpio(GPIO_N_BT_RST);
++
++ funcs->configure = NULL;
++ funcs->set_txrx = NULL;
++ funcs->get_txrx = NULL;
++
++ /* Make sure the LED is off */
++ h6300_clear_led(INDEX_BT_LED);
++
++ printk(KERN_NOTICE "h6300_bt_brf6100.c, h6300_bt_remove() done\n");
++
++ return 0;
++}
++
++static struct device_driver bt_driver = {
++ .name = "h6300_bt",
++ .bus = &platform_bus_type,
++ .probe = h6300_bt_probe,
++ .remove = h6300_bt_remove,
++};
++
++static int __init
++h6300_bt_init(void)
++{
++ printk(KERN_NOTICE "h6300 Bluetooth Driver init()\n");
++ return driver_register(&bt_driver);
++}
++
++static void __exit
++h6300_bt_exit(void)
++{
++ printk(KERN_NOTICE "h6300 Bluetooth Driver exit()\n");
++ driver_unregister(&bt_driver);
++}
++
++module_init(h6300_bt_init);
++module_exit(h6300_bt_exit);
++
++MODULE_AUTHOR("Mika Laitio, <lamikr@cc.jyu.fi>");
++MODULE_DESCRIPTION("iPAQ h6300 BRF6100 Bluetooth driver.");
++MODULE_LICENSE("GPL");
++
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/h6300_bt_led.c bt_kernel/drivers/bluetooth/omap/h6300_bt_led.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/h6300_bt_led.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/bluetooth/omap/h6300_bt_led.c 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,41 @@
++/*
++ * Bluetooth interface driver helper for controlling bluetooth leds available in iPAQ h6300.
++ *
++ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi>
++ * Ideas from the brf6150 bt driver made by Todd Blumer for the pxa hx4700.
++ *
++ * 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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gpio.h>
++
++/*
++ * Low level access for disabling h6300 bt led.
++ *
++ * TODO: implement for h6300
++ */
++void h6300_clear_led(int led_num)
++{
++ printk(KERN_NOTICE "h6300_bt_led.c h6300_clear_led() done\n");
++ //hx4700_set_led(led_num, 0, 16);
++}
++EXPORT_SYMBOL(h6300_clear_led);
++
++/*
++ * Low level access for setting up the bt led.
++ *
++ * TODO: implement for h6300
++ */
++void h6300_set_led(int led_num, int duty_time, int cycle_time)
++{
++ printk(KERN_NOTICE "h6300_bt_led.c h6300_set_led() done\n");
++}
++EXPORT_SYMBOL(h6300_set_led);
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/h6300_bt_led.h bt_kernel/drivers/bluetooth/omap/h6300_bt_led.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/h6300_bt_led.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/bluetooth/omap/h6300_bt_led.h 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,9 @@
++#ifndef H6300_BT_LED_H_
++#define H6300_BT_LED_H_
++
++#define INDEX_BT_LED 2
++
++void h6300_clear_led(int led_num);
++void h6300_set_led(int led_num, int duty_time, int cycle_time);
++
++#endif /*H6300_BT_LED_H_*/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/Makefile bt_kernel/drivers/bluetooth/omap/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/bluetooth/omap/Makefile 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/bluetooth/omap/Makefile 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,6 @@
++#
++# Makefile for the Linux iPAQ H6300 BRF6100 Bluetooth device drivers.
++#
++
++h6300_bt-objs := h6300_bt_led.o h6300_bt_brf6100.o
++obj-$(CONFIG_BT_H6300) += h6300_bt.o
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/char/.gitignore bt_kernel/drivers/char/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/char/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/char/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,3 +0,0 @@
+-consolemap_deftbl.c
+-defkeymap.c
+-
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/keyboard/omap-keypad.c bt_kernel/drivers/input/keyboard/omap-keypad.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/keyboard/omap-keypad.c 2005-10-30 17:44:17.444087478 +0200
++++ bt_kernel/drivers/input/keyboard/omap-keypad.c 2005-10-30 16:34:01.215220000 +0200
+@@ -5,10 +5,11 @@
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
++ * iPAQ h6300 key and joypad support added by Mika Laitio. (2005)
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+- *
++ *
+ * 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
+@@ -40,6 +41,7 @@
+ #include <asm/arch/mux.h>
+
+ #undef NEW_BOARD_LEARNING_MODE
++//#define NEW_BOARD_LEARNING_MODE 1
+
+ static void omap_kp_tasklet(unsigned long);
+ static void omap_kp_timer(unsigned long);
+@@ -48,6 +50,8 @@
+ static unsigned char keypad_state[8];
+ static unsigned int keypad_irq = INT_KEYBOARD;
+
++static int prevJoypadKeycodePressEmulated;
++
+ static struct timer_list kp_timer;
+ DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+@@ -165,6 +169,47 @@
+ 0
+ };
+
++#define _h6300_KEY_CALENDAR 67 // xmodmap 75 aka F9
++#define _H6300_KEY_TELEPHONE 68 // xmodmap 76 aka F10
++#define _H6300_KEY_HOMEPAGE 87 // xmodmap 87 aka Num_Lock
++#define _H6300_KEY_MAIL 88 // xmodmap 88 aka Scroll_Lock
++
++/*
++ * Following 5 keypad events are not really sent to userspace.
++ * Instead if the good combination of them is sent, then that is send.
++ * (up, right, down, left, enter)
++ */
++#define _H6300_JOYPAD_UP_RIGHT 1 // 00001
++#define _H6300_JOYPAD_DOWN_RIGHT 2 // 00010
++#define _h6300_JOYPAD_DOWN_LEFT 4 // 00100
++#define _h6300_JOYPAD_UP_LEFT 8 // 01000
++#define _H6300_JOYPAD_KEY_OK 16 // 10000
++
++static int h6300_keymap[] = {
++ KEY(2, 0, _h6300_KEY_CALENDAR), // address button in the bottom left
++ KEY(2, 3, _H6300_KEY_TELEPHONE), // start call button in the bottom
++ KEY(3, 1, _H6300_KEY_HOMEPAGE), // stop call button in the bottom
++ KEY(3, 4, _H6300_KEY_MAIL), // messaging button in the bottom right
++
++ KEY(0, 0, KEY_VOLUMEUP), // volume up button in the right side
++ KEY(0, 1, KEY_VOLUMEDOWN), // volume down button in the right side
++ KEY(3, 2, KEY_RECORD), // record button in the left side
++
++ KEY(1, 0, _h6300_JOYPAD_UP_LEFT),
++ KEY(1, 1, _h6300_JOYPAD_DOWN_LEFT),
++ KEY(1, 2, _H6300_JOYPAD_KEY_OK),
++ KEY(1, 3, _H6300_JOYPAD_DOWN_RIGHT),
++ KEY(1, 4, _H6300_JOYPAD_UP_RIGHT),
++
++ KEY(5, 0, KEY_RIGHT),
++ KEY(5, 1, KEY_DOWN),
++ KEY(5, 2, KEY_LEFT),
++ KEY(5, 3, KEY_UP),
++ KEY(5, 4, KEY_ENTER),
++
++ 0
++};
++
+ static int *keymap;
+
+ static irqreturn_t omap_kp_interrupt(int irq, void *dev_id,
+@@ -191,7 +236,8 @@
+ for (col = 0; col < 8; col++) {
+ omap_writew(~(1 << col) & 0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+- if (machine_is_omap_osk() || machine_is_omap_h2() || machine_is_omap_h3()) {
++ if (machine_is_omap_osk() || machine_is_omap_h2() || machine_is_omap_h3() || machine_is_h6300()) {
++ // makes keyboard act a little bit slower
+ udelay(9);
+ } else {
+ udelay(4);
+@@ -214,16 +260,24 @@
+ return -1;
+ }
+
++int is_key_down(unsigned char new_state[],
++ int col,
++ int row)
++{
++ return (new_state[col] & (1 << row)) ? 1 : 0;
++}
++
+ static void omap_kp_tasklet(unsigned long data)
+ {
+ unsigned char new_state[8], changed, key_down = 0;
+ int col, row;
+ int spurious = 0;
++ int report_key, report_col, report_row, joypad_checked; // joypad specific variables
+
+ /* check for any changes */
+ omap_kp_scan_keypad(new_state);
+-
+ /* check for changes and print those */
++ joypad_checked = 0;
+ for (col = 0; col < 8; col++) {
+ changed = new_state[col] ^ keypad_state[col];
+ key_down |= new_state[col];
+@@ -245,9 +299,173 @@
+ spurious = 1;
+ continue;
+ }
+-
+- input_report_key(&omap_kp_dev, key,
+- new_state[col] & (1 << row));
++ if (machine_is_h6300() &&
++ ((col == 1) || (col == 5)))
++ {
++ if (col == 5)
++ {
++ continue;
++ }
++ if ((joypad_checked == 0) &&
++ ((key == _H6300_JOYPAD_KEY_OK) ||
++ (key == _h6300_JOYPAD_UP_LEFT) ||
++ (key == _H6300_JOYPAD_UP_RIGHT) ||
++ (key == _H6300_JOYPAD_DOWN_RIGHT) ||
++ (key == _h6300_JOYPAD_DOWN_LEFT)))
++ {
++ if (is_key_down(new_state, col, row))
++ {
++ /*
++ * only enter pressed
++ * 1 0 0 _H6300_JOYPAD_KEY_OK 0 0
++ * --> 100100 == 36
++ */
++ if (new_state[1] == 36)
++ {
++ joypad_checked = 1;
++ prevJoypadKeycodePressEmulated = KEY_ENTER;
++ new_state[5] = 48; //110000
++ report_key = prevJoypadKeycodePressEmulated;
++ report_col = 5;
++ report_row = 4;
++ input_report_key(&omap_kp_dev,
++ report_key,
++ new_state[report_col] & (1 << report_row));
++ }
++ /*
++ * enter, up_left and up_right sensors pressed.
++ * 1 _H6300_JOYPAD_UP_RIGHT 0 _H6300_JOYPAD_KEY_OK 0 _h6300_JOYPAD_UP_LEFT
++ * --> 110101 == 53
++ * OR
++ * 1 KEY_UP_RIGHT 0 0 0 _h6300_JOYPAD_UP_LEFT
++ * --> 110001 == 42
++ * --> move to up
++ */
++ else if ((new_state[1] == 53) ||
++ (new_state[1] == 49))
++ {
++ joypad_checked = 1;
++ prevJoypadKeycodePressEmulated = KEY_UP;
++ new_state[5] = 40; //101000
++ report_key = prevJoypadKeycodePressEmulated;
++ report_col = 5;
++ report_row = 3;
++ input_report_key(&omap_kp_dev,
++ report_key,
++ new_state[report_col] & (1 << report_row));
++ }
++ /*
++ * enter, down_left and down_right sensors pressed
++ * --> 101110 == 46
++ * OR
++ * down_left and down_right
++ * -->101010 == 42
++ * --> move to down
++ */
++ else if ((new_state[1] == 46) ||
++ (new_state[1] == 42))
++ {
++ joypad_checked = 1;
++ prevJoypadKeycodePressEmulated = KEY_DOWN;
++ new_state[5] = 34; //100010
++ report_key = prevJoypadKeycodePressEmulated;
++ report_col = 5;
++ report_row = 1;
++ input_report_key(&omap_kp_dev,
++ report_key,
++ new_state[report_col] & (1 << report_row));
++ }
++ /*
++ * enter, up_right and down_right sensors pressed
++ * --> 111100 == 60
++ * or
++ * down_right and up_right
++ * --> 111000 == 56
++ * --> move to right
++ */
++ else if ((new_state[1] == 60) ||
++ (new_state[1] == 56))
++ {
++ joypad_checked = 1;
++ prevJoypadKeycodePressEmulated = KEY_RIGHT;
++ new_state[5] = 33; //100001
++ report_key = prevJoypadKeycodePressEmulated;
++ report_col = 5;
++ report_row = 0;
++ input_report_key(&omap_kp_dev,
++ report_key,
++ new_state[report_col] & (1 << report_row));
++ }
++ /*
++ * enter, up_left and down_left sensors pressed
++ * --> 100111 == 39
++ * or up_left and down_left
++ * --> 100011 == 35
++ * --> move to left
++ */
++ else if ((new_state[1] == 39) ||
++ (new_state[1] == 35))
++ {
++ joypad_checked = 1;
++ prevJoypadKeycodePressEmulated = KEY_LEFT;
++ new_state[5] = 36; //100100
++ report_key = prevJoypadKeycodePressEmulated;
++ report_col = 5;
++ report_row = 2;
++ input_report_key(&omap_kp_dev,
++ report_key,
++ new_state[report_col] & (1 << report_row));
++ }
++ else
++ {
++ //printk("missed new_state = %d\n", new_state[1]);
++ }
++ }
++ else
++ {
++ if (prevJoypadKeycodePressEmulated != 0)
++ {
++ // report key up event
++ joypad_checked = 1;
++ new_state[5] = 32; //100000
++ report_key = prevJoypadKeycodePressEmulated;
++ report_col = 5;
++ switch(prevJoypadKeycodePressEmulated)
++ {
++ case KEY_RIGHT:
++ report_row = 0;
++ break;
++ case KEY_DOWN:
++ report_row = 1;
++ break;
++ case KEY_LEFT:
++ report_row = 2;
++ break;
++ case KEY_UP:
++ report_row = 3;
++ break;
++ case KEY_ENTER:
++ report_row = 4;
++ break;
++ default:
++ printk(KERN_WARNING "Unknown iPAQ h6300 column 1 key = %d released. This should newer happen!\n",
++ key);
++ report_row = 0;
++ }
++ input_report_key(&omap_kp_dev,
++ report_key,
++ new_state[report_col] & (1 << report_row));
++ prevJoypadKeycodePressEmulated = 0;
++ }
++ }
++ }
++ }
++ else
++ {
++ input_report_key(&omap_kp_dev,
++ key,
++ new_state[col] & (1 << row));
++ }
+ #endif
+ }
+ }
+@@ -285,7 +503,12 @@
+ } else if (machine_is_omap_perseus2()) {
+ keymap = p2_keymap;
+ keypad_irq = INT_730_MPUIO_KEYPAD;
++ } else if (machine_is_h6300()) {
++ keymap = h6300_keymap;
++ // set keyboard to send repeated key events if key is hold down
++ set_bit(EV_REP, omap_kp_dev.evbit);
+ } else {
++ printk("omap_keypad.c, keyMap = test_keymap\n");
+ keymap = test_keymap;
+ }
+
+@@ -335,6 +558,7 @@
+
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+ }
++ prevJoypadKeycodePressEmulated = 0;
+
+ /* scan current status and enable interrupt */
+ omap_kp_scan_keypad(keypad_state);
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/touchscreen/omap/Makefile bt_kernel/drivers/input/touchscreen/omap/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/touchscreen/omap/Makefile 2005-10-30 17:44:17.519075542 +0200
++++ bt_kernel/drivers/input/touchscreen/omap/Makefile 2005-10-22 03:52:45.687256000 +0300
+@@ -8,5 +8,6 @@
+ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H3) += ts_hx.o
+ objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += ts_inn1510.o
+ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_OSK) += ts_osk.o
++objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_H6300) += ts_hx.o
+
+ omapts-objs := omap_ts.o $(objs-yy)
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/touchscreen/omap/omap_ts.c bt_kernel/drivers/input/touchscreen/omap/omap_ts.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/touchscreen/omap/omap_ts.c 2005-10-30 17:44:17.542071882 +0200
++++ bt_kernel/drivers/input/touchscreen/omap/omap_ts.c 2005-10-22 03:52:45.687256000 +0300
+@@ -46,7 +46,7 @@
+ #define OMAP_TS_NAME "omap_ts"
+
+ static struct ts_device *__initdata ts_devs[] = {
+-#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
++#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) || defined(CONFIG_MACH_OMAP_H6300)
+ &hx_ts,
+ #endif
+ #ifdef CONFIG_MACH_OMAP_OSK
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/touchscreen/omap/ts_hx.c bt_kernel/drivers/input/touchscreen/omap/ts_hx.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/input/touchscreen/omap/ts_hx.c 2005-10-30 17:44:17.566068062 +0200
++++ bt_kernel/drivers/input/touchscreen/omap/ts_hx.c 2005-09-28 02:45:59.570809000 +0300
+@@ -33,6 +33,7 @@
+ #include <asm/arch/mux.h>
+ #include <asm/arch/hardware.h>
+ #include <asm/hardware/tsc2101.h>
++#include <linux/delay.h>
+
+ #include "../drivers/ssi/omap-tsc2101.h"
+ #include "omap_ts.h"
+@@ -88,14 +89,19 @@
+ } else if (machine_is_omap_h3()) {
+ gpio = H3_GPIO_NUM;
+ omap_cfg_reg(W19_1610_GPIO48);
++ } else if (machine_is_h6300 ()) {
++ gpio = 2;
++ omap_cfg_reg(M14_1510_GPIO2);
+ } else
+ return -ENODEV;
+
+ ts->irq = OMAP_GPIO_IRQ(gpio);
+- if (omap_request_gpio(gpio) != 0) {
+- printk(KERN_ERR "hX_ts_init.c: Could not reserve GPIO!\n");
+- return -EINVAL;
+- };
++ if (!machine_is_h6300 ()){
++ if (omap_request_gpio(gpio) != 0) {
++ printk(KERN_ERR "hX_ts_init.c: Could not reserve GPIO!\n");
++ return -EINVAL;
++ };
++ }
+
+ omap_set_gpio_direction(gpio, 1);
+ set_irq_type(ts->irq, IRQT_FALLING);
+@@ -180,5 +186,7 @@
+ omap_free_gpio(H2_GPIO_NUM);
+ else if (machine_is_omap_h3())
+ omap_free_gpio(H3_GPIO_NUM);
++ else if (machine_is_h6300())
++ omap_free_gpio(2);
+ }
+ #endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/busses/i2c-omap.c bt_kernel/drivers/i2c/busses/i2c-omap.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/busses/i2c-omap.c 2005-10-30 17:44:17.091143658 +0200
++++ bt_kernel/drivers/i2c/busses/i2c-omap.c 2005-10-20 20:53:33.518825000 +0300
+@@ -124,10 +124,10 @@
+ /* I2C System Configuration Register (OMAP_I2C_SYSC): */
+ #define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */
+
++#undef I2C_OMAP_DEBUG
+ /* ------- debugging ---------------------------------------------------*/
+
+-#define I2C_OMAP_DEBUG
+-#ifdef I2c_OMAP_DEBUG
++#ifdef I2C_OMAP_DEBUG
+ static int i2c_debug;
+
+ module_param(i2c_debug, int, 0);
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/chips/Kconfig bt_kernel/drivers/i2c/chips/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/chips/Kconfig 2005-10-30 17:44:17.176130130 +0200
++++ bt_kernel/drivers/i2c/chips/Kconfig 2005-10-14 18:55:31.156317000 +0300
+@@ -56,6 +56,16 @@
+ This driver can also be built as a module. If so, the module
+ will be called pca9539.
+
++config PCA9535
++ tristate "Philips PCA9535 16-bit I/O port"
++ depends on I2C
++ help
++ If you say yes here you get support for the Philips PCA9535
++ 16-bit I/O port.
++
++ This driver can also be built as a module. If so, the module
++ will be called pca9539.
++
+ config SENSORS_PCF8591
+ tristate "Philips PCF8591"
+ depends on I2C && EXPERIMENTAL
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/chips/Makefile bt_kernel/drivers/i2c/chips/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/chips/Makefile 2005-10-30 17:44:17.176130130 +0200
++++ bt_kernel/drivers/i2c/chips/Makefile 2005-10-14 18:55:31.156317000 +0300
+@@ -16,6 +16,7 @@
+ obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
+ obj-$(CONFIG_GPIOEXPANDER_OMAP) += gpio_expander_omap.o
+ obj-$(CONFIG_MENELAUS) += menelaus.o
++obj-$(CONFIG_PCA9535) += pca9535.o
+
+ ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
+ EXTRA_CFLAGS += -DDEBUG
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/chips/pca9535.c bt_kernel/drivers/i2c/chips/pca9535.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/chips/pca9535.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/i2c/chips/pca9535.c 2005-10-25 22:23:21.875634000 +0300
+@@ -0,0 +1,414 @@
++/*
++ Driver for Philips PCA9535 16-bit low power I/O port with interrupt
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++
++ Copyright (C) 2005 Husam Senussi
++ Framework based on Pawel Kolodziejski's pca9535 driver in
++ handheld.org's 2.6.13 kernel. Driver updated by Mika Laitio.
++*/
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/hwmon.h>
++#include <linux/err.h>
++
++#include <asm/arch/pca9535.h>
++#include <linux/delay.h>
++
++#include <linux/interrupt.h>
++#include <asm/mach-types.h>
++#include <asm/irq.h>
++#include <asm/mach/arch.h>
++#include <asm/hardware.h>
++
++EXPORT_SYMBOL(pca9535_gpio_read);
++EXPORT_SYMBOL(pca9535_gpio_write);
++EXPORT_SYMBOL(pca9535_gpio_direction);
++
++static int pca9535_attach_adapter(struct i2c_adapter *adapter);
++static int pca9535_detach_client(struct i2c_client *client);
++static int pca9535_attach(struct i2c_adapter *adapter, int address, int zero_or_minus_one);
++static u32 pca9535_read_reg(struct i2c_client *client, u8 regaddr);
++static void pca9535_write_reg(struct i2c_client *client, u8 regaddr, u16 param);
++
++enum pca9535_cmd
++{
++ PCA9535_INPUT_0 = 0,
++ PCA9535_INPUT_1 = 1,
++ PCA9535_OUTPUT_0 = 2,
++ PCA9535_OUTPUT_1 = 3,
++ PCA9535_INVERT_0 = 4,
++ PCA9535_INVERT_1 = 5,
++ PCA9535_DIRECTION_0 = 6,
++ PCA9535_DIRECTION_1 = 7,
++};
++
++struct pca9535_data {
++ struct semaphore lock;
++ struct i2c_client client;
++};
++
++static struct i2c_driver pca9535_driver = {
++ .owner = THIS_MODULE,
++ .name = "pca9535",
++ .flags = I2C_DF_NOTIFY,
++ .attach_adapter = pca9535_attach_adapter,
++ .detach_client = pca9535_detach_client,
++};
++
++static struct i2c_client *pca9535_i2c_client = NULL;
++static struct pca9535_data pca9535_inited;
++
++static unsigned short normal_i2c[] = { 0x20, I2C_CLIENT_END };
++
++#define DRIVER_VERSION "20 OCT 2005"
++#define DRIVER_NAME "PCA9535"
++
++/*
++ * sysfs callback function.
++ */
++static ssize_t pca9535_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct sensor_device_attribute *psa = to_sensor_dev_attr(attr);
++ struct i2c_client *client = to_i2c_client(dev);
++ return sprintf(buf, "%02X\n", (pca9535_read_reg(client, psa->index) >> 8));
++}
++
++/*
++ * sysfs callback function.
++ */
++static ssize_t pca9535_store(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct sensor_device_attribute *psa = to_sensor_dev_attr(attr);
++ struct i2c_client *client = to_i2c_client(dev);
++ unsigned long val = simple_strtoul(buf, NULL, 0);
++ unsigned long old = pca9535_read_reg(client, psa->index);
++
++ if (val > 0xff)
++ return -EINVAL;
++
++ val = (old & 0xff) | (val << 8);
++ pca9535_write_reg(client, psa->index, val);
++ return count;
++}
++
++#define PCA9535_ENTRY_RO(name, cmd_idx) \
++ static SENSOR_DEVICE_ATTR(name, S_IRUGO, pca9535_show, NULL, cmd_idx)
++
++#define PCA9535_ENTRY_RW(name, cmd_idx) \
++ static SENSOR_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, pca9535_show, \
++ pca9535_store, cmd_idx)
++
++PCA9535_ENTRY_RO(input0, PCA9535_INPUT_0);
++PCA9535_ENTRY_RO(input1, PCA9535_INPUT_1);
++PCA9535_ENTRY_RW(output0, PCA9535_OUTPUT_0);
++PCA9535_ENTRY_RW(output1, PCA9535_OUTPUT_1);
++PCA9535_ENTRY_RW(invert0, PCA9535_INVERT_0);
++PCA9535_ENTRY_RW(invert1, PCA9535_INVERT_1);
++PCA9535_ENTRY_RW(direction0, PCA9535_DIRECTION_0);
++PCA9535_ENTRY_RW(direction1, PCA9535_DIRECTION_1);
++
++static struct attribute *pca9535_attributes[] = {
++ &sensor_dev_attr_input0.dev_attr.attr,
++ &sensor_dev_attr_input1.dev_attr.attr,
++ &sensor_dev_attr_output0.dev_attr.attr,
++ &sensor_dev_attr_output1.dev_attr.attr,
++ &sensor_dev_attr_invert0.dev_attr.attr,
++ &sensor_dev_attr_invert1.dev_attr.attr,
++ &sensor_dev_attr_direction0.dev_attr.attr,
++ &sensor_dev_attr_direction1.dev_attr.attr,
++ NULL
++};
++
++static struct attribute_group pca9535_defattr_group = {
++ .attrs = pca9535_attributes,
++};
++//End of sysfs management code.
++
++I2C_CLIENT_INSMOD;
++
++u32 pca9535_read_input(void)
++{
++ return pca9535_read_reg(pca9535_i2c_client, 0);
++}
++EXPORT_SYMBOL(pca9535_read_input);
++
++void pca9535_write_output(u16 param)
++{
++ pca9535_write_reg(pca9535_i2c_client, 2, param);
++}
++EXPORT_SYMBOL(pca9535_write_output);
++
++void pca9535_set_dir(u16 param)
++{
++ pca9535_write_reg(pca9535_i2c_client, 6, param);
++}
++EXPORT_SYMBOL(pca9535_set_dir);
++
++static int pca9535_attach_adapter(struct i2c_adapter *adapter)
++{
++ return i2c_probe(adapter, &addr_data, pca9535_attach);
++}
++
++static int pca9535_attach(struct i2c_adapter *adapter, int address, int zero_or_minus_one)
++{
++ struct i2c_client *new_client;
++ int err = 0;
++
++ printk("pca9535_attach() started\n");
++ new_client = &(pca9535_inited.client);
++ i2c_set_clientdata(new_client, 0);
++ new_client->addr = address;
++ new_client->adapter = adapter;
++ new_client->driver = &pca9535_driver;
++ new_client->flags = I2C_CLIENT_ALLOW_USE;
++ strcpy(new_client->name, DRIVER_NAME);
++
++ if ((err = i2c_attach_client(new_client)))
++ goto exit_free;
++
++ pca9535_i2c_client = new_client;
++
++ init_MUTEX(&pca9535_inited.lock);
++ i2c_set_clientdata(pca9535_i2c_client, &pca9535_inited);
++
++ sysfs_create_group(&pca9535_i2c_client->dev.kobj, &pca9535_defattr_group);
++
++ printk("pca9535_attach() ok\n");
++ return 0;
++
++exit_free:
++ printk("pca9535_attach() failed, error code = %d\n", err);
++ return err;
++}
++
++static int pca9535_detach_client(struct i2c_client *client)
++{
++ int err;
++
++ if ((err = i2c_detach_client(client))) {
++ dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
++ return err;
++ }
++ pca9535_i2c_client = NULL;
++
++ return 0;
++}
++
++static int __init pca9535_init(void)
++{
++ return i2c_add_driver(&pca9535_driver);
++}
++
++static void __exit pca9535_exit(void)
++{
++ i2c_del_driver(&pca9535_driver);
++}
++
++/*
++ * Reads the value of GPIO available via I2C.
++ */
++int pca9535_gpio_read(int gpio){
++ unsigned char reg = 0;
++ unsigned long val = 0;
++
++ printk("9535_gpio_read() called\n");
++ if(!pca9535_i2c_client)
++ return -ENODEV;
++
++ if(gpio < GPIO0 || gpio > GPIO17)
++ return -EINVAL;
++
++ if(gpio >= GPIO0 && gpio <= GPIO7){
++ reg = PCA9535_INPUT_0;
++ gpio -= GPIO0;
++ }else if(gpio >= GPIO8 && gpio <= GPIO17){
++ reg = PCA9535_INPUT_1;
++ gpio -= GPIO8;
++ }
++
++ down(&pca9535_inited.lock);
++
++ // Read the existing values first
++ val = pca9535_read_reg(pca9535_i2c_client, reg) >> 8;
++ val = (val >> gpio) & 0x01;
++
++ up(&pca9535_inited.lock);
++
++ return val;
++}
++
++/*
++ * Set the value of I2C GPIO.
++ */
++int pca9535_gpio_write(int gpio, unsigned char value){
++ unsigned char in_reg = 0;
++ unsigned char out_reg = 0;
++ unsigned long val = 0;
++ unsigned long old = 0;
++ int ret = 0;
++
++ if(!pca9535_i2c_client)
++ return -ENODEV;
++
++ if(gpio < GPIO0 || gpio > GPIO17)
++ return -EINVAL;
++
++ if(gpio >= GPIO0 && gpio <= GPIO7){
++ in_reg = PCA9535_INPUT_0;
++ out_reg = PCA9535_OUTPUT_0;
++ gpio -= GPIO0;
++ }else if(gpio >= GPIO8 && gpio <= GPIO17){
++ in_reg = PCA9535_INPUT_1;
++ out_reg = PCA9535_OUTPUT_1;
++ gpio -= GPIO8;
++ }
++
++ down(&pca9535_inited.lock);
++
++ // Read the existing values first
++ val = pca9535_read_reg(pca9535_i2c_client, in_reg);
++ old = val >> 8;
++
++ switch(value){
++ case LOW:
++ old |= (1 << gpio);
++ break;
++ case HI:
++ old &= ~(1 << gpio);
++ break;
++ default:
++ ret = -EINVAL;
++ goto error;
++ }
++
++ val = (val & 0xff) | (old << 8);
++
++ // write the values back to the register
++ pca9535_write_reg(pca9535_i2c_client, out_reg, val);
++error:
++
++ up(&pca9535_inited.lock);
++ return ret;
++}
++
++/*
++ * Set the direction of I2C GPIO.
++ */
++int pca9535_gpio_direction(int gpio, unsigned char direction){
++ unsigned char reg = 0;
++ unsigned long val = 0;
++ unsigned long old = 0;
++ int ret = 0;
++
++ if(!pca9535_i2c_client)
++ return -ENODEV;
++
++ if(gpio < GPIO0 || gpio > GPIO17)
++ return -EINVAL;
++
++ if(gpio >= GPIO0 && gpio <= GPIO7){
++ reg = PCA9535_DIRECTION_0;
++ gpio -= GPIO0;
++ }else if(gpio >= GPIO8 && gpio <= GPIO17){
++ reg = PCA9535_DIRECTION_1;
++ gpio -= GPIO8;
++ }
++
++ down(&pca9535_inited.lock);
++
++ // Read the existing values first
++ old = pca9535_read_reg(pca9535_i2c_client, reg);
++ val = old >> 8;
++
++ switch(direction){
++ case GPIO_INPUT:
++ val |= (1 << gpio);
++ break;
++ case GPIO_OUTPUT:
++ val &= ~(1 << gpio);
++ break;
++ default:
++ ret = -EINVAL;
++ goto error;
++ }
++
++ val = (old & 0xff) | (val << 8);
++
++ // write the values back to the register
++ pca9535_write_reg(pca9535_i2c_client, reg, val);
++error:
++
++ up(&pca9535_inited.lock);
++ return ret;
++}
++
++static u32 pca9535_read_reg(struct i2c_client *client, u8 regaddr)
++{
++ char buffer[3];
++ int r;
++ u32 data;
++
++ buffer[0] = regaddr;
++ buffer[1] = 0;
++ buffer[2] = 0;
++
++ r = i2c_master_send(client, buffer, 1);
++ if (r != 1) {
++ printk(KERN_ERR "pca9535: read failed, status %d\n", r);
++ return 0xffffffff;
++ }
++
++ r = i2c_master_recv(client, buffer, 3);
++ if (r != 3) {
++ printk(KERN_ERR "pca9535: read failed, status %d\n", r);
++ return 0xffffffff;
++ }
++
++ data = buffer[1];
++ data |= buffer[2] << 8;
++ //printk(KERN_ERR "%s: reading %x in %x\n", __FUNCTION__, data, regaddr);
++
++ return data;
++}
++
++static void pca9535_write_reg(struct i2c_client *client, u8 regaddr, u16 data)
++{
++ char buffer[3];
++ int r;
++
++ //printk(KERN_ERR "%s: writing %x in %x\n", __FUNCTION__, data, regaddr);
++ buffer[0] = regaddr;
++ buffer[1] = data >> 8;
++ buffer[2] = data & 0xff;
++
++ r = i2c_master_send(client, buffer, 3);
++ if (r != 3) {
++ printk(KERN_ERR "pca9535: write failed, status %d\n", r);
++ }
++}
++
++MODULE_AUTHOR("Husam Senussi <husamsenussi@gmail.com>");
++MODULE_DESCRIPTION("PCA9535 driver");
++MODULE_LICENSE("GPL");
++
++module_init(pca9535_init);
++module_exit(pca9535_exit);
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/i2c-core.c bt_kernel/drivers/i2c/i2c-core.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/i2c/i2c-core.c 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/i2c/i2c-core.c 2005-10-22 03:52:45.687256000 +0300
+@@ -1180,8 +1180,12 @@
+ command,size,data);
+ up(&adapter->bus_lock);
+ } else
++ {
++ printk("i2c-core, calling i2c_smbus_xfer_emulated!");
+ res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
+ command,size,data);
++ printk("i2c-core, i2c_smbus_xfer_emulated retVal = %d", res);
++ }
+
+ if(res >= 0 && swpec &&
+ size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA &&
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/media/video/omap/omap16xxcam.c bt_kernel/drivers/media/video/omap/omap16xxcam.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/media/video/omap/omap16xxcam.c 2005-10-30 17:44:17.750038779 +0200
++++ bt_kernel/drivers/media/video/omap/omap16xxcam.c 2005-10-30 16:32:39.609796000 +0200
+@@ -325,18 +325,22 @@
+
+ if (machine_is_omap_h3())
+ omap_set_dma_src_params(dmach, OMAP_DMA_PORT_OCP_T1,
+- OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG);
++ OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG,
++ 0, 0);
+ else
+ omap_set_dma_src_params(dmach, OMAP_DMA_PORT_TIPB,
+- OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG);
++ OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG,
++ 0, 0);
+
+ omap_set_dma_dest_params(dmach, OMAP_DMA_PORT_EMIFF,
+- OMAP_DMA_AMODE_POST_INC, sg_dma_address(sglist));
++ OMAP_DMA_AMODE_POST_INC, sg_dma_address(sglist),
++ 0, 0);
+
+ omap_set_dma_transfer_params(dmach, OMAP_DMA_DATA_TYPE_S32,
+ FIFO_TRIGGER_LVL,
+ sg_dma_len(sglist)/(4 * FIFO_TRIGGER_LVL),
+- OMAP_DMA_SYNC_FRAME);
++ OMAP_DMA_SYNC_FRAME,
++ 0, 0);
+
+
+ omap_writew(omap_readw(OMAP_DMA_CLNK_CTRL(dmach)) & ~(1<<15),
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/mmc/omap.c bt_kernel/drivers/mmc/omap.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/mmc/omap.c 2005-10-30 17:44:17.905014111 +0200
++++ bt_kernel/drivers/mmc/omap.c 2005-10-30 16:32:39.609796000 +0200
+@@ -4,6 +4,7 @@
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
++ * Other hacks (DMA, SD, etc) by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -703,20 +704,24 @@
+ buf = 0x800f | ((frame - 1) << 8);
+ omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- data_addr);
++ data_addr,
++ 0, 0);
+ omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+- sg_dma_address(sg));
++ sg_dma_address(sg),
++ 0, 0);
+ omap_set_dma_dest_data_pack(dma_ch, 1);
+ omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ } else {
+ buf = 0x0f80 | ((frame - 1) << 0);
+ omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- data_addr);
++ data_addr,
++ 0, 0);
+ omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+- sg_dma_address(sg));
++ sg_dma_address(sg),
++ 0, 0);
+ omap_set_dma_src_data_pack(dma_ch, 1);
+ omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ }
+@@ -727,7 +732,8 @@
+
+ OMAP_MMC_WRITE(host->base, BUF, buf);
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
+- frame, count, OMAP_DMA_SYNC_FRAME);
++ frame, count, OMAP_DMA_SYNC_FRAME,
++ 0, 0);
+ }
+
+ /* a scatterlist segment completed */
+@@ -767,9 +773,6 @@
+
+ host->sg_idx++;
+ if (host->sg_idx < host->sg_len) {
+- /* REVISIT we only checked the first segment
+- * for being a dma candidate ...
+- */
+ mmc_omap_prepare_dma(host, host->data);
+ omap_start_dma(host->dma_ch);
+ } else
+@@ -1089,9 +1092,10 @@
+ if (dsor > 250)
+ dsor = 250;
+ dsor++;
+- }
+
+- /* REVISIT: if (ios->bus_width == MMC_BUS_WIDTH_4) dsor |= 1 << 15; */
++ if (ios->bus_width == MMC_BUS_WIDTH_4)
++ dsor |= 1 << 15;
++ }
+
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+@@ -1132,9 +1136,17 @@
+ clk_unuse(host->fclk);
+ }
+
++static int mmc_omap_get_ro(struct mmc_host *mmc)
++{
++ struct mmc_omap_host *host = mmc_priv(mmc);
++
++ return host->wp_pin && omap_get_gpio_datain(host->wp_pin);
++}
++
+ static struct mmc_host_ops mmc_omap_ops = {
+ .request = mmc_omap_request,
+ .set_ios = mmc_omap_set_ios,
++ .get_ro = mmc_omap_get_ro,
+ };
+
+ static int __init mmc_omap_probe(struct device *dev)
+@@ -1192,9 +1204,7 @@
+ goto out;
+ }
+
+- /* REVISIT: SD-only support, when core merged
+- * - if (minfo->wire4) mmc->caps |= MMC_CAP_4_BIT_DATA;
+- * - mmc_omap_ops.get_ro uses wp_pin to sense slider
++ /* REVISIT:
+ * Also, use minfo->cover to decide how to manage
+ * the card detect sensing.
+ */
+@@ -1212,6 +1222,9 @@
+ host->irq = pdev->resource[1].start;
+ host->base = (void __iomem *)pdev->resource[0].start;
+
++ if (minfo->wire4)
++ mmc->caps |= MMC_CAP_4_BIT_DATA;
++
+ mmc->ops = &mmc_omap_ops;
+ mmc->f_min = 400000;
+ mmc->f_max = 24000000;
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/mtd/nand/omap-hw.c bt_kernel/drivers/mtd/nand/omap-hw.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/mtd/nand/omap-hw.c 2005-10-30 17:44:18.133977666 +0200
++++ bt_kernel/drivers/mtd/nand/omap-hw.c 2005-10-30 16:32:39.609796000 +0200
+@@ -232,25 +232,30 @@
+ fifo_reg = NAND_BASE + NND_FIFO;
+ if (is_write) {
+ omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_TIPB,
+- OMAP_DMA_AMODE_CONSTANT, fifo_reg);
++ OMAP_DMA_AMODE_CONSTANT, fifo_reg,
++ 0, 0);
+ omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+- virt_to_phys(addr));
++ virt_to_phys(addr),
++ 0, 0);
+ // omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ /* Set POSTWRITE bit */
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 16));
+ } else {
+ omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_TIPB,
+- OMAP_DMA_AMODE_CONSTANT, fifo_reg);
++ OMAP_DMA_AMODE_CONSTANT, fifo_reg,
++ 0, 0);
+ omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+- virt_to_phys(addr));
++ virt_to_phys(addr),
++ 0, 0);
+ // omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_8);
+ /* Set PREFETCH bit */
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 17));
+ }
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, block_size / 4,
+- block_count, OMAP_DMA_SYNC_FRAME);
++ block_count, OMAP_DMA_SYNC_FRAME,
++ 0, 0);
+ init_completion(&comp);
+
+ len = u32_count << 2;
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/irda/omap1610-ir.c bt_kernel/drivers/net/irda/omap1610-ir.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/irda/omap1610-ir.c 2005-10-30 17:44:18.200967003 +0200
++++ bt_kernel/drivers/net/irda/omap1610-ir.c 2005-10-30 16:32:39.609796000 +0200
+@@ -166,14 +166,16 @@
+ static void omap1610_irda_start_rx_dma(struct omap1610_irda *si)
+ {
+ /* Configure DMA */
+- omap_set_dma_src_params(si->rx_dma_channel, 0x3, 0x0, (unsigned long)UART3_RHR);
++ omap_set_dma_src_params(si->rx_dma_channel, 0x3, 0x0, (unsigned long)UART3_RHR,
++ 0, 0);
+
+ omap_enable_dma_irq(si->rx_dma_channel, 0x01);
+
+ omap_set_dma_dest_params(si->rx_dma_channel, 0x0, 0x1,
+- si->rx_buf_dma_phys);
++ si->rx_buf_dma_phys,
++ 0, 0);
+
+- omap_set_dma_transfer_params(si->rx_dma_channel, 0x0, 4096, 0x1, 0x0);
++ omap_set_dma_transfer_params(si->rx_dma_channel, 0x0, 4096, 0x1, 0x0, 0, 0);
+
+ omap_start_dma(si->rx_dma_channel);
+ }
+@@ -183,14 +185,16 @@
+ __ECHO_IN;
+
+ /* Configure DMA */
+- omap_set_dma_dest_params(si->tx_dma_channel, 0x03, 0x0, (unsigned long)UART3_THR);
++ omap_set_dma_dest_params(si->tx_dma_channel, 0x03, 0x0, (unsigned long)UART3_THR,
++ 0, 0);
+
+ omap_enable_dma_irq(si->tx_dma_channel, 0x01);
+
+ omap_set_dma_src_params(si->tx_dma_channel, 0x0, 0x1,
+- si->tx_buf_dma_phys);
++ si->tx_buf_dma_phys,
++ 0, 0);
+
+- omap_set_dma_transfer_params(si->tx_dma_channel, 0x0, size, 0x1, 0x0);
++ omap_set_dma_transfer_params(si->tx_dma_channel, 0x0, size, 0x1, 0x0, 0, 0);
+
+ HDBG1(1);
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/Kconfig bt_kernel/drivers/net/wireless/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/Kconfig 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/net/wireless/Kconfig 2005-10-22 03:52:45.687256000 +0300
+@@ -483,5 +483,7 @@
+ depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
+ default y
+
++source "drivers/net/wireless/tiacx/Kconfig"
++
+ endmenu
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/Makefile bt_kernel/drivers/net/wireless/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/Makefile 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/net/wireless/Makefile 2005-10-14 18:55:31.156317000 +0300
+@@ -39,3 +39,4 @@
+ # 16-bit wireless PCMCIA client drivers
+ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
+ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
++obj-$(CONFIG_ACX) += tiacx/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx_config.h bt_kernel/drivers/net/wireless/tiacx/acx_config.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx_config.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/acx_config.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,44 @@
++/* temporary hack until proper Kconfig integration */
++#define CONFIG_ACX_PCI 1
++#define CONFIG_ACX_USB 1
++
++#define WLAN_RELEASE "v0.3.10"
++
++/* set to 0 if you don't want any debugging code to be compiled in */
++/* set to 1 if you want some debugging */
++/* set to 2 if you want extensive debug log */
++#define ACX_DEBUG 2
++
++/* assume 32bit I/O width
++ * (16bit is also compatible with Compact Flash) */
++#define ACX_IO_WIDTH 16
++
++/* Set this to 1 if you want monitor mode to use
++ * phy header. Currently it is not useful anyway since we
++ * don't know what useful info (if any) is in phy header.
++ * If you want faster/smaller code, say 0 here */
++#define WANT_PHY_HDR 0
++
++/* whether to do Tx descriptor cleanup in softirq (i.e. not in IRQ
++ * handler) or not. Note that doing it later does slightly increase
++ * system load, so still do that stuff in the IRQ handler for now,
++ * even if that probably means worse latency */
++#define TX_CLEANUP_IN_SOFTIRQ 0
++
++/* set to 1 if you want to have 1 driver per card instead of 1 single driver
++ * managing all cards (of a particular bus type) in your system
++ * Useful e.g. if you need to reinitialize single cards from time to time
++ * LINUX 2.4.X ONLY!! (pci_for_each_dev()) Feel free to implement 2.6.x
++ * compatibility... */
++#define SEPARATE_DRIVER_INSTANCES 0
++
++/* Locking: */
++/* very talkative */
++#define PARANOID_LOCKING 1
++/* normal (use when bug-free) */
++/* #define DO_LOCKING 1 */
++/* else locking is disabled! */
++
++/* 0 - normal mode */
++/* 1 - development/debug: probe for IEs on modprobe */
++#define CMD_DISCOVERY 0
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx_func.h bt_kernel/drivers/net/wireless/tiacx/acx_func.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx_func.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/acx_func.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,660 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++
++/***********************************************************************
++** LOGGING
++**
++** - Avoid SHOUTING needlessly. Avoid excessive verbosity.
++** Gradually remove messages which are old debugging aids.
++**
++** - Use printk() for messages which are to be always logged.
++** Supply either 'acx:' or '<devname>:' prefix so that user
++** can figure out who's speaking among other kernel chatter.
++** acx: is for general issues (e.g. "acx: no firmware image!")
++** while <devname>: is related to a particular device
++** (think about multi-card setup). Double check that message
++** is not confusing to the average user.
++**
++** - use printk KERN_xxx level only if message is not a WARNING
++** but is INFO, ERR etc.
++**
++** - Use printk_ratelimited() for messages which may flood
++** (e.g. "rx DUP pkt!").
++**
++** - Use acxlog() for messages which may be omitted (and they
++** _will_ be omitted in non-debug builds). Note that
++** message levels may be disabled at compile-time selectively,
++** thus select them wisely. Example: L_DEBUG is the lowest
++** (most likely to be compiled out) -> use for less important stuff.
++**
++** - Do not print important stuff with acxlog(), or else people
++** will never build non-debug driver.
++**
++** Style:
++** hex: capital letters, zero filled (e.g. 0x02AC)
++** str: dont start from capitals, no trailing periods ("tx: queue is stopped")
++*/
++#if ACX_DEBUG > 1
++
++void log_fn_enter(const char *funcname);
++void log_fn_exit(const char *funcname);
++void log_fn_exit_v(const char *funcname, int v);
++
++#define FN_ENTER \
++ do { \
++ if (unlikely(acx_debug & L_FUNC)) { \
++ log_fn_enter(__func__); \
++ } \
++ } while (0)
++
++#define FN_EXIT1(v) \
++ do { \
++ if (unlikely(acx_debug & L_FUNC)) { \
++ log_fn_exit_v(__func__, v); \
++ } \
++ } while (0)
++#define FN_EXIT0 \
++ do { \
++ if (unlikely(acx_debug & L_FUNC)) { \
++ log_fn_exit(__func__); \
++ } \
++ } while (0)
++
++#else
++
++#define FN_ENTER
++#define FN_EXIT1(v)
++#define FN_EXIT0
++
++#endif /* ACX_DEBUG > 1 */
++
++
++#if ACX_DEBUG
++
++#define acxlog(chan, args...) \
++ do { \
++ if (acx_debug & (chan)) \
++ printk(args); \
++ } while (0)
++#define printk_ratelimited(args...) printk(args)
++
++#else /* Non-debug build: */
++
++#define acxlog(chan, args...)
++/* Standard way of log flood prevention */
++#define printk_ratelimited(args...) \
++do { \
++ if (printk_ratelimit()) \
++ printk(args); \
++} while (0)
++
++#endif /* ACX_DEBUG */
++
++void acx_print_mac(const char *head, const u8 *mac, const char *tail);
++
++/* Optimized out to nothing in non-debug build */
++static inline void
++acxlog_mac(int level, const char *head, const u8 *mac, const char *tail)
++{
++ if (acx_debug & level) {
++ acx_print_mac(head, mac, tail);
++ }
++}
++
++
++/***********************************************************************
++** MAC address helpers
++*/
++static inline void
++MAC_COPY(u8 *mac, const u8 *src)
++{
++ *(u32*)mac = *(u32*)src;
++ ((u16*)mac)[2] = ((u16*)src)[2];
++ /* kernel's memcpy will do the same: memcpy(dst, src, ETH_ALEN); */
++}
++
++static inline void
++MAC_FILL(u8 *mac, u8 val)
++{
++ memset(mac, val, ETH_ALEN);
++}
++
++static inline void
++MAC_BCAST(u8 *mac)
++{
++ ((u16*)mac)[2] = *(u32*)mac = -1;
++}
++
++static inline void
++MAC_ZERO(u8 *mac)
++{
++ ((u16*)mac)[2] = *(u32*)mac = 0;
++}
++
++static inline int
++mac_is_equal(const u8 *a, const u8 *b)
++{
++ /* can't beat this */
++ return memcmp(a, b, ETH_ALEN) == 0;
++}
++
++static inline int
++mac_is_bcast(const u8 *mac)
++{
++ /* AND together 4 first bytes with sign-entended 2 last bytes
++ ** Only bcast address gives 0xffffffff. +1 gives 0 */
++ return ( *(s32*)mac & ((s16*)mac)[2] ) + 1 == 0;
++}
++
++static inline int
++mac_is_zero(const u8 *mac)
++{
++ return ( *(u32*)mac | ((u16*)mac)[2] ) == 0;
++}
++
++static inline int
++mac_is_directed(const u8 *mac)
++{
++ return (mac[0] & 1)==0;
++}
++
++static inline int
++mac_is_mcast(const u8 *mac)
++{
++ return (mac[0] & 1) && !mac_is_bcast(mac);
++}
++
++#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X"
++#define MAC(bytevector) \
++ ((unsigned char *)bytevector)[0], \
++ ((unsigned char *)bytevector)[1], \
++ ((unsigned char *)bytevector)[2], \
++ ((unsigned char *)bytevector)[3], \
++ ((unsigned char *)bytevector)[4], \
++ ((unsigned char *)bytevector)[5]
++
++
++/***********************************************************************
++** Random helpers
++*/
++#define TO_STRING(x) #x
++#define STRING(x) TO_STRING(x)
++
++#define CLEAR_BIT(val, mask) ((val) &= ~(mask))
++#define SET_BIT(val, mask) ((val) |= (mask))
++
++/* undefined if v==0 */
++static inline unsigned int
++lowest_bit(u16 v)
++{
++ unsigned int n = 0;
++ while (!(v & 0xf)) { v>>=4; n+=4; }
++ while (!(v & 1)) { v>>=1; n++; }
++ return n;
++}
++
++/* undefined if v==0 */
++static inline unsigned int
++highest_bit(u16 v)
++{
++ unsigned int n = 0;
++ while (v>0xf) { v>>=4; n+=4; }
++ while (v>1) { v>>=1; n++; }
++ return n;
++}
++
++/* undefined if v==0 */
++static inline int
++has_only_one_bit(u16 v)
++{
++ return ((v-1) ^ v) >= v;
++}
++
++
++/***********************************************************************
++** LOCKING
++** We have priv->sem and priv->lock.
++**
++** We employ following naming convention in order to get locking right:
++**
++** acx_e_xxxx - external entry points called from process context.
++** It is okay to sleep. priv->sem is to be taken on entry.
++** acx_i_xxxx - external entry points possibly called from atomic context.
++** Sleeping is not allowed (and thus down(sem) is not legal!)
++** acx_s_xxxx - potentially sleeping functions. Do not ever call under lock!
++** acx_l_xxxx - functions which expect lock to be already taken.
++** rest - non-sleeping functions which do not require locking
++** but may be run inder lock
++**
++** Theory of operation:
++**
++** All process-context entry points (_e_ functions) take sem
++** immediately. IRQ handler and other 'atomic-context' entry points
++** (_i_ functions) take lock immediately on entry, but dont take sem
++** because that might sleep.
++**
++** Thus *all* code is either protected by sem or lock, or both.
++**
++** Code which must not run concurrently with IRQ takes lock.
++** Such code is marked with _l_.
++**
++** This results in the following rules of thumb useful in code review:
++**
++** + If a function calls _s_ fn, it must be an _s_ itself.
++** + You can call _l_ fn only (a) from another _l_ fn
++** or (b) from _s_, _e_ or _i_ fn by taking lock, calling _l_,
++** and dropping lock.
++** + All IRQ code runs under lock.
++** + Any _s_ fn is running under sem.
++** + Code under sem can race only with IRQ code.
++** + Code under sem+lock cannot race with anything.
++*/
++
++/* These functions *must* be inline or they will break horribly on SPARC, due
++ * to its weird semantics for save/restore flags */
++
++#if defined(PARANOID_LOCKING) /* Lock debugging */
++
++void acx_lock_debug(wlandevice_t *priv, const char* where);
++void acx_unlock_debug(wlandevice_t *priv, const char* where);
++void acx_down_debug(wlandevice_t *priv, const char* where);
++void acx_up_debug(wlandevice_t *priv, const char* where);
++void acx_lock_unhold(void);
++void acx_sem_unhold(void);
++
++static inline void
++acx_lock_helper(wlandevice_t *priv, unsigned long *fp, const char* where)
++{
++ acx_lock_debug(priv, where);
++ spin_lock_irqsave(&priv->lock, *fp);
++}
++static inline void
++acx_unlock_helper(wlandevice_t *priv, unsigned long *fp, const char* where)
++{
++ acx_unlock_debug(priv, where);
++ spin_unlock_irqrestore(&priv->lock, *fp);
++}
++static inline void
++acx_down_helper(wlandevice_t *priv, const char* where)
++{
++ acx_down_debug(priv, where);
++}
++static inline void
++acx_up_helper(wlandevice_t *priv, const char* where)
++{
++ acx_up_debug(priv, where);
++}
++#define acx_lock(priv, flags) acx_lock_helper(priv, &(flags), __FILE__ ":" STRING(__LINE__))
++#define acx_unlock(priv, flags) acx_unlock_helper(priv, &(flags), __FILE__ ":" STRING(__LINE__))
++#define acx_sem_lock(priv) acx_down_helper(priv, __FILE__ ":" STRING(__LINE__))
++#define acx_sem_unlock(priv) acx_up_helper(priv, __FILE__ ":" STRING(__LINE__))
++
++#elif defined(DO_LOCKING)
++
++#define acx_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
++#define acx_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
++#define acx_sem_lock(priv) down(&priv->sem)
++#define acx_sem_unlock(priv) up(&priv->sem)
++#define acx_lock_unhold() ((void)0)
++#define acx_sem_unhold() ((void)0)
++
++#else /* no locking! :( */
++
++#define acx_lock(priv, flags) ((void)0)
++#define acx_unlock(priv, flags) ((void)0)
++#define acx_sem_lock(priv) ((void)0)
++#define acx_sem_unlock(priv) ((void)0)
++#define acx_lock_unhold() ((void)0)
++#define acx_sem_unhold() ((void)0)
++
++#endif
++
++
++/***********************************************************************
++*/
++
++/* Can race with rx path (which is not protected by sem):
++** rx -> process_[re]assocresp() -> set_status(ASSOCIATED) -> wake_queue()
++** Can race with tx_complete IRQ:
++** IRQ -> acx_l_clean_tx_desc -> acx_wake_queue
++** Review carefully all callsites */
++static inline void
++acx_stop_queue(netdevice_t *dev, const char *msg)
++{
++ if(netif_queue_stopped(dev))
++ return;
++
++ netif_stop_queue(dev);
++ if (msg)
++ acxlog(L_BUFT, "tx: stop queue %s\n", msg);
++}
++
++static inline int
++acx_queue_stopped(netdevice_t *dev)
++{
++ return netif_queue_stopped(dev);
++}
++
++static inline void
++acx_start_queue(netdevice_t *dev, const char *msg)
++{
++ netif_start_queue(dev);
++ if (msg)
++ acxlog(L_BUFT, "tx: start queue %s\n", msg);
++}
++
++static inline void
++acx_wake_queue(netdevice_t *dev, const char *msg)
++{
++ netif_wake_queue(dev);
++ if (msg)
++ acxlog(L_BUFT, "tx: wake queue %s\n", msg);
++}
++
++static inline void
++acx_carrier_off(netdevice_t *dev, const char *msg)
++{
++ netif_carrier_off(dev);
++ if (msg)
++ acxlog(L_BUFT, "tx: carrier off %s\n", msg);
++}
++
++static inline void
++acx_carrier_on(netdevice_t *dev, const char *msg)
++{
++ netif_carrier_on(dev);
++ if (msg)
++ acxlog(L_BUFT, "tx: carrier on %s\n", msg);
++}
++
++/* This function does not need locking UNLESS you call it
++** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can
++** wake queue. This can race with stop_queue elsewhere. */
++void acx_set_status(wlandevice_t *priv, u16 status);
++
++
++/***********************************************************************
++** Communication with firmware
++*/
++#define CMD_TIMEOUT_MS(n) (n)
++#define ACX_CMD_TIMEOUT_DEFAULT CMD_TIMEOUT_MS(50)
++
++#if ACX_DEBUG
++
++/* We want to log cmd names */
++int acxpci_s_issue_cmd_timeo_debug(wlandevice_t *priv, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr);
++int acxusb_s_issue_cmd_timeo_debug(wlandevice_t *priv, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr);
++static inline int
++acx_s_issue_cmd_timeo_debug(wlandevice_t *priv, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr)
++{
++#if defined(CONFIG_ACX_CFI)
++ return acxpci_s_issue_cmd_timeo_debug(priv, cmd, param, len, timeout, cmdstr);
++#else
++ if (IS_PCI(priv))
++ return acxpci_s_issue_cmd_timeo_debug(priv, cmd, param, len, timeout, cmdstr);
++ return acxusb_s_issue_cmd_timeo_debug(priv, cmd, param, len, timeout, cmdstr);
++#endif
++}
++#define acx_s_issue_cmd(priv,cmd,param,len) \
++ acx_s_issue_cmd_timeo_debug(priv,cmd,param,len,ACX_CMD_TIMEOUT_DEFAULT,#cmd)
++#define acx_s_issue_cmd_timeo(priv,cmd,param,len,timeo) \
++ acx_s_issue_cmd_timeo_debug(priv,cmd,param,len,timeo,#cmd)
++int acx_s_configure_debug(wlandevice_t *priv, void *pdr, int type, const char* str);
++#define acx_s_configure(priv,pdr,type) \
++ acx_s_configure_debug(priv,pdr,type,#type)
++int acx_s_interrogate_debug(wlandevice_t *priv, void *pdr, int type, const char* str);
++#define acx_s_interrogate(priv,pdr,type) \
++ acx_s_interrogate_debug(priv,pdr,type,#type)
++
++#else
++
++int acxpci_s_issue_cmd_timeo(wlandevice_t *priv, unsigned cmd, void *param, unsigned len, unsigned timeout);
++int acxusb_s_issue_cmd_timeo(wlandevice_t *priv, unsigned cmd, void *param, unsigned len, unsigned timeout);
++static inline int
++acx_s_issue_cmd_timeo(wlandevice_t *priv, unsigned cmd, void *param, unsigned len, unsigned timeout)
++{
++ if (IS_PCI(priv))
++ return acxpci_s_issue_cmd_timeo(priv, cmd, param, len, timeout);
++ return acxusb_s_issue_cmd_timeo(priv, cmd, param, len, timeout);
++}
++static inline int
++acx_s_issue_cmd(wlandevice_t *priv, unsigned cmd, void *param, unsigned len)
++{
++ if (IS_PCI(priv))
++ return acxpci_s_issue_cmd_timeo(priv, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT);
++ return acxusb_s_issue_cmd_timeo(priv, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT);
++}
++int acx_s_configure(wlandevice_t *priv, void *pdr, int type);
++int acx_s_interrogate(wlandevice_t *priv, void *pdr, int type);
++
++#endif
++
++void acx_s_cmd_start_scan(wlandevice_t *priv);
++
++
++/***********************************************************************
++** Ioctls
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra);
++int
++acx100pci_ioctl_set_phy_amp_bias(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra);
++
++
++/***********************************************************************
++** Unsorted yet :)
++*/
++int acxpci_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf);
++int acxusb_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf);
++static inline int
++acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf)
++{
++#if defined(CONFIG_ACX_CFI)
++ return acxpci_s_read_phy_reg(priv, reg, charbuf);
++#else
++ if (IS_PCI(priv))
++ return acxpci_s_read_phy_reg(priv, reg, charbuf);
++ return acxusb_s_read_phy_reg(priv, reg, charbuf);
++#endif
++}
++
++int acxpci_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value);
++int acxusb_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value);
++static inline int
++acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value)
++{
++#if defined(CONFIG_ACX_CFI)
++ return acxpci_s_write_phy_reg(priv, reg, value);
++#else
++ if (IS_PCI(priv))
++ return acxpci_s_write_phy_reg(priv, reg, value);
++ return acxusb_s_write_phy_reg(priv, reg, value);
++#endif
++}
++
++void acx_s_msleep(int ms);
++int acx_s_init_mac(netdevice_t *dev);
++void acx_set_reg_domain(wlandevice_t *priv, unsigned char reg_dom_id);
++void acx_set_timer(wlandevice_t *priv, int timeout_us);
++void acx_update_capabilities(wlandevice_t *priv);
++int acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf);
++void acx_s_start(wlandevice_t *priv);
++#if USE_FW_LOADER_26
++firmware_image_t *acx_s_read_fw(struct device *dev, const char *file, u32 *size);
++#else
++firmware_image_t *acx_s_read_fw(const char *file, u32 *size);
++#define acx_s_read_fw(dev, file, size) acx_s_read_fw(file, size)
++#endif
++void acx_s_initialize_rx_config(wlandevice_t *priv);
++void acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all);
++void acx_init_task_scheduler(wlandevice_t *priv);
++void acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag);
++int acx_s_upload_radio(wlandevice_t *priv);
++void acx_read_configoption(wlandevice_t *priv);
++int acx_proc_register_entries(const struct net_device *dev);
++int acx_proc_unregister_entries(const struct net_device *dev);
++void acx_l_update_ratevector(wlandevice_t *priv);
++
++int acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd);
++
++client_t *acx_l_sta_list_get(wlandevice_t *priv, const u8 *address);
++void acx_l_sta_list_del(wlandevice_t *priv, client_t *clt);
++
++int acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt);
++void acx_i_timer(unsigned long a);
++int acx_s_complete_scan(wlandevice_t *priv);
++
++static inline wlan_hdr_t*
++acx_get_wlan_hdr(wlandevice_t *priv, const rxbuffer_t *rxbuf)
++{
++ if (!(priv->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR))
++ return (wlan_hdr_t*)&rxbuf->hdr_a3;
++
++ /* take into account phy header in front of packet */
++ if (IS_ACX111(priv))
++ return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 8);
++
++ return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 4);
++}
++
++struct sk_buff *acx_rxbuf_to_ether(struct wlandevice *priv, rxbuffer_t *rxbuf);
++
++void acx_l_power_led(wlandevice_t *priv, int enable);
++
++unsigned int acx_l_clean_tx_desc(wlandevice_t *priv);
++void acx_l_clean_tx_desc_emergency(wlandevice_t *priv);
++
++u8 acx_signal_determine_quality(u8 signal, u8 noise);
++
++void acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf);
++void acx_l_process_rx_desc(wlandevice_t *priv);
++
++tx_t* acxpci_l_alloc_tx(wlandevice_t *priv);
++tx_t* acxusb_l_alloc_tx(wlandevice_t *priv);
++static inline tx_t*
++acx_l_alloc_tx(wlandevice_t *priv)
++{
++#if defined(CONFIG_ACX_CFI)
++ return acxpci_l_alloc_tx(priv);
++#else
++ if (IS_PCI(priv))
++ return acxpci_l_alloc_tx(priv);
++ return acxusb_l_alloc_tx(priv);
++#endif
++}
++
++void* acxpci_l_get_txbuf(wlandevice_t *priv, tx_t *tx_opaque);
++void* acxusb_l_get_txbuf(wlandevice_t *priv, tx_t *tx_opaque);
++static inline void*
++acx_l_get_txbuf(wlandevice_t *priv, tx_t *tx_opaque)
++{
++#if defined(CONFIG_ACX_CFI)
++ return acxpci_l_get_txbuf(priv, tx_opaque);
++#else
++ if (IS_PCI(priv))
++ return acxpci_l_get_txbuf(priv, tx_opaque);
++ return acxusb_l_get_txbuf(priv, tx_opaque);
++#endif
++}
++
++void acxpci_l_tx_data(wlandevice_t *priv, tx_t *tx_opaque, int len);
++void acxusb_l_tx_data(wlandevice_t *priv, tx_t *tx_opaque, int len);
++static inline void
++acx_l_tx_data(wlandevice_t *priv, tx_t *tx_opaque, int len)
++{
++#if defined(CONFIG_ACX_CFI)
++ acxpci_l_tx_data(priv, tx_opaque, len);
++#else
++ if (IS_PCI(priv))
++ acxpci_l_tx_data(priv, tx_opaque, len);
++ else
++ acxusb_l_tx_data(priv, tx_opaque, len);
++#endif
++}
++
++void acx_dump_bytes(const void *, int);
++void acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr);
++
++u8 acx_rate111to100(u16);
++
++void acx100usb_l_tx_data(wlandevice_t *priv, struct txdesc *desc);
++int acx_s_set_defaults(wlandevice_t *priv);
++void acx_init_mboxes(wlandevice_t *priv);
++
++int acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb);
++
++#if !ACX_DEBUG
++static inline const char* acx_get_packet_type_string(u16 fc) { return ""; }
++#else
++const char* acx_get_packet_type_string(u16 fc);
++#endif
++const char* acx_cmd_status_str(unsigned int state);
++
++int acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev);
++void acx_free_desc_queues(wlandevice_t *priv);
++
++int acx_s_create_hostdesc_queues(wlandevice_t *priv);
++void acx_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start);
++
++int acx100_s_init_wep(wlandevice_t *priv);
++int acx100_s_init_packet_templates(wlandevice_t *priv);
++int acx111_s_init_packet_templates(wlandevice_t *priv);
++
++void great_inquisitor(wlandevice_t *priv);
++
++char* acxpci_s_proc_diag_output(char *p, wlandevice_t *priv);
++int acx_proc_eeprom_output(char *p, wlandevice_t *priv);
++void acx_set_interrupt_mask(wlandevice_t *priv);
++int acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm);
++
++#if defined(CONFIG_ACX_CFI)
++int __init acxcfi_e_init_module(void);
++#else
++int __init acxpci_e_init_module(void);
++int __init acxusb_e_init_module(void);
++#endif
++
++#if defined(CONFIG_ACX_CFI)
++void __exit acxcfi_e_cleanup_module(void);
++#else
++void __exit acxpci_e_cleanup_module(void);
++void __exit acxusb_e_cleanup_module(void);
++#endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx.h bt_kernel/drivers/net/wireless/tiacx/acx.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/acx.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,6 @@
++#include "acx_config.h"
++#include "wlan_compat.h"
++#include "wlan_hdr.h"
++#include "wlan_mgmt.h"
++#include "acx_struct.h"
++#include "acx_func.h"
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx_struct.h bt_kernel/drivers/net/wireless/tiacx/acx_struct.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/acx_struct.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/acx_struct.h 2005-10-29 22:02:44.690471000 +0300
+@@ -0,0 +1,1966 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** Forward declarations of types
++*/
++typedef struct tx tx_t;
++typedef struct wlandevice wlandevice_t;
++typedef struct client client_t;
++typedef struct rxdesc rxdesc_t;
++typedef struct txdesc txdesc_t;
++typedef struct rxhostdesc rxhostdesc_t;
++typedef struct txhostdesc txhostdesc_t;
++
++
++/***********************************************************************
++** Debug / log functionality
++*/
++enum {
++ L_LOCK = (ACX_DEBUG>1)*0x0001, /* locking debug log */
++ L_INIT = (ACX_DEBUG>0)*0x0002, /* special card initialization logging */
++ L_IRQ = (ACX_DEBUG>0)*0x0004, /* interrupt stuff */
++ L_ASSOC = (ACX_DEBUG>0)*0x0008, /* assocation (network join) and station log */
++ L_FUNC = (ACX_DEBUG>1)*0x0020, /* logging of function enter / leave */
++ L_XFER = (ACX_DEBUG>1)*0x0080, /* logging of transfers and mgmt */
++ L_DATA = (ACX_DEBUG>1)*0x0100, /* logging of transfer data */
++ L_DEBUG = (ACX_DEBUG>1)*0x0200, /* log of debug info */
++ L_IOCTL = (ACX_DEBUG>0)*0x0400, /* log ioctl calls */
++ L_CTL = (ACX_DEBUG>1)*0x0800, /* log of low-level ctl commands */
++ L_BUFR = (ACX_DEBUG>1)*0x1000, /* debug rx buffer mgmt (ring buffer etc.) */
++ L_XFER_BEACON = (ACX_DEBUG>1)*0x2000, /* also log beacon packets */
++ L_BUFT = (ACX_DEBUG>1)*0x4000, /* debug tx buffer mgmt (ring buffer etc.) */
++ L_USBRXTX = (ACX_DEBUG>0)*0x8000, /* debug USB rx/tx operations */
++ L_BUF = L_BUFR + L_BUFT,
++ L_ANY = 0xffff
++};
++
++#if ACX_DEBUG
++extern unsigned int acx_debug;
++#else
++enum { acx_debug = 0 };
++#endif
++
++
++/*============================================================================*
++ * Random helpers *
++ *============================================================================*/
++#define ACX_PACKED __WLAN_ATTRIB_PACK__
++
++#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0]))
++
++/* Use worker_queues for 2.5/2.6 Kernels and queue tasks for 2.4 Kernels
++ (used for the 'bottom half' of the interrupt routine) */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41)
++#include <linux/workqueue.h>
++/* #define NEWER_KERNELS_ONLY 1 */
++#define USE_WORKER_TASKS
++#define WORK_STRUCT struct work_struct
++#define SCHEDULE_WORK schedule_work
++#define FLUSH_SCHEDULED_WORK flush_scheduled_work
++
++#else
++#include <linux/tqueue.h>
++#define USE_QUEUE_TASKS
++#define WORK_STRUCT struct tq_struct
++#define SCHEDULE_WORK schedule_task
++#define INIT_WORK(work, func, ndev) \
++ do { \
++ (work)->routine = (func); \
++ (work)->data = (ndev); \
++ } while (0)
++#define FLUSH_SCHEDULED_WORK flush_scheduled_tasks
++
++#endif
++
++
++/*============================================================================*
++ * Constants *
++ *============================================================================*/
++#define OK 0
++#define NOT_OK 1
++
++/* The supported chip models */
++#define CHIPTYPE_ACX100 1
++#define CHIPTYPE_ACX111 2
++
++#define IS_ACX100(priv) ((priv)->chip_type == CHIPTYPE_ACX100)
++#define IS_ACX111(priv) ((priv)->chip_type == CHIPTYPE_ACX111)
++
++/* Supported interfaces */
++#define DEVTYPE_PCI 0
++#define DEVTYPE_USB 1
++
++#if defined(CONFIG_ACX_PCI)
++ #if !defined(CONFIG_ACX_USB)
++ #define IS_PCI(priv) 1
++ #else
++ #define IS_PCI(priv) ((priv)->dev_type == DEVTYPE_PCI)
++ #endif
++#else
++ #define IS_PCI(priv) 0
++#endif
++
++#if defined(CONFIG_ACX_USB)
++ #if !defined(CONFIG_ACX_PCI)
++ #define IS_USB(priv) 1
++ #else
++ #define IS_USB(priv) ((priv)->dev_type == DEVTYPE_USB)
++ #endif
++#else
++ #define IS_USB(priv) 0
++#endif
++
++/* Driver defaults */
++#define DEFAULT_DTIM_INTERVAL 10
++/* used to be 2048, but FreeBSD driver changed it to 4096 to work properly
++** in noisy wlans */
++#define DEFAULT_MSDU_LIFETIME 4096
++#define DEFAULT_RTS_THRESHOLD 2312 /* max. size: disable RTS mechanism */
++#define DEFAULT_BEACON_INTERVAL 100
++
++#define ACX100_BAP_DATALEN_MAX 4096
++#define ACX100_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */
++#define ACX100_RIDDATA_MAXLEN ACX100_RID_GUESSING_MAXLEN
++
++/* Support Constants */
++/* Radio type names, found in Win98 driver's TIACXLN.INF */
++#define RADIO_MAXIM_0D 0x0d
++#define RADIO_RFMD_11 0x11
++#define RADIO_RALINK_15 0x15
++/* used in ACX111 cards (WG311v2, WL-121, ...): */
++#define RADIO_RADIA_16 0x16
++/* most likely *sometimes* used in ACX111 cards: */
++#define RADIO_UNKNOWN_17 0x17
++/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */
++#define RADIO_UNKNOWN_19 0x19
++
++/* Controller Commands */
++/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */
++#define ACX1xx_CMD_RESET 0x00
++#define ACX1xx_CMD_INTERROGATE 0x01
++#define ACX1xx_CMD_CONFIGURE 0x02
++#define ACX1xx_CMD_ENABLE_RX 0x03
++#define ACX1xx_CMD_ENABLE_TX 0x04
++#define ACX1xx_CMD_DISABLE_RX 0x05
++#define ACX1xx_CMD_DISABLE_TX 0x06
++#define ACX1xx_CMD_FLUSH_QUEUE 0x07
++#define ACX1xx_CMD_SCAN 0x08
++#define ACX1xx_CMD_STOP_SCAN 0x09
++#define ACX1xx_CMD_CONFIG_TIM 0x0a
++#define ACX1xx_CMD_JOIN 0x0b
++#define ACX1xx_CMD_WEP_MGMT 0x0c
++#ifdef OLD_FIRMWARE_VERSIONS
++#define ACX100_CMD_HALT 0x0e /* mapped to unknownCMD in FW150 */
++#else
++#define ACX1xx_CMD_MEM_READ 0x0d
++#define ACX1xx_CMD_MEM_WRITE 0x0e
++#endif
++#define ACX1xx_CMD_SLEEP 0x0f
++#define ACX1xx_CMD_WAKE 0x10
++#define ACX1xx_CMD_UNKNOWN_11 0x11 /* mapped to unknownCMD in FW150 */
++#define ACX100_CMD_INIT_MEMORY 0x12
++#define ACX1xx_CMD_CONFIG_BEACON 0x13
++#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE 0x14
++#define ACX1xx_CMD_CONFIG_NULL_DATA 0x15
++#define ACX1xx_CMD_CONFIG_PROBE_REQUEST 0x16
++#define ACX1xx_CMD_TEST 0x17
++#define ACX1xx_CMD_RADIOINIT 0x18
++#define ACX111_CMD_RADIOCALIB 0x19
++
++/* 'After Interrupt' Commands */
++#define ACX_AFTER_IRQ_CMD_STOP_SCAN 0x01
++#define ACX_AFTER_IRQ_CMD_ASSOCIATE 0x02
++#define ACX_AFTER_IRQ_CMD_RADIO_RECALIB 0x04
++#define ACX_AFTER_IRQ_UPDATE_CARD_CFG 0x08
++#define ACX_AFTER_IRQ_TX_CLEANUP 0x10
++#define ACX_AFTER_IRQ_COMPLETE_SCAN 0x20
++#define ACX_AFTER_IRQ_RESTART_SCAN 0x40
++
++/***********************************************************************
++** Tx/Rx buffer sizes and watermarks
++*/
++/* BTW, this will alloc and use DMAable buffers of
++** WLAN_A4FR_MAXLEN_WEP_FCS * (RX_CNT + TX_CNT) bytes
++** RX/TX_CNT=32 -> ~150k DMA buffers
++** RX/TX_CNT=16 -> ~75k DMA buffers
++*/
++#define RX_CNT 32
++#define TX_CNT 32
++
++/* we clean up txdescs when we have N free txdesc: */
++#define TX_START_CLEAN (TX_CNT - (TX_CNT/4))
++#define TX_EMERG_CLEAN 2
++/* we stop queue if we have less than N free txbufs: */
++#define TX_STOP_QUEUE 3
++/* we start queue if we have more than N free txbufs: */
++#define TX_START_QUEUE 6
++
++/***********************************************************************
++** Interrogate/Configure cmd constants
++**
++** NB: length includes JUST the data part of the IE
++** (does not include size of the (type,len) pair)
++**
++** TODO: seems that acx100, acx100usb, acx111 have some differences,
++** fix code with regard to this!
++*/
++
++#define DEF_IE(name, val, len) enum { ACX##name=val, ACX##name##_LEN=len }
++
++/* Information Elements: Network Parameters, Static Configuration Entities */
++/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */
++DEF_IE(1xx_IE_UNKNOWN_00 ,0x0000, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_ACX_TIMER ,0x0001, 0x10);
++DEF_IE(1xx_IE_POWER_MGMT ,0x0002, 0x06);
++DEF_IE(1xx_IE_QUEUE_CONFIG ,0x0003, 0x1c);
++DEF_IE(100_IE_BLOCK_SIZE ,0x0004, 0x02);
++DEF_IE(1xx_IE_MEMORY_CONFIG_OPTIONS ,0x0005, 0x14);
++DEF_IE(1xx_IE_RATE_FALLBACK ,0x0006, 0x01);
++DEF_IE(100_IE_WEP_OPTIONS ,0x0007, 0x03);
++DEF_IE(111_IE_RADIO_BAND ,0x0007, -1);
++DEF_IE(1xx_IE_MEMORY_MAP ,0x0008, 0x28); /* huh? */
++DEF_IE(100_IE_SSID ,0x0008, 0x20); /* huh? */
++DEF_IE(1xx_IE_SCAN_STATUS ,0x0009, 0x04); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_ASSOC_ID ,0x000a, 0x02);
++DEF_IE(1xx_IE_UNKNOWN_0B ,0x000b, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_UNKNOWN_0C ,0x000c, -1); /* very small implementation in FW150! */
++DEF_IE(111_IE_CONFIG_OPTIONS ,0x000c, 0x14c);
++DEF_IE(1xx_IE_FWREV ,0x000d, 0x18);
++DEF_IE(1xx_IE_FCS_ERROR_COUNT ,0x000e, 0x04);
++DEF_IE(1xx_IE_MEDIUM_USAGE ,0x000f, 0x08);
++DEF_IE(1xx_IE_RXCONFIG ,0x0010, 0x04);
++DEF_IE(100_IE_UNKNOWN_11 ,0x0011, -1); /* NONBINARY: large implementation in FW150! link quality readings or so? */
++DEF_IE(111_IE_QUEUE_THRESH ,0x0011, -1);
++DEF_IE(100_IE_UNKNOWN_12 ,0x0012, -1); /* NONBINARY: VERY large implementation in FW150!! */
++DEF_IE(111_IE_BSS_POWER_SAVE ,0x0012, -1);
++DEF_IE(1xx_IE_FIRMWARE_STATISTICS ,0x0013, 0x9c);
++DEF_IE(1xx_IE_FEATURE_CONFIG ,0x0015, 0x08);
++DEF_IE(111_IE_KEY_CHOOSE ,0x0016, 0x04); /* for rekeying. really len=4?? */
++DEF_IE(1xx_IE_DOT11_STATION_ID ,0x1001, 0x06);
++DEF_IE(100_IE_DOT11_UNKNOWN_1002 ,0x1002, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(111_IE_DOT11_FRAG_THRESH ,0x1002, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_DOT11_BEACON_PERIOD ,0x1003, 0x02); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_DOT11_DTIM_PERIOD ,0x1004, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_DOT11_SHORT_RETRY_LIMIT ,0x1005, 0x01);
++DEF_IE(1xx_IE_DOT11_LONG_RETRY_LIMIT ,0x1006, 0x01);
++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE ,0x1007, 0x20); /* configure default keys */
++DEF_IE(1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME ,0x1008, 0x04);
++DEF_IE(1xx_IE_DOT11_GROUP_ADDR ,0x1009, -1);
++DEF_IE(1xx_IE_DOT11_CURRENT_REG_DOMAIN ,0x100a, 0x02);
++//It's harmless to have larger struct. Use USB case always.
++////#ifdef ACX_PCI
++////DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x01);
++////#else
++DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02);
++////#endif
++DEF_IE(1xx_IE_DOT11_UNKNOWN_100C ,0x100c, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01);
++////#ifdef ACX_PCI
++////DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x01);
++////#else
++DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02);
++////#endif
++//USB doesn't return anything - len==0?!
++DEF_IE(100_IE_DOT11_ED_THRESHOLD ,0x100f, 0x04);
++DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID */
++DEF_IE(100_IE_DOT11_UNKNOWN_1011 ,0x1011, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_DOT11_UNKNOWN_1012 ,0x1012, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_DOT11_UNKNOWN_1013 ,0x1013, -1); /* mapped to cfgInvalid in FW150 */
++
++#if 0
++/* Experimentally obtained on acx100, fw 1.9.8.b
++** -1 means that fw returned 'invalid IE'
++** 0200 FC00 nnnn... are test read contents: u16 type, u16 len, data
++** (AA are poison bytes marking bytes not written by fw)
++**
++** Looks like acx100 fw does not update len field (thus len=256-4=FC here)
++** A number of IEs seem to trash type,len fields
++** IEs marked 'huge' return gobs of data (no poison bytes remain)
++*/
++DEF_IE(100_IE_INVAL_00, 0x0000, -1);
++DEF_IE(100_IE_INVAL_01, 0x0001, -1); /* IE_ACX_TIMER, len=16 on older fw */
++DEF_IE(100_IE_POWER_MGMT, 0x0002, 4); /* 0200FC00 00040000 AAAAAAAA */
++DEF_IE(100_IE_QUEUE_CONFIG, 0x0003, 28); /* 0300FC00 48060000 9CAD0000 0101AAAA DCB00000 E4B00000 9CAA0000 00AAAAAA */
++DEF_IE(100_IE_BLOCK_SIZE, 0x0004, 2); /* 0400FC00 0001AAAA AAAAAAAA AAAAAAAA */
++/* write only: */
++DEF_IE(100_IE_MEMORY_CONFIG_OPTIONS, 0x0005, 20);
++DEF_IE(100_IE_RATE_FALLBACK, 0x0006, 1); /* 0600FC00 00AAAAAA AAAAAAAA AAAAAAAA */
++/* write only: */
++DEF_IE(100_IE_WEP_OPTIONS, 0x0007, 3);
++DEF_IE(100_IE_MEMORY_MAP, 0x0008, 40); /* huge: 0800FC00 30000000 6CA20000 70A20000... */
++/* gives INVAL on read: */
++DEF_IE(100_IE_SCAN_STATUS, 0x0009, -1);
++DEF_IE(100_IE_ASSOC_ID, 0x000a, 2); /* huge: 0A00FC00 00000000 01040800 00000000... */
++DEF_IE(100_IE_INVAL_0B, 0x000b, -1);
++/* 'command rejected': */
++DEF_IE(100_IE_CONFIG_OPTIONS, 0x000c, -3);
++DEF_IE(100_IE_FWREV, 0x000d, 24); /* 0D00FC00 52657620 312E392E 382E6200 AAAAAAAA AAAAAAAA 05050201 AAAAAAAA */
++DEF_IE(100_IE_FCS_ERROR_COUNT, 0x000e, 4);
++DEF_IE(100_IE_MEDIUM_USAGE, 0x000f, 8); /* E41F0000 2D780300 FCC91300 AAAAAAAA */
++DEF_IE(100_IE_RXCONFIG, 0x0010, 4); /* 1000FC00 00280000 AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_QUEUE_THRESH, 0x0011, 12); /* 1100FC00 AAAAAAAA 00000000 00000000 */
++DEF_IE(100_IE_BSS_POWER_SAVE, 0x0012, 1); /* 1200FC00 00AAAAAA AAAAAAAA AAAAAAAA */
++/* read only, variable len */
++DEF_IE(100_IE_FIRMWARE_STATISTICS, 0x0013, 256); /* 0000AC00 00000000 ... */
++DEF_IE(100_IE_INT_CONFIG, 0x0014, 20); /* 00000000 00000000 00000000 00000000 5D74D105 00000000 AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_FEATURE_CONFIG, 0x0015, 8); /* 1500FC00 16000000 AAAAAAAA AAAAAAAA */
++/* returns 'invalid MAC': */
++DEF_IE(100_IE_KEY_CHOOSE, 0x0016, -4);
++DEF_IE(100_IE_INVAL_17, 0x0017, -1);
++DEF_IE(100_IE_UNKNOWN_18, 0x0018, 0); /* null len?! 1800FC00 AAAAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_UNKNOWN_19, 0x0019, 256); /* huge: 1900FC00 9C1F00EA FEFFFFEA FEFFFFEA... */
++DEF_IE(100_IE_INVAL_1A, 0x001A, -1);
++
++DEF_IE(100_IE_DOT11_INVAL_1000, 0x1000, -1);
++DEF_IE(100_IE_DOT11_STATION_ID, 0x1001, 6); /* huge: 0110FC00 58B10E2F 03000000 00000000... */
++DEF_IE(100_IE_DOT11_INVAL_1002, 0x1002, -1);
++DEF_IE(100_IE_DOT11_INVAL_1003, 0x1003, -1);
++DEF_IE(100_IE_DOT11_INVAL_1004, 0x1004, -1);
++DEF_IE(100_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1);
++DEF_IE(100_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1);
++/* write only: */
++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, 32);
++DEF_IE(100_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); /* huge: 0810FC00 00020000 F4010000 00000000... */
++/* undoc but returns something */
++DEF_IE(100_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* huge: 0910FC00 00000000 00000000 00000000... */
++DEF_IE(100_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); /* 0A10FC00 30AAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_CURRENT_ANTENNA, 0x100b, 1); /* 0B10FC00 8FAAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_INVAL_100C, 0x100c, -1);
++DEF_IE(100_IE_DOT11_TX_POWER_LEVEL, 0x100d, 2); /* 00000000 0100AAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_CURRENT_CCA_MODE, 0x100e, 1); /* 0E10FC00 0DAAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_ED_THRESHOLD, 0x100f, 4); /* 0F10FC00 70000000 AAAAAAAA AAAAAAAA */
++/* set default key ID */
++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); /* 1010FC00 00AAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_INVAL_1011, 0x1011, -1);
++DEF_IE(100_IE_DOT11_INVAL_1012, 0x1012, -1);
++DEF_IE(100_IE_DOT11_INVAL_1013, 0x1013, -1);
++DEF_IE(100_IE_DOT11_UNKNOWN_1014, 0x1014, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1015, 0x1015, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1016, 0x1016, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1017, 0x1017, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1018, 0x1018, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1019, 0x1019, 256); /* huge */
++#endif
++
++#if 0
++/* Experimentally obtained on PCI acx111 Xterasys XN-2522g, fw 1.2.1.34
++** -1 means that fw returned 'invalid IE'
++** 0400 0800 nnnn... are test read contents: u16 type, u16 len, data
++** (AA are poison bytes marking bytes not written by fw)
++**
++** Looks like acx111 fw reports real len!
++*/
++DEF_IE(111_IE_INVAL_00, 0x0000, -1);
++DEF_IE(111_IE_INVAL_01, 0x0001, -1);
++DEF_IE(111_IE_POWER_MGMT, 0x0002, 12);
++/* write only, variable len: 12 + rxqueue_cnt*8 + txqueue_cnt*4: */
++DEF_IE(111_IE_MEMORY_CONFIG, 0x0003, 24);
++DEF_IE(111_IE_BLOCK_SIZE, 0x0004, 8); /* 04000800 AA00AAAA AAAAAAAA */
++/* variable len: 8 + rxqueue_cnt*8 + txqueue_cnt*8: */
++DEF_IE(111_IE_QUEUE_HEAD, 0x0005, 24);
++DEF_IE(111_IE_RATE_FALLBACK, 0x0006, 1);
++/* acx100 name:WEP_OPTIONS */
++/* said to have len:1 (not true, actually returns 12 bytes): */
++DEF_IE(111_IE_RADIO_BAND, 0x0007, 12); /* 07000C00 AAAA1F00 FF03AAAA AAAAAAAA */
++DEF_IE(111_IE_MEMORY_MAP, 0x0008, 48);
++/* said to have len:4, but gives INVAL on read: */
++DEF_IE(111_IE_SCAN_STATUS, 0x0009, -1);
++DEF_IE(111_IE_ASSOC_ID, 0x000a, 2);
++/* write only, len is not known: */
++DEF_IE(111_IE_UNKNOWN_0B, 0x000b, 0);
++/* read only, variable len. I see 67 byte reads: */
++DEF_IE(111_IE_CONFIG_OPTIONS, 0x000c, 67); /* 0C004300 01160500 ... */
++DEF_IE(111_IE_FWREV, 0x000d, 24);
++DEF_IE(111_IE_FCS_ERROR_COUNT, 0x000e, 4);
++DEF_IE(111_IE_MEDIUM_USAGE, 0x000f, 8);
++DEF_IE(111_IE_RXCONFIG, 0x0010, 4);
++DEF_IE(111_IE_QUEUE_THRESH, 0x0011, 12);
++DEF_IE(111_IE_BSS_POWER_SAVE, 0x0012, 1);
++/* read only, variable len. I see 240 byte reads: */
++DEF_IE(111_IE_FIRMWARE_STATISTICS, 0x0013, 240); /* 1300F000 00000000 ... */
++/* said to have len=17. looks like fw pads it to 20: */
++DEF_IE(111_IE_INT_CONFIG, 0x0014, 20); /* 14001400 00000000 00000000 00000000 00000000 00000000 */
++DEF_IE(111_IE_FEATURE_CONFIG, 0x0015, 8);
++/* said to be name:KEY_INDICATOR, len:4, but gives INVAL on read: */
++DEF_IE(111_IE_KEY_CHOOSE, 0x0016, -1);
++/* said to have len:4, but in fact returns 8: */
++DEF_IE(111_IE_MAX_USB_XFR, 0x0017, 8); /* 17000800 00014000 00000000 */
++DEF_IE(111_IE_INVAL_18, 0x0018, -1);
++DEF_IE(111_IE_INVAL_19, 0x0019, -1);
++/* undoc but returns something: */
++/* huh, fw indicates len=20 but uses 4 more bytes in buffer??? */
++DEF_IE(111_IE_UNKNOWN_1A, 0x001A, 20); /* 1A001400 AA00AAAA 0000020F FF030000 00020000 00000007 04000000 */
++
++DEF_IE(111_IE_DOT11_INVAL_1000, 0x1000, -1);
++DEF_IE(111_IE_DOT11_STATION_ID, 0x1001, 6);
++DEF_IE(111_IE_DOT11_FRAG_THRESH, 0x1002, 2);
++/* acx100 only? gives INVAL on read: */
++DEF_IE(111_IE_DOT11_BEACON_PERIOD, 0x1003, -1);
++/* said to be MAX_RECV_MSDU_LIFETIME: */
++DEF_IE(111_IE_DOT11_DTIM_PERIOD, 0x1004, 4);
++DEF_IE(111_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1);
++DEF_IE(111_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1);
++/* acx100 only? gives INVAL on read: */
++DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, -1);
++DEF_IE(111_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4);
++/* undoc but returns something. maybe it's 2 multicast MACs to listen to? */
++DEF_IE(111_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* 09100C00 00000000 00000000 00000000 */
++DEF_IE(111_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1);
++DEF_IE(111_IE_DOT11_CURRENT_ANTENNA, 0x100b, 2);
++DEF_IE(111_IE_DOT11_INVAL_100C, 0x100c, -1);
++DEF_IE(111_IE_DOT11_TX_POWER_LEVEL, 0x100d, 1);
++/* said to have len=1 but gives INVAL on read: */
++DEF_IE(111_IE_DOT11_CURRENT_CCA_MODE, 0x100e, -1);
++/* said to have len=4 but gives INVAL on read: */
++DEF_IE(111_IE_DOT11_ED_THRESHOLD, 0x100f, -1);
++/* set default key ID. write only: */
++DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1);
++/* undoc but returns something: */
++DEF_IE(111_IE_DOT11_UNKNOWN_1011, 0x1011, 1); /* 11100100 20 */
++DEF_IE(111_IE_DOT11_INVAL_1012, 0x1012, -1);
++DEF_IE(111_IE_DOT11_INVAL_1013, 0x1013, -1);
++#endif
++
++
++/*============================================================================*
++ * Information Frames Structures *
++ *============================================================================*/
++
++/* Used in beacon frames and the like */
++#define DOT11RATEBYTE_1 (1*2)
++#define DOT11RATEBYTE_2 (2*2)
++#define DOT11RATEBYTE_5_5 (5*2+1)
++#define DOT11RATEBYTE_11 (11*2)
++#define DOT11RATEBYTE_22 (22*2)
++#define DOT11RATEBYTE_6_G (6*2)
++#define DOT11RATEBYTE_9_G (9*2)
++#define DOT11RATEBYTE_12_G (12*2)
++#define DOT11RATEBYTE_18_G (18*2)
++#define DOT11RATEBYTE_24_G (24*2)
++#define DOT11RATEBYTE_36_G (36*2)
++#define DOT11RATEBYTE_48_G (48*2)
++#define DOT11RATEBYTE_54_G (54*2)
++#define DOT11RATEBYTE_BASIC 0x80 /* flags rates included in basic rate set */
++
++
++/***********************************************************************
++** rxbuffer_t
++**
++** This is the format of rx data returned by acx
++*/
++
++/* I've hoped it's a 802.11 PHY header, but no...
++ * so far, I've seen on acx111:
++ * 0000 3a00 0000 0000 IBBS Beacons
++ * 0000 3c00 0000 0000 ESS Beacons
++ * 0000 2700 0000 0000 Probe requests
++ * --vda
++ */
++typedef struct phy_hdr {
++ u8 unknown[4] ACX_PACKED;
++ u8 acx111_unknown[4] ACX_PACKED;
++} phy_hdr_t;
++
++/* seems to be a bit similar to hfa384x_rx_frame.
++ * These fields are still not quite obvious, though.
++ * Some seem to have different meanings... */
++
++#define RXBUF_HDRSIZE 12
++#define PHY_HDR(rxbuf) ((phy_hdr_t*)&rxbuf->hdr_a3)
++#define RXBUF_BYTES_RCVD(rxbuf) (le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff)
++#define RXBUF_BYTES_USED(rxbuf) \
++ ((le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE)
++/*
++mac_cnt_rcvd:
++ 12 bits: length of frame from control field to last byte of FCS
++ 4 bits: reserved
++
++mac_cnt_mblks:
++ 6 bits: number of memory block used to store frame in adapter memory
++ 1 bit: Traffic Indicator bit in TIM of received Beacon was set
++
++mac_status: 1 byte (bitmap):
++ 7 Matching BSSID
++ 6 Matching SSID
++ 5 BDCST Address 1 field is a broadcast
++ 4 VBM received beacon frame has more than one set bit (?!)
++ 3 TIM Set bit representing this station is set in TIM of received beacon
++ 2 GROUP Address 1 is a multicast
++ 1 ADDR1 Address 1 matches our MAC
++ 0 FCSGD FSC is good
++
++phy_stat_baseband: 1 byte (bitmap):
++ 7 Preamble frame had a long preamble
++ 6 PLCP Error CRC16 error in PLCP header
++ 5 Unsup_Mod unsupported modulation
++ 4 Selected Antenna antenna 1 was used to receive this frame
++ 3 PBCC/CCK frame used: 1=PBCC, 0=CCK modulation
++ 2 OFDM frame used OFDM modulation
++ 1 TI Protection protection frame was detected
++ 0 Reserved
++
++phy_plcp_signal: 1 byte:
++ Receive PLCP Signal field from the Baseband Processor
++
++phy_level: 1 byte:
++ receive AGC gain level (can be used to measure receive signal strength)
++
++phy_snr: 1 byte:
++ estimated noise power of equalized receive signal
++ at input of FEC decoder (can be used to measure receive signal quality)
++
++time: 4 bytes:
++ timestamp sampled from either the Access Manager TSF counter
++ or free-running microsecond counter when the MAC receives
++ first byte of PLCP header.
++*/
++
++typedef struct rxbuffer {
++ u16 mac_cnt_rcvd ACX_PACKED; /* only 12 bits are len! (0xfff) */
++ u8 mac_cnt_mblks ACX_PACKED;
++ u8 mac_status ACX_PACKED;
++ u8 phy_stat_baseband ACX_PACKED; /* bit 0x80: used LNA (Low-Noise Amplifier) */
++ u8 phy_plcp_signal ACX_PACKED;
++ u8 phy_level ACX_PACKED; /* PHY stat */
++ u8 phy_snr ACX_PACKED; /* PHY stat */
++ u32 time ACX_PACKED; /* timestamp upon MAC rcv first byte */
++/* 4-byte (acx100) or 8-byte (acx111) phy header will be here
++** if RX_CFG1_INCLUDE_PHY_HDR is in effect:
++** phy_hdr_t phy */
++ wlan_hdr_a3_t hdr_a3 ACX_PACKED;
++ /* maximally sized data part of wlan packet */
++ u8 data_a3[WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN] ACX_PACKED;
++ /* can add hdr/data_a4 if needed */
++} rxbuffer_t;
++
++
++/*--- Firmware statistics ----------------------------------------------------*/
++typedef struct fw_stats {
++ u32 val0x0 ACX_PACKED; /* hdr; */
++ u32 tx_desc_of ACX_PACKED;
++ u32 rx_oom ACX_PACKED;
++ u32 rx_hdr_of ACX_PACKED;
++ u32 rx_hdr_use_next ACX_PACKED;
++ u32 rx_dropped_frame ACX_PACKED;
++ u32 rx_frame_ptr_err ACX_PACKED;
++ u32 rx_xfr_hint_trig ACX_PACKED;
++
++ u32 rx_dma_req ACX_PACKED;
++ u32 rx_dma_err ACX_PACKED;
++ u32 tx_dma_req ACX_PACKED;
++ u32 tx_dma_err ACX_PACKED;
++
++ u32 cmd_cplt ACX_PACKED;
++ u32 fiq ACX_PACKED;
++ u32 rx_hdrs ACX_PACKED;
++ u32 rx_cmplt ACX_PACKED;
++ u32 rx_mem_of ACX_PACKED;
++ u32 rx_rdys ACX_PACKED;
++ u32 irqs ACX_PACKED;
++ u32 acx_trans_procs ACX_PACKED;
++ u32 decrypt_done ACX_PACKED;
++ u32 dma_0_done ACX_PACKED;
++ u32 dma_1_done ACX_PACKED;
++ u32 tx_exch_complet ACX_PACKED;
++ u32 commands ACX_PACKED;
++ u32 acx_rx_procs ACX_PACKED;
++ u32 hw_pm_mode_changes ACX_PACKED;
++ u32 host_acks ACX_PACKED;
++ u32 pci_pm ACX_PACKED;
++ u32 acm_wakeups ACX_PACKED;
++
++ u32 wep_key_count ACX_PACKED;
++ u32 wep_default_key_count ACX_PACKED;
++ u32 dot11_def_key_mib ACX_PACKED;
++ u32 wep_key_not_found ACX_PACKED;
++ u32 wep_decrypt_fail ACX_PACKED;
++} fw_stats_t;
++
++/* Firmware version struct */
++
++typedef struct fw_ver {
++ u16 cmd ACX_PACKED;
++ u16 size ACX_PACKED;
++ char fw_id[20] ACX_PACKED;
++ u32 hw_id ACX_PACKED;
++} fw_ver_t;
++
++#define FW_ID_SIZE 20
++
++
++/*--- WEP stuff --------------------------------------------------------------*/
++#define DOT11_MAX_DEFAULT_WEP_KEYS 4
++
++/* non-firmware struct, no packing necessary */
++typedef struct wep_key {
++ size_t size; /* most often used member first */
++ u8 index;
++ u8 key[29];
++ u16 strange_filler;
++} wep_key_t; /* size = 264 bytes (33*8) */
++/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key
++ * (strange_filler)? */
++
++/* non-firmware struct, no packing necessary */
++typedef struct key_struct {
++ u8 addr[ETH_ALEN]; /* 0x00 */
++ u16 filler1; /* 0x06 */
++ u32 filler2; /* 0x08 */
++ u32 index; /* 0x0c */
++ u16 len; /* 0x10 */
++ u8 key[29]; /* 0x12; is this long enough??? */
++} key_struct_t; /* size = 276. FIXME: where is the remaining space?? */
++
++
++/*--- Client (peer) info -----------------------------------------------------*/
++/* priv->sta_list[] is used for:
++** accumulating and processing of scan results
++** keeping client info in AP mode
++** keeping AP info in STA mode (AP is the only one 'client')
++** keeping peer info in ad-hoc mode
++** non-firmware struct --> no packing necessary */
++enum {
++ CLIENT_EMPTY_SLOT_0 = 0,
++ CLIENT_EXIST_1 = 1,
++ CLIENT_AUTHENTICATED_2 = 2,
++ CLIENT_ASSOCIATED_3 = 3,
++ CLIENT_JOIN_CANDIDATE = 4
++};
++struct client {
++ struct client* next;
++ unsigned long mtime; /* last time we heard it, in jiffies */
++ size_t essid_len; /* length of ESSID (without '\0') */
++ u32 sir; /* Standard IR */
++ u32 snr; /* Signal to Noise Ratio */
++ u16 aid; /* association ID */
++ u16 seq; /* from client's auth req */
++ u16 auth_alg; /* from client's auth req */
++ u16 cap_info; /* from client's assoc req */
++ u16 rate_cap; /* what client supports (all rates) */
++ u16 rate_bas; /* what client supports (basic rates) */
++ u16 rate_cfg; /* what is allowed (by iwconfig etc) */
++ u16 rate_cur; /* currently used rate mask */
++ u8 rate_100; /* currently used rate byte (acx100 only) */
++ u8 used; /* misnamed, more like 'status' */
++ u8 address[ETH_ALEN];
++ u8 bssid[ETH_ALEN]; /* ad-hoc hosts can have bssid != mac */
++ u8 channel;
++ u8 auth_step;
++ u8 ignore_count;
++ u8 fallback_count;
++ u8 stepup_count;
++ char essid[IW_ESSID_MAX_SIZE + 1]; /* ESSID and trailing '\0' */
++/* FIXME: this one is too damn big */
++ char challenge_text[WLAN_CHALLENGE_LEN];
++};
++
++
++/*============================================================================*
++ * Hardware structures *
++ *============================================================================*/
++
++/* An opaque typesafe helper type
++ *
++ * Some hardware fields are actually pointers,
++ * but they have to remain u32, since using ptr instead
++ * (8 bytes on 64bit systems!) would disrupt the fixed descriptor
++ * format the acx firmware expects in the non-user area.
++ * Since we cannot cram an 8 byte ptr into 4 bytes, we need to
++ * enforce that pointed to data remains in low memory
++ * (address value needs to fit in 4 bytes) on 64bit systems.
++ *
++ * This is easy to get wrong, thus we are using a small struct
++ * and special macros to access it. Macros will check for
++ * attempts to overflow an acx_ptr with value > 0xffffffff.
++ *
++ * Attempts to use acx_ptr without macros result in compile-time errors */
++
++typedef struct {
++ u32 v ACX_PACKED;
++} acx_ptr;
++
++#if ACX_DEBUG
++#define CHECK32(n) BUG_ON(sizeof(n)>4 && (long)(n)>0xffffff00)
++#else
++#define CHECK32(n) ((void)0)
++#endif
++
++/* acx_ptr <-> integer conversion */
++#define cpu2acx(n) ({ CHECK32(n); ((acx_ptr){ .v = cpu_to_le32(n) }); })
++#define acx2cpu(a) (le32_to_cpu(a.v))
++
++/* acx_ptr <-> pointer conversion */
++#define ptr2acx(p) ({ CHECK32(p); ((acx_ptr){ .v = cpu_to_le32((u32)(long)(p)) }); })
++#define acx2ptr(a) ((void*)le32_to_cpu(a.v))
++
++/* Values for rate field (acx100 only) */
++#define RATE100_1 10
++#define RATE100_2 20
++#define RATE100_5 55
++#define RATE100_11 110
++#define RATE100_22 220
++/* This bit denotes use of PBCC:
++** (PBCC encoding is usable with 11 and 22 Mbps speeds only) */
++#define RATE100_PBCC511 0x80
++
++/* Bit values for rate111 field */
++#define RATE111_1 0x0001 /* DBPSK */
++#define RATE111_2 0x0002 /* DQPSK */
++#define RATE111_5 0x0004 /* CCK or PBCC */
++#define RATE111_6 0x0008 /* CCK-OFDM or OFDM */
++#define RATE111_9 0x0010 /* CCK-OFDM or OFDM */
++#define RATE111_11 0x0020 /* CCK or PBCC */
++#define RATE111_12 0x0040 /* CCK-OFDM or OFDM */
++#define RATE111_18 0x0080 /* CCK-OFDM or OFDM */
++#define RATE111_22 0x0100 /* PBCC */
++#define RATE111_24 0x0200 /* CCK-OFDM or OFDM */
++#define RATE111_36 0x0400 /* CCK-OFDM or OFDM */
++#define RATE111_48 0x0800 /* CCK-OFDM or OFDM */
++#define RATE111_54 0x1000 /* CCK-OFDM or OFDM */
++#define RATE111_RESERVED 0x2000
++#define RATE111_PBCC511 0x4000 /* PBCC mod at 5.5 or 11Mbit (else CCK) */
++#define RATE111_SHORTPRE 0x8000 /* short preamble */
++/* Special 'try everything' value */
++#define RATE111_ALL 0x1fff
++/* These bits denote acx100 compatible settings */
++#define RATE111_ACX100_COMPAT 0x0127
++/* These bits denote 802.11b compatible settings */
++#define RATE111_80211B_COMPAT 0x0027
++
++/* Descriptor Ctl field bits
++ * init value is 0x8e, "idle" value is 0x82 (in idle tx descs)
++ */
++#define DESC_CTL_SHORT_PREAMBLE 0x01 /* preamble type: 0 = long; 1 = short */
++#define DESC_CTL_FIRSTFRAG 0x02 /* this is the 1st frag of the frame */
++#define DESC_CTL_AUTODMA 0x04
++#define DESC_CTL_RECLAIM 0x08 /* ready to reuse */
++#define DESC_CTL_HOSTDONE 0x20 /* host has finished processing */
++#define DESC_CTL_ACXDONE 0x40 /* acx has finished processing */
++/* host owns the desc [has to be released last, AFTER modifying all other desc fields!] */
++#define DESC_CTL_HOSTOWN 0x80
++
++#define DESC_CTL_INIT (DESC_CTL_HOSTOWN | DESC_CTL_RECLAIM | \
++ DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG)
++#define DESC_CTL_DONE (DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN)
++
++#define DESC_CTL_HOSTOWN_STR "80"
++#define DESC_CTL_DONE_STR "C0"
++/* Descriptor Status field
++ */
++#define DESC_STATUS_FULL (1 << 31)
++
++/* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */
++#define DESC_CTL2_SEQ 0x01 /* don't increase sequence field */
++#define DESC_CTL2_FCS 0x02 /* don't add the FCS */
++#define DESC_CTL2_MORE_FRAG 0x04
++#define DESC_CTL2_RETRY 0x08 /* don't increase retry field */
++#define DESC_CTL2_POWER 0x10 /* don't increase power mgmt. field */
++#define DESC_CTL2_RTS 0x20 /* do RTS/CTS magic before sending */
++#define DESC_CTL2_WEP 0x40 /* encrypt this frame */
++#define DESC_CTL2_DUR 0x80 /* don't increase duration field */
++
++/***************************************************************
++** PCI structures
++*/
++/* IRQ Constants
++** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE) */
++#define HOST_INT_RX_DATA 0x0001
++#define HOST_INT_TX_COMPLETE 0x0002
++#define HOST_INT_TX_XFER 0x0004
++#define HOST_INT_RX_COMPLETE 0x0008
++#define HOST_INT_DTIM 0x0010
++#define HOST_INT_BEACON 0x0020
++#define HOST_INT_TIMER 0x0040
++#define HOST_INT_KEY_NOT_FOUND 0x0080
++#define HOST_INT_IV_ICV_FAILURE 0x0100
++#define HOST_INT_CMD_COMPLETE 0x0200
++#define HOST_INT_INFO 0x0400
++#define HOST_INT_OVERFLOW 0x0800
++#define HOST_INT_PROCESS_ERROR 0x1000
++#define HOST_INT_SCAN_COMPLETE 0x2000
++#define HOST_INT_FCS_THRESHOLD 0x4000
++#define HOST_INT_UNKNOWN 0x8000
++
++/* Outside of "#ifdef PCI" because USB needs to know sizeof()
++** of txdesc and rxdesc: */
++struct txdesc {
++ acx_ptr pNextDesc ACX_PACKED; /* pointer to next txdesc */
++ acx_ptr HostMemPtr ACX_PACKED; /* 0x04 */
++ acx_ptr AcxMemPtr ACX_PACKED; /* 0x08 */
++ u32 tx_time ACX_PACKED; /* 0x0c */
++ u16 total_length ACX_PACKED; /* 0x10 */
++ u16 Reserved ACX_PACKED; /* 0x12 */
++
++/* The following 16 bytes do not change when acx100 owns the descriptor */
++/* BUG: fw clears last byte of this area which is supposedly reserved
++** for driver use. amd64 blew up. We dare not use it now */
++ u32 dummy[4] ACX_PACKED;
++
++ u8 Ctl_8 ACX_PACKED; /* 0x24, 8bit value */
++ u8 Ctl2_8 ACX_PACKED; /* 0x25, 8bit value */
++ u8 error ACX_PACKED; /* 0x26 */
++ u8 ack_failures ACX_PACKED; /* 0x27 */
++ u8 rts_failures ACX_PACKED; /* 0x28 */
++ u8 rts_ok ACX_PACKED; /* 0x29 */
++ union {
++ struct {
++ u8 rate ACX_PACKED; /* 0x2a */
++ u8 queue_ctrl ACX_PACKED; /* 0x2b */
++ } r1 ACX_PACKED;
++ struct {
++ u16 rate111 ACX_PACKED; /* 0x2a */
++ } r2 ACX_PACKED;
++ } u ACX_PACKED;
++ u32 queue_info ACX_PACKED; /* 0x2c (acx100, reserved on acx111) */
++}; /* size : 48 = 0x30 */
++/* NB: acx111 txdesc structure is 4 byte larger */
++/* All these 4 extra bytes are reserved. tx alloc code takes them into account */
++
++struct rxdesc {
++ acx_ptr pNextDesc ACX_PACKED; /* 0x00 */
++ acx_ptr HostMemPtr ACX_PACKED; /* 0x04 */
++ acx_ptr ACXMemPtr ACX_PACKED; /* 0x08 */
++ u32 rx_time ACX_PACKED; /* 0x0c */
++ u16 total_length ACX_PACKED; /* 0x10 */
++ u16 WEP_length ACX_PACKED; /* 0x12 */
++ u32 WEP_ofs ACX_PACKED; /* 0x14 */
++
++/* the following 16 bytes do not change when acx100 owns the descriptor */
++ u8 driverWorkspace[16] ACX_PACKED; /* 0x18 */
++
++ u8 Ctl_8 ACX_PACKED;
++ u8 rate ACX_PACKED;
++ u8 error ACX_PACKED;
++ u8 SNR ACX_PACKED; /* Signal-to-Noise Ratio */
++ u8 RxLevel ACX_PACKED;
++ u8 queue_ctrl ACX_PACKED;
++ u16 unknown ACX_PACKED;
++ u32 unknown2 ACX_PACKED;
++}; /* size 52 = 0x34 */
++
++#ifdef ACX_PCI
++
++/* Register I/O offsets */
++#define ACX100_EEPROM_ID_OFFSET 0x380
++
++/* please add further ACX hardware register definitions only when
++ it turns out you need them in the driver, and please try to use
++ firmware functionality instead, since using direct I/O access instead
++ of letting the firmware do it might confuse the firmware's state
++ machine */
++
++/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION
++** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */
++enum {
++ IO_ACX_SOFT_RESET = 0,
++
++ IO_ACX_HW_SLAVE_REG_ADDR,
++ IO_ACX_HW_SLAVE_REG_DATA,
++ IO_ACX_HW_SLAVE_REG_CTRL,
++
++ IO_ACX_SLV_MEM_ADDR,
++ IO_ACX_SLV_MEM_DATA,
++ IO_ACX_SLV_MEM_CTL,
++ IO_ACX_SLV_END_CTL,
++ IO_ACX_CHIPID,
++
++ IO_ACX_FEMR, /* Function Event Mask */
++
++ IO_ACX_INT_TRIG,
++ IO_ACX_IRQ_MASK,
++ IO_ACX_IRQ_STATUS_NON_DES,
++ IO_ACX_IRQ_STATUS_CLEAR, /* CLEAR = clear on read */
++ IO_ACX_IRQ_ACK,
++ IO_ACX_HINT_TRIG,
++
++ IO_ACX_ENABLE,
++
++ IO_ACX_EEPROM_CTL,
++ IO_ACX_EEPROM_ADDR,
++ IO_ACX_EEPROM_DATA,
++ IO_ACX_EEPROM_CFG,
++
++ IO_ACX_PHY_ADDR,
++ IO_ACX_PHY_DATA,
++ IO_ACX_PHY_CTL,
++
++ IO_ACX_GPIO_OE,
++
++ IO_ACX_GPIO_IN,
++ IO_ACX_GPIO_OUT,
++ IO_ACX_GPIO_PD,
++ IO_ACX_GPIO_CFG,
++
++ IO_ACX_CMD_MAILBOX_OFFS,
++ IO_ACX_INFO_MAILBOX_OFFS,
++ IO_ACX_EEPROM_INFORMATION,
++
++ IO_ACX_EE_START,
++ IO_ACX_SOR_CFG,
++ IO_ACX_ECPU_CTRL,
++
++ IO_ACX_HI_CTRL,
++ IO_ACX_LPWR_MGR,
++
++ IO_ACX_PCI_ARB_CFG,
++};
++/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION
++** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */
++
++/* Values for IO_ACX_INT_TRIG register: */
++/* inform hw that rxdesc in queue needs processing */
++#define INT_TRIG_RXPRC 0x08
++/* inform hw that txdesc in queue needs processing */
++#define INT_TRIG_TXPRC 0x04
++/* ack that we received info from info mailbox */
++#define INT_TRIG_INFOACK 0x02
++/* inform hw that we have filled command mailbox */
++#define INT_TRIG_CMD 0x01
++
++struct txhostdesc {
++ acx_ptr data_phy ACX_PACKED; /* 0x00 [u8 *] */
++ u16 data_offset ACX_PACKED; /* 0x04 */
++ u16 reserved ACX_PACKED; /* 0x06 */
++ u16 Ctl_16 ACX_PACKED; /* 16bit value, endianness!! */
++ u16 length ACX_PACKED; /* 0x0a */
++ acx_ptr desc_phy_next ACX_PACKED; /* 0x0c [txhostdesc *] */
++ acx_ptr pNext ACX_PACKED; /* 0x10 [txhostdesc *] */
++ u32 Status ACX_PACKED; /* 0x14, unused on Tx */
++/* From here on you can use this area as you want (variable length, too!) */
++ u8 *data ACX_PACKED;
++};
++
++struct rxhostdesc {
++ acx_ptr data_phy ACX_PACKED; /* 0x00 [rxbuffer_t *] */
++ u16 data_offset ACX_PACKED; /* 0x04 */
++ u16 reserved ACX_PACKED; /* 0x06 */
++ u16 Ctl_16 ACX_PACKED; /* 0x08; 16bit value, endianness!! */
++ u16 length ACX_PACKED; /* 0x0a */
++ acx_ptr desc_phy_next ACX_PACKED; /* 0x0c [rxhostdesc_t *] */
++ acx_ptr pNext ACX_PACKED; /* 0x10 [rxhostdesc_t *] */
++ u32 Status ACX_PACKED; /* 0x14 */
++/* From here on you can use this area as you want (variable length, too!) */
++ rxbuffer_t *data ACX_PACKED;
++};
++
++#endif /* ACX_PCI */
++
++/***************************************************************
++** USB structures and constants
++*/
++#ifdef ACX_USB
++
++/* Buffer size for fw upload */
++#define ACX100_USB_RWMEM_MAXLEN 2048
++
++/* Should be sent to the ctrlout endpoint */
++#define ACX100_USB_ENBULKIN 6
++
++/* The number of bulk URBs to use */
++#define ACX100_USB_NUM_BULK_URBS 8
++
++/* Should be sent to the bulkout endpoint */
++#define ACX_USB_REQ_UPLOAD_FW 0x10
++#define ACX_USB_REQ_ACK_CS 0x11
++#define ACX_USB_REQ_CMD 0x12
++
++/* Used for usb_txbuffer.desc field */
++#define USB_TXBUF_TXDESC 0xA
++/* Used for usb_txbuffer.hostData field */
++#define USB_TXBUF_HD_ISDATA 0x10000
++#define USB_TXBUF_HD_DIRECTED 0x20000
++#define USB_TXBUF_HD_BROADCAST 0x40000
++/* Size of header (everything up to data[]) */
++#define USB_TXBUF_HDRSIZE 14
++typedef struct usb_txbuffer {
++ u16 desc ACX_PACKED;
++ u16 MPDUlen ACX_PACKED;
++ u8 index ACX_PACKED;
++ u8 txRate ACX_PACKED;
++ u32 hostData ACX_PACKED;
++ u8 ctrl1 ACX_PACKED;
++ u8 ctrl2 ACX_PACKED;
++ u16 dataLength ACX_PACKED;
++ /* wlan packet content is placed here: */
++ u8 data[WLAN_A4FR_MAXLEN_WEP_FCS] ACX_PACKED;
++} usb_txbuffer_t;
++
++typedef struct {
++ void *device;
++ int number;
++} acx_usb_bulk_context_t;
++
++typedef struct usb_tx {
++ unsigned busy:1;
++ struct urb *urb;
++ wlandevice_t *priv;
++ client_t *txc;
++ /* actual USB bulk output data block is here: */
++ usb_txbuffer_t bulkout;
++} usb_tx_t;
++
++#endif /* ACX_USB */
++
++
++/*============================================================================*
++ * Main acx per-device data structure (netdev_priv(dev)) *
++ *============================================================================*/
++#define ACX_STATE_FW_LOADED 0x01
++#define ACX_STATE_IFACE_UP 0x02
++
++/* MAC mode (BSS type) defines
++ * Note that they shouldn't be redefined, since they are also used
++ * during communication with firmware */
++#define ACX_MODE_0_ADHOC 0
++#define ACX_MODE_1_UNUSED 1
++#define ACX_MODE_2_STA 2
++#define ACX_MODE_3_AP 3
++/* These are our own inventions. Sending these to firmware
++** makes it stop emitting beacons, which is exactly what we want
++** for these modes */
++#define ACX_MODE_MONITOR 0xfe
++#define ACX_MODE_OFF 0xff
++/* 'Submode': identifies exact status of ADHOC/STA host */
++#define ACX_STATUS_0_STOPPED 0
++#define ACX_STATUS_1_SCANNING 1
++#define ACX_STATUS_2_WAIT_AUTH 2
++#define ACX_STATUS_3_AUTHENTICATED 3
++#define ACX_STATUS_4_ASSOCIATED 4
++
++/* FIXME: this should be named something like struct acx_priv (typedef'd to
++ * acx_priv_t) */
++
++/* non-firmware struct, no packing necessary */
++struct wlandevice {
++ /*** Device chain ***/
++ struct wlandevice *next; /* link for list of devices */
++
++ /*** Linux network device ***/
++ struct net_device *netdev; /* pointer to linux netdevice */
++ struct net_device *prev_nd; /* FIXME: We should not chain via our
++ * private struct wlandevice _and_
++ * the struct net_device. */
++ /*** Device statistics ***/
++ struct net_device_stats stats; /* net device statistics */
++#ifdef WIRELESS_EXT
++ struct iw_statistics wstats; /* wireless statistics */
++#endif
++ /*** Power managment ***/
++ struct pm_dev *pm; /* PM crap */
++
++ /*** Management timer ***/
++ struct timer_list mgmt_timer;
++
++ /*** Locking ***/
++ struct semaphore sem;
++ spinlock_t lock;
++#if defined(PARANOID_LOCKING) /* Lock debugging */
++ const char *last_sem;
++ const char *last_lock;
++ unsigned long sem_time;
++ unsigned long lock_time;
++#endif
++
++ /*** Hardware identification ***/
++ const char *chip_name;
++ u8 dev_type;
++ u8 chip_type;
++ u8 form_factor;
++ u8 radio_type;
++ u8 eeprom_version;
++
++ /*** Firmware identification ***/
++ char firmware_version[FW_ID_SIZE+1];
++ u32 firmware_numver;
++ u32 firmware_id;
++
++ /*** Device state ***/
++ u16 dev_state_mask;
++ u8 led_power; /* power LED status */
++ u32 get_mask; /* mask of settings to fetch from the card */
++ u32 set_mask; /* mask of settings to write to the card */
++
++ /* Barely used in USB case */
++ u16 irq_status;
++
++ u8 after_interrupt_jobs; /* mini job list for doing actions after an interrupt occurred */
++ WORK_STRUCT after_interrupt_task; /* our task for after interrupt actions */
++
++ /*** scanning ***/
++ u16 scan_count; /* number of times to do channel scan */
++ u8 scan_mode; /* 0 == active, 1 == passive, 2 == background */
++ u8 scan_rate;
++ u16 scan_duration;
++ u16 scan_probe_delay;
++#if WIRELESS_EXT > 15
++ struct iw_spy_data spy_data; /* FIXME: needs to be implemented! */
++#endif
++
++ /*** Wireless network settings ***/
++ /* copy of the device address (ifconfig hw ether) that we actually use
++ ** for 802.11; copied over from the network device's MAC address
++ ** (ifconfig) when it makes sense only */
++ u8 dev_addr[MAX_ADDR_LEN];
++ u8 bssid[ETH_ALEN]; /* the BSSID after having joined */
++ u8 ap[ETH_ALEN]; /* The AP we want, FF:FF:FF:FF:FF:FF is any */
++ u16 aid; /* The Association ID sent from the AP / last used AID if we're an AP */
++ u16 mode; /* mode from iwconfig */
++ u16 status; /* 802.11 association status */
++ u8 essid_active; /* specific ESSID active, or select any? */
++ u8 essid_len; /* to avoid dozens of strlen() */
++ /* INCLUDES \0 termination for easy printf - but many places
++ ** simply want the string data memcpy'd plus a length indicator!
++ ** Keep that in mind... */
++ char essid[IW_ESSID_MAX_SIZE+1];
++ /* essid we are going to use for association, in case of "essid 'any'"
++ ** and in case of hidden ESSID (use configured ESSID then) */
++ char essid_for_assoc[IW_ESSID_MAX_SIZE+1];
++ char nick[IW_ESSID_MAX_SIZE+1]; /* see essid! */
++ u8 channel;
++ u8 reg_dom_id; /* reg domain setting */
++ u16 reg_dom_chanmask;
++ u16 auth_or_assoc_retries;
++ u16 scan_retries;
++ unsigned long scan_start; /* YES, jiffies is defined as "unsigned long" */
++
++ /* stations known to us (if we're an ap) */
++ client_t sta_list[32]; /* tab is larger than list, so that */
++ client_t *sta_hash_tab[64]; /* hash collisions are not likely */
++ client_t *ap_client; /* this one is our AP (STA mode only) */
++
++ unsigned long dup_msg_expiry;
++ int dup_count;
++ int nondup_count;
++ u16 last_seq_ctrl; /* duplicate packet detection */
++
++ /* 802.11 power save mode */
++ u8 ps_wakeup_cfg;
++ u8 ps_listen_interval;
++ u8 ps_options;
++ u8 ps_hangover_period;
++ u16 ps_enhanced_transition_time;
++
++ /*** PHY settings ***/
++ u8 fallback_threshold;
++ u8 stepup_threshold;
++ u16 rate_basic;
++ u16 rate_oper;
++ u16 rate_bcast;
++ u16 rate_bcast100;
++ u8 rate_auto; /* false if "iwconfig rate N" (WITHOUT 'auto'!) */
++ u8 preamble_mode; /* 0 == Long Preamble, 1 == Short, 2 == Auto */
++ u8 preamble_cur;
++
++ u8 tx_disabled;
++ u8 tx_level_dbm;
++ /* u8 tx_level_val; */
++ /* u8 tx_level_auto; whether to do automatic power adjustment */
++
++ unsigned long recalib_time_last_success;
++ unsigned long recalib_time_last_attempt;
++ int recalib_failure_count;
++ int recalib_msg_ratelimit;
++ int retry_errors_msg_ratelimit;
++
++ unsigned long brange_time_last_state_change; /* time the power LED was last changed */
++ u8 brange_last_state; /* last state of the LED */
++ u8 brange_max_quality; /* maximum quality that equates to full speed */
++
++ u8 sensitivity;
++ u8 antenna; /* antenna settings */
++ u8 ed_threshold; /* energy detect threshold */
++ u8 cca; /* clear channel assessment */
++
++ u16 rts_threshold;
++ u32 short_retry;
++ u32 long_retry;
++ u16 msdu_lifetime;
++ u16 listen_interval; /* given in units of beacon interval */
++ u32 beacon_interval;
++
++ u16 capabilities;
++ u8 capab_short;
++ u8 capab_pbcc;
++ u8 capab_agility;
++ u8 rate_supported_len;
++ u8 rate_supported[13];
++
++ /*** Encryption settings (WEP) ***/
++ u32 auth_alg; /* used in transmit_authen1 */
++ u8 wep_enabled;
++ u8 wep_restricted;
++ u8 wep_current_index;
++ wep_key_t wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS]; /* the default WEP keys */
++ key_struct_t wep_key_struct[10];
++
++ /*** Card Rx/Tx management ***/
++ u16 rx_config_1;
++ u16 rx_config_2;
++ u16 memblocksize;
++ u32 tx_free;
++
++ /*** Unknown ***/
++ u8 dtim_interval;
++
++/*** PCI/USB/... must be last or else hw agnostic code breaks horribly ***/
++ /* hack to let common code compile. FIXME */
++ dma_addr_t rxhostdesc_startphy;
++
++ /*** PCI stuff ***/
++#ifdef ACX_PCI
++ /* pointers to tx buffers, tx host descriptors (in host memory)
++ ** and tx descrs in device memory */
++ u8 *txbuf_start;
++ txhostdesc_t *txhostdesc_start;
++ txdesc_t *txdesc_start; /* points to PCI-mapped memory */
++ /* same for rx */
++ rxbuffer_t *rxbuf_start;
++ rxhostdesc_t *rxhostdesc_start;
++ rxdesc_t *rxdesc_start;
++ /* physical addresses of above host memory areas */
++ dma_addr_t rxbuf_startphy;
++ /* dma_addr_t rxhostdesc_startphy; */
++ dma_addr_t txbuf_startphy;
++ dma_addr_t txhostdesc_startphy;
++ /* sizes of above host memory areas */
++ unsigned int txbuf_area_size;
++ unsigned int txhostdesc_area_size;
++ unsigned int rxbuf_area_size;
++ unsigned int rxhostdesc_area_size;
++
++ unsigned int txdesc_size; /* size per tx descr; ACX111 = ACX100 + 4 */
++ unsigned int tx_head; /* current ring buffer pool member index */
++ unsigned int tx_tail;
++ unsigned int rx_tail;
++
++ client_t *txc[TX_CNT];
++
++ u8 need_radio_fw;
++ u8 irqs_active; /* whether irq sending is activated */
++
++ const u16 *io; /* points to ACX100 or ACX111 I/O register address set */
++
++#if defined(CONFIG_ACX_CFI)
++ struct device *dev;
++#else
++ struct pci_dev *pdev;
++#endif
++
++ unsigned long membase;
++ unsigned long membase2;
++ void *iobase;
++ void *iobase2;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
++ /* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced a shorter API version,
++ then it made its way into 2.6.10 */
++ u32 pci_state[16]; /* saved PCI state for suspend/resume */
++#endif
++ u16 irq_mask; /* interrupt types to mask out (not wanted) with many IRQs activated */
++ u16 irq_mask_off; /* interrupt types to mask out (not wanted) with IRQs off */
++ unsigned int irq_loops_this_jiffy;
++ unsigned long irq_last_jiffies;
++
++ /* command interface */
++ void *cmd_area; /* points to PCI mapped memory */
++ void *info_area; /* points to PCI mapped memory */
++ u16 cmd_type;
++ u16 cmd_status;
++ u16 info_type;
++ u16 info_status;
++#endif
++
++ /*** USB stuff ***/
++#ifdef ACX_USB
++ struct usb_device *usbdev;
++
++ rxbuffer_t rxtruncbuf;
++ /* TODO: convert (bulkins,bulkrx_urbs,rxcons) triple into
++ ** struct usb_rx (see struct usb_tx for an example) */
++ rxbuffer_t bulkins[ACX100_USB_NUM_BULK_URBS];
++ struct urb *bulkrx_urbs[ACX100_USB_NUM_BULK_URBS];
++ /* Used by rx urb completion handler in order to find
++ ** corresponding priv/index pair */
++ acx_usb_bulk_context_t rxcons[ACX100_USB_NUM_BULK_URBS];
++ usb_tx_t usb_tx[ACX100_USB_NUM_BULK_URBS];
++
++ int bulkinep; /* bulk-in endpoint */
++ int bulkoutep; /* bulk-out endpoint */
++ int rxtruncsize;
++#endif
++
++};
++
++/* For use with ACX1xx_IE_RXCONFIG */
++/* bit description
++ * 13 include additional header (length etc.) *required*
++ * struct is defined in 'struct rxbuffer'
++ * is this bit acx100 only? does acx111 always put the header,
++ * and bit setting is irrelevant? --vda
++ * 10 receive frames only with SSID used in last join cmd
++ * 9 discard broadcast
++ * 8 receive packets for multicast address 1
++ * 7 receive packets for multicast address 0
++ * 6 discard all multicast packets
++ * 5 discard frames from foreign BSSID
++ * 4 discard frames with foreign destination MAC address
++ * 3 promiscuous mode (receive ALL frames, disable filter)
++ * 2 include FCS
++ * 1 include phy header
++ * 0 ???
++ */
++#define RX_CFG1_INCLUDE_RXBUF_HDR 0x2000 /* ACX100 only */
++#define RX_CFG1_FILTER_SSID 0x0400
++#define RX_CFG1_FILTER_BCAST 0x0200
++#define RX_CFG1_RCV_MC_ADDR1 0x0100
++#define RX_CFG1_RCV_MC_ADDR0 0x0080
++#define RX_CFG1_FILTER_ALL_MULTI 0x0040
++#define RX_CFG1_FILTER_BSSID 0x0020
++#define RX_CFG1_FILTER_MAC 0x0010
++#define RX_CFG1_RCV_PROMISCUOUS 0x0008
++#define RX_CFG1_INCLUDE_FCS 0x0004
++#define RX_CFG1_INCLUDE_PHY_HDR (WANT_PHY_HDR ? 0x0002 : 0)
++/* bit description
++ * 11 receive association requests etc.
++ * 10 receive authentication frames
++ * 9 receive beacon frames
++ * 8 receive contention free packets
++ * 7 receive control frames
++ * 6 receive data frames
++ * 5 receive broken frames
++ * 4 receive management frames
++ * 3 receive probe requests
++ * 2 receive probe responses
++ * 1 receive RTS/CTS/ACK frames
++ * 0 receive other
++ */
++#define RX_CFG2_RCV_ASSOC_REQ 0x0800
++#define RX_CFG2_RCV_AUTH_FRAMES 0x0400
++#define RX_CFG2_RCV_BEACON_FRAMES 0x0200
++#define RX_CFG2_RCV_CONTENTION_FREE 0x0100
++#define RX_CFG2_RCV_CTRL_FRAMES 0x0080
++#define RX_CFG2_RCV_DATA_FRAMES 0x0040
++#define RX_CFG2_RCV_BROKEN_FRAMES 0x0020
++#define RX_CFG2_RCV_MGMT_FRAMES 0x0010
++#define RX_CFG2_RCV_PROBE_REQ 0x0008
++#define RX_CFG2_RCV_PROBE_RESP 0x0004
++#define RX_CFG2_RCV_ACK_FRAMES 0x0002
++#define RX_CFG2_RCV_OTHER 0x0001
++
++/* For use with ACX1xx_IE_FEATURE_CONFIG */
++#define FEATURE1_80MHZ_CLOCK 0x00000040L
++#define FEATURE1_4X 0x00000020L
++#define FEATURE1_LOW_RX 0x00000008L
++#define FEATURE1_EXTRA_LOW_RX 0x00000001L
++
++#define FEATURE2_SNIFFER 0x00000080L
++#define FEATURE2_NO_TXCRYPT 0x00000001L
++
++/*-- get and set mask values --*/
++#define GETSET_LED_POWER 0x00000001L
++#define GETSET_STATION_ID 0x00000002L
++#define SET_TEMPLATES 0x00000004L
++#define SET_STA_LIST 0x00000008L
++#define GETSET_TX 0x00000010L
++#define GETSET_RX 0x00000020L
++#define SET_RXCONFIG 0x00000040L
++#define GETSET_ANTENNA 0x00000080L
++#define GETSET_SENSITIVITY 0x00000100L
++#define GETSET_TXPOWER 0x00000200L
++#define GETSET_ED_THRESH 0x00000400L
++#define GETSET_CCA 0x00000800L
++#define GETSET_POWER_80211 0x00001000L
++#define GETSET_RETRY 0x00002000L
++#define GETSET_REG_DOMAIN 0x00004000L
++#define GETSET_CHANNEL 0x00008000L
++/* Used when ESSID changes etc and we need to scan for AP anew */
++#define GETSET_RESCAN 0x00010000L
++#define GETSET_MODE 0x00020000L
++#define GETSET_WEP 0x00040000L
++#define SET_WEP_OPTIONS 0x00080000L
++#define SET_MSDU_LIFETIME 0x00100000L
++#define SET_RATE_FALLBACK 0x00200000L
++#define GETSET_ALL 0x80000000L
++
++
++/*============================================================================*
++ * Firmware loading *
++ *============================================================================*/
++/* Doh, 2.4.x also has CONFIG_FW_LOADER_MODULE
++ * (but doesn't have the new device model yet which we require!)
++ * FIXME: exact version that introduced new device handling? */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
++#define USE_FW_LOADER_26 1
++#define USE_FW_LOADER_LEGACY 0
++#else
++#define USE_FW_LOADER_26 0
++#define USE_FW_LOADER_LEGACY 1
++#endif
++#endif
++
++#if USE_FW_LOADER_26
++#include <linux/firmware.h> /* request_firmware() */
++#include <linux/pci.h> /* struct pci_device */
++#endif
++
++
++/***********************************************************************
++*/
++typedef struct acx100_ie_memblocksize {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u16 size ACX_PACKED;
++} acx100_ie_memblocksize_t;
++
++typedef struct acx100_ie_queueconfig {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u32 AreaSize ACX_PACKED;
++ u32 RxQueueStart ACX_PACKED;
++ u8 QueueOptions ACX_PACKED;
++ u8 NumTxQueues ACX_PACKED;
++ u8 NumRxDesc ACX_PACKED; /* for USB only */
++ u8 pad1 ACX_PACKED;
++ u32 QueueEnd ACX_PACKED;
++ u32 HostQueueEnd ACX_PACKED; /* QueueEnd2 */
++ u32 TxQueueStart ACX_PACKED;
++ u8 TxQueuePri ACX_PACKED;
++ u8 NumTxDesc ACX_PACKED;
++ u16 pad2 ACX_PACKED;
++} acx100_ie_queueconfig_t;
++
++typedef struct acx111_ie_queueconfig {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u32 tx_memory_block_address ACX_PACKED;
++ u32 rx_memory_block_address ACX_PACKED;
++ u32 rx1_queue_address ACX_PACKED;
++ u32 reserved1 ACX_PACKED;
++ u32 tx1_queue_address ACX_PACKED;
++ u8 tx1_attributes ACX_PACKED;
++ u16 reserved2 ACX_PACKED;
++ u8 reserved3 ACX_PACKED;
++} acx111_ie_queueconfig_t;
++
++typedef struct acx100_ie_memconfigoption {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u32 DMA_config ACX_PACKED;
++ acx_ptr pRxHostDesc ACX_PACKED;
++ u32 rx_mem ACX_PACKED;
++ u32 tx_mem ACX_PACKED;
++ u16 RxBlockNum ACX_PACKED;
++ u16 TxBlockNum ACX_PACKED;
++} acx100_ie_memconfigoption_t;
++
++typedef struct acx111_ie_memoryconfig {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u16 no_of_stations ACX_PACKED;
++ u16 memory_block_size ACX_PACKED;
++ u8 tx_rx_memory_block_allocation ACX_PACKED;
++ u8 count_rx_queues ACX_PACKED;
++ u8 count_tx_queues ACX_PACKED;
++ u8 options ACX_PACKED;
++ u8 fragmentation ACX_PACKED;
++ u16 reserved1 ACX_PACKED;
++ u8 reserved2 ACX_PACKED;
++
++ /* start of rx1 block */
++ u8 rx_queue1_count_descs ACX_PACKED;
++ u8 rx_queue1_reserved1 ACX_PACKED;
++ u8 rx_queue1_type ACX_PACKED; /* must be set to 7 */
++ u8 rx_queue1_prio ACX_PACKED; /* must be set to 0 */
++ acx_ptr rx_queue1_host_rx_start ACX_PACKED;
++ /* end of rx1 block */
++
++ /* start of tx1 block */
++ u8 tx_queue1_count_descs ACX_PACKED;
++ u8 tx_queue1_reserved1 ACX_PACKED;
++ u8 tx_queue1_reserved2 ACX_PACKED;
++ u8 tx_queue1_attributes ACX_PACKED;
++ /* end of tx1 block */
++} acx111_ie_memoryconfig_t;
++
++typedef struct acx_ie_memmap {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u32 CodeStart ACX_PACKED;
++ u32 CodeEnd ACX_PACKED;
++ u32 WEPCacheStart ACX_PACKED;
++ u32 WEPCacheEnd ACX_PACKED;
++ u32 PacketTemplateStart ACX_PACKED;
++ u32 PacketTemplateEnd ACX_PACKED;
++ u32 QueueStart ACX_PACKED;
++ u32 QueueEnd ACX_PACKED;
++ u32 PoolStart ACX_PACKED;
++ u32 PoolEnd ACX_PACKED;
++} acx_ie_memmap_t;
++
++typedef struct acx111_ie_feature_config {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u32 feature_options ACX_PACKED;
++ u32 data_flow_options ACX_PACKED;
++} acx111_ie_feature_config_t;
++
++typedef struct acx111_ie_tx_level {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u8 level ACX_PACKED;
++} acx111_ie_tx_level_t;
++
++#define PS_CFG_ENABLE 0x80
++#define PS_CFG_PENDING 0x40 /* status flag when entering PS */
++#define PS_CFG_WAKEUP_MODE_MASK 0x07
++#define PS_CFG_WAKEUP_BY_HOST 0x03
++#define PS_CFG_WAKEUP_EACH_ITVL 0x02
++#define PS_CFG_WAKEUP_ON_DTIM 0x01
++#define PS_CFG_WAKEUP_ALL_BEAC 0x00
++
++/* Enhanced PS mode: sleep until Rx Beacon w/ the STA's AID bit set
++** in the TIM; newer firmwares only(?) */
++#define PS_OPT_ENA_ENHANCED_PS 0x04
++#define PS_OPT_STILL_RCV_BCASTS 0x01
++
++typedef struct acx100_ie_powermgmt {
++ u32 type ACX_PACKED;
++ u32 len ACX_PACKED;
++ u8 wakeup_cfg ACX_PACKED;
++ u8 listen_interval ACX_PACKED; /* for EACH_ITVL: wake up every "beacon units" interval */
++ u8 options ACX_PACKED;
++ u8 hangover_period ACX_PACKED; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */
++ u16 enhanced_ps_transition_time ACX_PACKED; /* rem. wake time for Enh. PS */
++} acx100_ie_powermgmt_t;
++
++typedef struct acx111_ie_powermgmt {
++ u32 type ACX_PACKED;
++ u32 len ACX_PACKED;
++ u8 wakeup_cfg ACX_PACKED;
++ u8 listen_interval ACX_PACKED; /* for EACH_ITVL: wake up every "beacon units" interval */
++ u8 options ACX_PACKED;
++ u8 hangover_period ACX_PACKED; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */
++ u32 beaconRxTime ACX_PACKED;
++ u32 enhanced_ps_transition_time ACX_PACKED; /* rem. wake time for Enh. PS */
++} acx111_ie_powermgmt_t;
++
++
++/***********************************************************************
++** Commands and template structures
++*/
++
++/*
++** SCAN command structure
++**
++** even though acx100 scan rates match RATE100 constants,
++** acx111 ones do not match! Therefore we do not use RATE100 #defines */
++#define ACX_SCAN_RATE_1 10
++#define ACX_SCAN_RATE_2 20
++#define ACX_SCAN_RATE_5 55
++#define ACX_SCAN_RATE_11 110
++#define ACX_SCAN_RATE_22 220
++#define ACX_SCAN_OPT_ACTIVE 0x00 /* a bit mask */
++#define ACX_SCAN_OPT_PASSIVE 0x01
++/* Background scan: we go into Power Save mode (by transmitting
++** NULL data frame to AP with the power mgmt bit set), do the scan,
++** and then exit Power Save mode. A plus is that AP buffers frames
++** for us while we do background scan. Thus we avoid frame losses.
++** Background scan can be active or passive, just like normal one */
++#define ACX_SCAN_OPT_BACKGROUND 0x02
++typedef struct acx100_scan {
++ u16 count ACX_PACKED; /* number of scans to do, 0xffff == continuous */
++ u16 start_chan ACX_PACKED;
++ u16 flags ACX_PACKED; /* channel list mask; 0x8000 == all channels? */
++ u8 max_rate ACX_PACKED; /* max. probe rate */
++ u8 options ACX_PACKED; /* bit mask, see defines above */
++ u16 chan_duration ACX_PACKED;
++ u16 max_probe_delay ACX_PACKED;
++} acx100_scan_t; /* length 0xc */
++
++#define ACX111_SCAN_RATE_6 0x0B
++#define ACX111_SCAN_RATE_9 0x0F
++#define ACX111_SCAN_RATE_12 0x0A
++#define ACX111_SCAN_RATE_18 0x0E
++#define ACX111_SCAN_RATE_24 0x09
++#define ACX111_SCAN_RATE_36 0x0D
++#define ACX111_SCAN_RATE_48 0x08
++#define ACX111_SCAN_RATE_54 0x0C
++#define ACX111_SCAN_OPT_5GHZ 0x04 /* else 2.4GHZ */
++#define ACX111_SCAN_MOD_SHORTPRE 0x01 /* you can combine SHORTPRE and PBCC */
++#define ACX111_SCAN_MOD_PBCC 0x80
++#define ACX111_SCAN_MOD_OFDM 0x40
++typedef struct acx111_scan {
++ u16 count ACX_PACKED; /* number of scans to do */
++ u8 channel_list_select ACX_PACKED; /* 0: scan all channels, 1: from chan_list only */
++ u16 reserved1 ACX_PACKED;
++ u8 reserved2 ACX_PACKED;
++ u8 rate ACX_PACKED; /* rate for probe requests (if active scan) */
++ u8 options ACX_PACKED; /* bit mask, see defines above */
++ u16 chan_duration ACX_PACKED; /* min time to wait for reply on one channel (in TU) */
++ /* (active scan only) (802.11 section 11.1.3.2.2) */
++ u16 max_probe_delay ACX_PACKED; /* max time to wait for reply on one channel (active scan) */
++ /* time to listen on a channel (passive scan) */
++ u8 modulation ACX_PACKED;
++ u8 channel_list[26] ACX_PACKED; /* bits 7:0 first byte: channels 8:1 */
++ /* bits 7:0 second byte: channels 16:9 */
++ /* 26 bytes is enough to cover 802.11a */
++} acx111_scan_t;
++
++
++/*
++** Radio calibration command structure
++*/
++typedef struct acx111_cmd_radiocalib {
++/* 0x80000000 == automatic calibration by firmware, according to interval;
++ * bits 0..3: select calibration methods to go through:
++ * calib based on DC, AfeDC, Tx mismatch, Tx equilization */
++ u32 methods ACX_PACKED;
++ u32 interval ACX_PACKED;
++} acx111_cmd_radiocalib_t;
++
++
++/*
++** Packet template structures
++**
++** Packet templates store contents of Beacon, Probe response, Probe request,
++** Null data frame, and TIM data frame. Firmware automatically transmits
++** contents of template at appropriate time:
++** - Beacon: when configured as AP or Ad-hoc
++** - Probe response: when configured as AP or Ad-hoc, whenever
++** a Probe request frame is received
++** - Probe request: when host issues SCAN command (active)
++** - Null data frame: when entering 802.11 power save mode
++** - TIM data: at the end of Beacon frames (if no TIM template
++** is configured, then transmits default TIM)
++** NB:
++** - size field must be set to size of actual template
++** (NOT sizeof(struct) - templates are variable in length),
++** size field is not itself counted.
++** - members flagged with an asterisk must be initialized with host,
++** rest must be zero filled.
++** - variable length fields shown only in comments */
++typedef struct acx_template_tim {
++ u16 size ACX_PACKED;
++ u8 tim_eid ACX_PACKED; /* 00 1 TIM IE ID * */
++ u8 len ACX_PACKED; /* 01 1 Length * */
++ u8 dtim_cnt ACX_PACKED; /* 02 1 DTIM Count */
++ u8 dtim_period ACX_PACKED; /* 03 1 DTIM Period */
++ u8 bitmap_ctrl ACX_PACKED; /* 04 1 Bitmap Control * (except bit0) */
++ /* 05 n Partial Virtual Bitmap * */
++ u8 variable[0x100 - 1-1-1-1-1] ACX_PACKED;
++} acx_template_tim_t;
++
++typedef struct acx100_template_probereq {
++ u16 size ACX_PACKED;
++ u16 fc ACX_PACKED; /* 00 2 fc */
++ u16 dur ACX_PACKED; /* 02 2 Duration */
++ u8 da[6] ACX_PACKED; /* 04 6 Destination Address * */
++ u8 sa[6] ACX_PACKED; /* 0A 6 Source Address * */
++ u8 bssid[6] ACX_PACKED; /* 10 6 BSSID * */
++ u16 seq ACX_PACKED; /* 16 2 Sequence Control */
++ u8 timestamp[8] ACX_PACKED;/* 18 8 Timestamp */
++ u16 beacon_interval ACX_PACKED; /* 20 2 Beacon Interval * */
++ u16 cap ACX_PACKED; /* 22 2 Capability Information * */
++ /* 24 n SSID * */
++ /* nn n Supported Rates * */
++ u8 variable[0x44 - 2-2-6-6-6-2-8-2-2] ACX_PACKED;
++} acx100_template_probereq_t;
++
++typedef struct acx111_template_probereq {
++ u16 size ACX_PACKED;
++ u16 fc ACX_PACKED; /* 00 2 fc * */
++ u16 dur ACX_PACKED; /* 02 2 Duration */
++ u8 da[6] ACX_PACKED; /* 04 6 Destination Address * */
++ u8 sa[6] ACX_PACKED; /* 0A 6 Source Address * */
++ u8 bssid[6] ACX_PACKED; /* 10 6 BSSID * */
++ u16 seq ACX_PACKED; /* 16 2 Sequence Control */
++ /* 18 n SSID * */
++ /* nn n Supported Rates * */
++ u8 variable[0x44 - 2-2-6-6-6-2] ACX_PACKED;
++} acx111_template_probereq_t;
++
++typedef struct acx_template_proberesp {
++ u16 size ACX_PACKED;
++ u16 fc ACX_PACKED; /* 00 2 fc * (bits [15:12] and [10:8] per 802.11 section 7.1.3.1) */
++ u16 dur ACX_PACKED; /* 02 2 Duration */
++ u8 da[6] ACX_PACKED; /* 04 6 Destination Address */
++ u8 sa[6] ACX_PACKED; /* 0A 6 Source Address */
++ u8 bssid[6] ACX_PACKED; /* 10 6 BSSID */
++ u16 seq ACX_PACKED; /* 16 2 Sequence Control */
++ u8 timestamp[8] ACX_PACKED;/* 18 8 Timestamp */
++ u16 beacon_interval ACX_PACKED; /* 20 2 Beacon Interval * */
++ u16 cap ACX_PACKED; /* 22 2 Capability Information * */
++ /* 24 n SSID * */
++ /* nn n Supported Rates * */
++ /* nn 1 DS Parameter Set * */
++ u8 variable[0x54 - 2-2-6-6-6-2-8-2-2] ACX_PACKED;
++} acx_template_proberesp_t;
++#define acx_template_beacon_t acx_template_proberesp_t
++#define acx_template_beacon acx_template_proberesp
++
++typedef struct acx_template_nullframe {
++ u16 size ACX_PACKED;
++ struct wlan_hdr_a3 hdr ACX_PACKED;
++} acx_template_nullframe_t;
++
++
++/*
++** JOIN command structure
++**
++** as opposed to acx100, acx111 dtim interval is AFTER rates_basic111.
++** NOTE: took me about an hour to get !@#$%^& packing right --> struct packing is eeeeevil... */
++typedef struct acx_joinbss {
++ u8 bssid[ETH_ALEN] ACX_PACKED;
++ u16 beacon_interval ACX_PACKED;
++ union {
++ struct {
++ u8 dtim_interval ACX_PACKED;
++ u8 rates_basic ACX_PACKED;
++ u8 rates_supported ACX_PACKED;
++ } acx100 ACX_PACKED;
++ struct {
++ u16 rates_basic ACX_PACKED;
++ u8 dtim_interval ACX_PACKED;
++ } acx111 ACX_PACKED;
++ } u ACX_PACKED;
++ u8 genfrm_txrate ACX_PACKED; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */
++ u8 genfrm_mod_pre ACX_PACKED; /* generated frame modulation/preamble:
++ ** bit7: PBCC, bit6: OFDM (else CCK/DQPSK/DBPSK)
++ ** bit5: short pre */
++ u8 macmode ACX_PACKED; /* BSS Type, must be one of ACX_MODE_xxx */
++ u8 channel ACX_PACKED;
++ u8 essid_len ACX_PACKED;
++ char essid[IW_ESSID_MAX_SIZE] ACX_PACKED;
++} acx_joinbss_t;
++
++#define JOINBSS_RATES_1 0x01
++#define JOINBSS_RATES_2 0x02
++#define JOINBSS_RATES_5 0x04
++#define JOINBSS_RATES_11 0x08
++#define JOINBSS_RATES_22 0x10
++
++/* Looks like missing bits are used to indicate 11g rates!
++** (it follows from the fact that constants below match 1:1 to RATE111_nn)
++** This was actually seen! Look at that Assoc Request sent by acx111,
++** it _does_ contain 11g rates in basic set:
++01:30:20.070772 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1
++01:30:20.074425 Authentication (Open System)-1: Succesful
++01:30:20.076539 Authentication (Open System)-2:
++01:30:20.076620 Acknowledgment
++01:30:20.088546 Assoc Request (xxx) [1.0* 2.0* 5.5* 6.0* 9.0* 11.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit]
++01:30:20.122413 Assoc Response AID(1) :: Succesful
++01:30:20.122679 Acknowledgment
++01:30:20.173204 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1
++*/
++#define JOINBSS_RATES_BASIC111_1 0x0001
++#define JOINBSS_RATES_BASIC111_2 0x0002
++#define JOINBSS_RATES_BASIC111_5 0x0004
++#define JOINBSS_RATES_BASIC111_11 0x0020
++#define JOINBSS_RATES_BASIC111_22 0x0100
++
++
++/***********************************************************************
++*/
++typedef struct mem_read_write {
++ u16 addr ACX_PACKED;
++ u16 type ACX_PACKED; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg. */
++ u32 len ACX_PACKED;
++ u32 data ACX_PACKED;
++} mem_read_write_t;
++
++typedef struct firmware_image {
++ u32 chksum ACX_PACKED;
++ u32 size ACX_PACKED;
++ u8 data[1] ACX_PACKED; /* the byte array of the actual firmware... */
++} firmware_image_t;
++
++typedef struct acx_cmd_radioinit {
++ u32 offset ACX_PACKED;
++ u32 len ACX_PACKED;
++} acx_cmd_radioinit_t;
++
++typedef struct acx100_ie_wep_options {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u16 NumKeys ACX_PACKED; /* max # of keys */
++ u8 WEPOption ACX_PACKED; /* 0 == decrypt default key only, 1 == override decrypt */
++ u8 Pad ACX_PACKED; /* used only for acx111 */
++} acx100_ie_wep_options_t;
++
++typedef struct ie_dot11WEPDefaultKey {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u8 action ACX_PACKED;
++ u8 keySize ACX_PACKED;
++ u8 defaultKeyNum ACX_PACKED;
++ u8 key[29] ACX_PACKED; /* check this! was Key[19]. */
++} ie_dot11WEPDefaultKey_t;
++
++typedef struct acx111WEPDefaultKey {
++ u8 MacAddr[ETH_ALEN] ACX_PACKED;
++ u16 action ACX_PACKED; /* NOTE: this is a u16, NOT a u8!! */
++ u16 reserved ACX_PACKED;
++ u8 keySize ACX_PACKED;
++ u8 type ACX_PACKED;
++ u8 index ACX_PACKED;
++ u8 defaultKeyNum ACX_PACKED;
++ u8 counter[6] ACX_PACKED;
++ u8 key[32] ACX_PACKED; /* up to 32 bytes (for TKIP!) */
++} acx111WEPDefaultKey_t;
++
++typedef struct ie_dot11WEPDefaultKeyID {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u8 KeyID ACX_PACKED;
++} ie_dot11WEPDefaultKeyID_t;
++
++typedef struct acx100_cmd_wep_mgmt {
++ u8 MacAddr[ETH_ALEN] ACX_PACKED;
++ u16 Action ACX_PACKED;
++ u16 KeySize ACX_PACKED;
++ u8 Key[29] ACX_PACKED; /* 29*8 == 232bits == WEP256 */
++} acx100_cmd_wep_mgmt_t;
++
++typedef struct defaultkey {
++ u8 num;
++} defaultkey_t;
++
++typedef struct acx_ie_generic {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ union {
++ /* struct wep wp ACX_PACKED; */
++ /* Association ID IE: just a 16bit value: */
++ u16 aid;
++ /* UNUSED? struct defaultkey dkey ACX_PACKED; */
++ /* generic member for quick implementation of commands */
++ u8 bytes[32] ACX_PACKED;
++ } m ACX_PACKED;
++} acx_ie_generic_t;
++
++/* Config Option structs */
++
++typedef struct co_antennas {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ u8 list[2] ACX_PACKED;
++} co_antennas_t;
++
++typedef struct co_powerlevels {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ u16 list[8] ACX_PACKED;
++} co_powerlevels_t;
++
++typedef struct co_datarates {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ u8 list[8] ACX_PACKED;
++} co_datarates_t;
++
++typedef struct co_domains {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ u8 list[6] ACX_PACKED;
++} co_domains_t;
++
++typedef struct co_product_id {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ u8 list[128] ACX_PACKED;
++} co_product_id_t;
++
++typedef struct co_manuf_id {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ u8 list[128] ACX_PACKED;
++} co_manuf_t;
++
++typedef struct co_fixed {
++ u8 type ACX_PACKED;
++ u8 len ACX_PACKED;
++ char NVSv[8] ACX_PACKED;
++ u8 MAC[6] ACX_PACKED;
++ u16 probe_delay ACX_PACKED;
++ u32 eof_memory ACX_PACKED;
++ u8 dot11CCAModes ACX_PACKED;
++ u8 dot11Diversity ACX_PACKED;
++ u8 dot11ShortPreambleOption ACX_PACKED;
++ u8 dot11PBCCOption ACX_PACKED;
++ u8 dot11ChannelAgility ACX_PACKED;
++ u8 dot11PhyType ACX_PACKED;
++/* u8 dot11TempType ACX_PACKED;
++ u8 num_var ACX_PACKED; seems to be erased */
++} co_fixed_t;
++
++
++typedef struct acx111_ie_configoption {
++ co_fixed_t configoption_fixed ACX_PACKED;
++ co_antennas_t antennas ACX_PACKED;
++ co_powerlevels_t power_levels ACX_PACKED;
++ co_datarates_t data_rates ACX_PACKED;
++ co_domains_t domains ACX_PACKED;
++ co_product_id_t product_id ACX_PACKED;
++ co_manuf_t manufacturer ACX_PACKED;
++} acx111_ie_configoption_t;
++
++
++/***********************************************************************
++*/
++#define CHECK_SIZEOF(type,size) { \
++ extern void BUG_bad_size_for_##type(void); \
++ if (sizeof(type)!=(size)) BUG_bad_size_for_##type(); \
++}
++
++static inline void
++acx_struct_size_check(void)
++{
++ //CHECK_SIZEOF(txdesc_t, 0x30);
++ CHECK_SIZEOF(acx100_ie_memconfigoption_t, 24);
++ CHECK_SIZEOF(acx100_ie_queueconfig_t, 0x20);
++ //CHECK_SIZEOF(acx_joinbss_t, 0x30);
++}
++
++
++/*============================================================================*
++ * Global data *
++ *============================================================================*/
++extern const u8 bitpos2ratebyte[];
++extern const u8 bitpos2rate100[];
++
++extern const u8 reg_domain_ids[];
++extern const u8 reg_domain_ids_len;
++
++extern const struct iw_handler_def acx_ioctl_handler_def;
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/cfi.c bt_kernel/drivers/net/wireless/tiacx/cfi.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/cfi.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/cfi.c 2005-10-29 22:02:44.690471000 +0300
+@@ -0,0 +1,4779 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++#define ACX_PCI 1
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
++#include <linux/moduleparam.h>
++#endif
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/if_arp.h>
++#include <linux/rtnetlink.h>
++#include <linux/wireless.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif
++#include <linux/device.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/ioport.h>
++#include <linux/pm.h>
++#include <linux/vmalloc.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/sizes.h>
++#include <asm/arch/tc.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/arch/gpio.h>
++#include <asm/arch/pca9535.h>
++
++#include "acx.h"
++
++#define CARDNAME "tnetw1100b"
++
++/*================================================================*/
++/* Local Constants */
++#define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE)
++#define PCI_ACX100_REGION1 0x01
++#define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */
++#define PCI_ACX100_REGION2 0x02
++#define PCI_ACX100_REGION2_SIZE 0x10000 /* Memory size - 64K bytes */
++
++#define PCI_ACX111_REGION1 0x00
++#define PCI_ACX111_REGION1_SIZE 0x2000 /* Memory size - 8K bytes */
++#define PCI_ACX111_REGION2 0x01
++#define PCI_ACX111_REGION2_SIZE 0x20000 /* Memory size - 128K bytes */
++
++/* Texas Instruments Vendor ID */
++#define PCI_VENDOR_ID_TI 0x104c
++
++/* ACX100 22Mb/s WLAN controller */
++#define PCI_DEVICE_ID_TI_TNETW1100A 0x8400
++#define PCI_DEVICE_ID_TI_TNETW1100B 0x8401
++
++/* ACX111 54Mb/s WLAN controller */
++#define PCI_DEVICE_ID_TI_TNETW1130 0x9066
++
++/* PCI Class & Sub-Class code, Network-'Other controller' */
++#define PCI_CLASS_NETWORK_OTHERS 0x280
++
++#define CARD_EEPROM_ID_SIZE 6
++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */
++
++
++/***********************************************************************
++*/
++static void acx_l_disable_irq(wlandevice_t *priv);
++static void acx_l_enable_irq(wlandevice_t *priv);
++static int acx_drv_probe(struct device *dev);
++static int acx_remove(struct device *dev);
++
++static inline void test(wlandevice_t *priv);
++
++#ifdef CONFIG_PM
++static int acx_suspend(struct device *dev, pm_message_t state, u32 level);
++static int acx_resume(struct device *dev, u32 level);
++#endif
++
++static void acx_i_tx_timeout(netdevice_t *dev);
++static struct net_device_stats *acx_e_get_stats(netdevice_t *dev);
++static struct iw_statistics *acx_e_get_wireless_stats(netdevice_t *dev);
++
++static irqreturn_t acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
++static void acx_i_set_multicast_list(netdevice_t *dev);
++
++static int acx_e_open(netdevice_t *dev);
++static int acx_e_close(netdevice_t *dev);
++static void acx_s_up(netdevice_t *dev);
++static void acx_s_down(netdevice_t *dev);
++
++
++/***********************************************************************
++** Register access
++*/
++
++/* Pick one */
++/* #define INLINE_IO static */
++#define INLINE_IO static inline
++
++INLINE_IO u32
++acx_read_reg32(wlandevice_t *priv, unsigned int offset)
++{
++#if ACX_IO_WIDTH == 32
++ return readl((u8 *)priv->iobase + priv->io[offset]);
++#else
++ return readw((u8 *)priv->iobase + priv->io[offset])
++ + (readw((u8 *)priv->iobase + priv->io[offset] + 2) << 16);
++#endif
++}
++
++INLINE_IO u16
++acx_read_reg16(wlandevice_t *priv, unsigned int offset)
++{
++ return readw((u8 *)priv->iobase + priv->io[offset]);
++}
++
++INLINE_IO u8
++acx_read_reg8(wlandevice_t *priv, unsigned int offset)
++{
++ return readb((u8 *)priv->iobase + priv->io[offset]);
++}
++
++INLINE_IO void
++acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val)
++{
++#if ACX_IO_WIDTH == 32
++ writel(val, (u8 *)priv->iobase + priv->io[offset]);
++#else
++ writew(val & 0xffff, (u8 *)priv->iobase + priv->io[offset]);
++ writew(val >> 16, (u8 *)priv->iobase + priv->io[offset] + 2);
++#endif
++}
++
++INLINE_IO void
++acx_write_reg16(wlandevice_t *priv, unsigned int offset, u16 val)
++{
++ writew(val, (u8 *)priv->iobase + priv->io[offset]);
++}
++
++INLINE_IO void
++acx_write_reg8(wlandevice_t *priv, unsigned int offset, u8 val)
++{
++ writeb(val, (u8 *)priv->iobase + priv->io[offset]);
++}
++
++/* Handle PCI posting properly:
++ * Make sure that writes reach the adapter in case they require to be executed
++ * *before* the next write, by reading a random (and safely accessible) register.
++ * This call has to be made if there is no read following (which would flush the data
++ * to the adapter), yet the written data has to reach the adapter immediately. */
++INLINE_IO void
++acx_write_flush(wlandevice_t *priv)
++{
++ /* readb(priv->iobase + priv->io[IO_ACX_INFO_MAILBOX_OFFS]); */
++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should also be safe): */
++ readb(priv->iobase);
++}
++
++
++INLINE_IO void acx_mailbox_fill(wlandevice_t *priv, unsigned int offset, unsigned char val, int len){
++ unsigned int addr = offset;
++ unsigned int buff = 0;
++
++ // Fill the buffer
++ memset((void *) &buff, val, len);
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x00010000);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, addr);
++
++ while(len >= 4){
++ // Write data
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA,buff);
++ acx_write_flush(priv);
++
++ addr += 4;
++ len -= 4;
++ }
++
++ if(len > 0) {
++ val = 0;
++ memset((void *) &buff, val , len);
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA,buff);
++ acx_write_flush(priv);
++ }
++}
++
++
++INLINE_IO void acx_mailbox_write(wlandevice_t *priv, unsigned int offset, void *cmd, int len){
++ unsigned char *buff = (unsigned char *) cmd;
++ unsigned int addr = offset;
++ unsigned int val = 0;
++
++ if(!cmd){
++ return;
++ }
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x00010000);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, addr);
++
++ while(len >= 4){
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA,*((unsigned int *) buff));
++ acx_write_flush(priv);
++
++ buff += 4;
++ addr += 4;
++ len -= 4;
++ }
++
++ if(len > 0) {
++ val = 0;
++ memcpy((void *) &val, (void *) buff, len);
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA,val);
++ acx_write_flush(priv);
++ }
++}
++
++INLINE_IO void acx_mailbox_read(wlandevice_t *priv, unsigned int offset, void *data, int len){
++ unsigned char *buff = (unsigned char *) data;
++ unsigned int addr = offset;
++ unsigned int value = 0;
++
++ if(!data){
++ return;
++ }
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x00010000);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, addr);
++
++ while(len >= 4){
++ *buff = 0;
++ *((unsigned int *) buff) = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ buff += 4;
++ addr += 4;
++ len -= 4;
++ }
++
++ if(len > 0){
++ value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++ memcpy((void *) buff, (void *) &value, len);
++ }
++}
++
++/***********************************************************************
++*/
++static const char name_acx100[] = "ACX100";
++static const char name_tnetw1100a[] = "TNETW1100A";
++static const char name_tnetw1100b[] = "TNETW1100B";
++
++static const char name_acx111[] = "ACX111";
++static const char name_tnetw1130[] = "TNETW1130";
++
++struct device_driver acx_driver = {
++ .name = CARDNAME,
++ .bus = &platform_bus_type,
++ .probe = acx_drv_probe,
++ .remove = acx_remove,
++#ifdef CONFIG_PM
++ .suspend = acx_suspend,
++ .resume = acx_resume
++#endif
++};
++
++typedef struct acx_device {
++ netdevice_t *newest;
++} acx_device_t;
++
++/* if this driver was only about PCI devices, then we probably wouldn't
++ * need this linked list.
++ * But if we want to register ALL kinds of devices in one global list,
++ * then we need it and need to maintain it properly. */
++static struct acx_device root_acx_dev = {
++ .newest = NULL,
++};
++DECLARE_MUTEX(root_acx_dev_sem);
++
++
++/***********************************************************************
++*/
++static inline txdesc_t*
++get_txdesc(wlandevice_t* priv, int index)
++{
++ return (txdesc_t*) (((u8*)priv->txdesc_start) + index * priv->txdesc_size);
++}
++
++static inline txdesc_t*
++move_txdesc(wlandevice_t* priv, txdesc_t* txdesc, int inc)
++{
++ return (txdesc_t*) (((u8*)txdesc) + inc * priv->txdesc_size);
++}
++
++static txhostdesc_t*
++acx_get_txhostdesc(wlandevice_t* priv, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)priv->txdesc_start;
++ if (ACX_DEBUG && (index % priv->txdesc_size)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= priv->txdesc_size;
++ if (ACX_DEBUG && (index >= TX_CNT)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return &priv->txhostdesc_start[index*2];
++}
++
++static client_t*
++acx_get_txc(wlandevice_t* priv, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)priv->txdesc_start;
++ if (ACX_DEBUG && (index % priv->txdesc_size)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= priv->txdesc_size;
++ if (ACX_DEBUG && (index >= TX_CNT)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return priv->txc[index];
++}
++
++static void
++acx_put_txc(wlandevice_t* priv, txdesc_t* txdesc, client_t* c)
++{
++ int index = (u8*)txdesc - (u8*)priv->txdesc_start;
++ if (ACX_DEBUG && (index % priv->txdesc_size)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ index /= priv->txdesc_size;
++ if (ACX_DEBUG && (index >= TX_CNT)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ priv->txc[index] = c;
++}
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acx_read_eeprom_offset
++**
++** Function called to read an octet in the EEPROM.
++**
++** This function is used by acx_probe_pci to check if the
++** connected card is a legal one or not.
++**
++** Arguments:
++** priv ptr to wlandevice structure
++** addr address to read in the EEPROM
++** charbuf ptr to a char. This is where the read octet
++** will be stored
++**
++** Returns:
++** zero (0) - failed
++** one (1) - success
++**
++** NOT ADAPTED FOR ACX111!!
++*/
++int
++acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
++ acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for EEPROM read\n",
++ priv->netdev->name);
++ goto fail;
++ }
++ }
++
++ *charbuf = acx_read_reg8(priv, IO_ACX_EEPROM_DATA);
++ acxlog(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf);
++ result = OK;
++
++fail:
++ return result;
++}
++
++
++/***********************************************************************
++** Dummy EEPROM read? why?!
++*/
++static int
++acx_read_eeprom_area(wlandevice_t *priv)
++{
++ int offs;
++ u8 tmp[0x3b];
++
++ for (offs = 0x8c; offs < 0xb9; offs++) {
++ acx_read_eeprom_offset(priv, offs, &tmp[offs - 0x8c]);
++ }
++ return OK;
++}
++
++
++/***********************************************************************
++** We don't lock hw accesses here since we never r/w eeprom in IRQ
++** Note: this function sleeps only because of GFP_KERNEL alloc
++*/
++#ifdef UNUSED
++int
++acx_s_write_eeprom_offset(wlandevice_t *priv, u32 addr, u32 len, const u8 *charbuf)
++{
++ u8 *data_verify = NULL;
++ unsigned long flags;
++ int count, i;
++ int result = NOT_OK;
++ u16 gpio_orig;
++
++ printk("acx: WARNING! I would write to EEPROM now. "
++ "Since I really DON'T want to unless you know "
++ "what you're doing (THIS CODE WILL PROBABLY "
++ "NOT WORK YET!), I will abort that now. And "
++ "definitely make sure to make a "
++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! "
++ "(the EEPROM content includes the PCI config header!! "
++ "If you kill important stuff, then you WILL "
++ "get in trouble and people DID get in trouble already)\n");
++ return OK;
++
++ FN_ENTER;
++
++ data_verify = kmalloc(len, GFP_KERNEL);
++ if (!data_verify) {
++ goto end;
++ }
++
++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line
++ * to be able to write to the EEPROM.
++ * NOTE: an EEPROM writing success has been reported,
++ * but you probably have to modify GPIO_OUT, too,
++ * and you probably need to activate a different GPIO
++ * line instead! */
++ gpio_orig = acx_read_reg16(priv, IO_ACX_GPIO_OE);
++ acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig & ~1);
++ acx_write_flush(priv);
++
++ /* ok, now start writing the data out */
++ for (i = 0; i < len; i++) {
++ acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
++ acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i);
++ acx_write_reg32(priv, IO_ACX_EEPROM_DATA, *(charbuf + i));
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 1);
++
++ while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(++count > 0xffff)) {
++ printk("WARNING, DANGER!!! "
++ "Timeout waiting for EEPROM write\n");
++ goto end;
++ }
++ }
++ }
++
++ /* disable EEPROM writing */
++ acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig);
++ acx_write_flush(priv);
++
++ /* now start a verification run */
++ count = 0xffff;
++ for (i = 0; i < len; i++) {
++ acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
++ acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2);
++
++ while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("timeout waiting for EEPROM read\n");
++ goto end;
++ }
++ }
++
++ data_verify[i] = acx_read_reg16(priv, IO_ACX_EEPROM_DATA);
++ }
++
++ if (0 == memcmp(charbuf, data_verify, len))
++ result = OK; /* read data matches, success */
++
++end:
++ kfree(data_verify);
++ FN_EXIT1(result);
++ return result;
++}
++#endif /* UNUSED */
++
++
++/***********************************************************************
++** acxpci_s_read_phy_reg
++**
++** Messing with rx/tx disabling and enabling here
++** (acx_write_reg32(priv, IO_ACX_ENABLE, 0b000000xx)) kills traffic
++*/
++int
++acxpci_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ FN_ENTER;
++
++ acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_PHY_CTL, 2);
++
++ count = 0xffff;
++ while (acx_read_reg32(priv, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ priv->netdev->name);
++ *charbuf = 0;
++ goto fail;
++ }
++ }
++
++ acxlog(L_DEBUG, "count was %u\n", count);
++ *charbuf = acx_read_reg8(priv, IO_ACX_PHY_DATA);
++
++ acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
++ result = OK;
++ goto fail; /* silence compiler warning */
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++int
++acxpci_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value)
++{
++ FN_ENTER;
++
++ /* FIXME: we didn't use 32bit access here since mprusko said that
++ * it results in distorted sensitivity on his card (huh!?!?
++ * doesn't happen with my setup...)
++ * But with the access reordering and flushing it
++ * shouldn't happen any more...
++ * FIXME: which radio is in the problematic card? My working one
++ * is 0x11 */
++ acx_write_reg32(priv, IO_ACX_PHY_DATA, value);
++ acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_PHY_CTL, 1);
++ acx_write_flush(priv);
++ acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#define NO_AUTO_INCREMENT 1
++
++/***********************************************************************
++** acx_s_write_fw
++**
++** Write the firmware image into the card.
++**
++** Arguments:
++** priv wlan device structure
++** apfw_image firmware image.
++**
++** Returns:
++** 1 firmware image corrupted
++** 0 success
++*/
++static int
++acx_s_write_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset)
++{
++ int len, size;
++ u32 sum, v32;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *image = (u8*)apfw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = image[0]+image[1]+image[2]+image[3];
++ image += 4;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ acxlog(L_INIT, "not using auto increment for firmware loading\n");
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++ acx_write_flush(priv);
++#endif
++
++ len = 0;
++ size = le32_to_cpu(apfw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)image);
++ sum += image[0]+image[1]+image[2]+image[3];
++ image += 4;
++ len += 4;
++
++#if NO_AUTO_INCREMENT
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++ acx_write_flush(priv);
++#endif
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, v32);
++ }
++
++ acxlog(L_DEBUG, "%s: firmware written\n", __func__);
++
++ /* compare our checksum with the stored image checksum */
++ return (sum != le32_to_cpu(apfw_image->chksum));
++}
++
++
++/***********************************************************************
++** acx_s_validate_fw
++**
++** Compare the firmware image given with
++** the firmware image written into the card.
++**
++** Arguments:
++** priv wlan device structure
++** apfw_image firmware image.
++**
++** Returns:
++** NOT_OK firmware image corrupted or not correctly written
++** OK success
++*/
++static int
++acx_s_validate_fw(wlandevice_t *priv, const firmware_image_t *apfw_image,
++ u32 offset)
++{
++ u32 v32, w32, sum;
++ int len, size;
++ int result = OK;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *image = (u8*)apfw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = image[0]+image[1]+image[2]+image[3];
++ image += 4;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++#endif
++
++ len = 0;
++ size = le32_to_cpu(apfw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)image);
++ image += 4;
++ len += 4;
++
++#if NO_AUTO_INCREMENT
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++#endif
++ w32 = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ if (unlikely(w32 != v32)) {
++ printk("acx: FATAL: firmware upload: "
++ "data parts at offset %d don't match (0x%08X vs. 0x%08X)! "
++ "I/O timing issues or defective memory, with DWL-xx0+? "
++ "ACX_IO_WIDTH=16 may help. Please report\n",
++ len, v32, w32);
++ result = NOT_OK;
++ break;
++ }
++
++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24);
++ }
++
++ /* sum control verification */
++ if (result != NOT_OK) {
++ if (sum != le32_to_cpu(apfw_image->chksum)) {
++ printk("acx: FATAL: firmware upload: "
++ "checksums don't match!\n");
++ result = NOT_OK;
++ }
++ }
++
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_upload_fw
++**
++** Arguments:
++** wlandevice: private device that contains card device
++** Returns:
++** NOT_OK: failed
++** OK: success
++** Call context:
++** acx_reset_dev
++*/
++static int
++acx_s_upload_fw(wlandevice_t *priv)
++{
++ firmware_image_t *apfw_image = NULL;
++ int res = NOT_OK;
++ int try;
++ u32 size;
++ char filename[sizeof("tiacx1NNcNN")];
++
++ FN_ENTER;
++
++ /* Try combined, then main image */
++ priv->need_radio_fw = 0;
++ sprintf(filename, "tiacx1%02dc%02X",
++ IS_ACX111(priv)*11, priv->radio_type);
++
++ apfw_image = acx_s_read_fw(priv->dev, filename, &size);
++ if (!apfw_image) {
++ priv->need_radio_fw = 1;
++ filename[sizeof("tiacx1NN")-1] = '\0';
++ apfw_image = acx_s_read_fw(priv->dev, filename, &size);
++ if (!apfw_image) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++ }
++
++ for (try = 1; try <= 5; try++) {
++ res = acx_s_write_fw(priv, apfw_image, 0);
++ acxlog(L_DEBUG|L_INIT, "acx_write_fw (main/combined):%d\n", res);
++ if (OK == res) {
++ res = acx_s_validate_fw(priv, apfw_image, 0);
++ acxlog(L_DEBUG|L_INIT, "acx_validate_fw "
++ "(main/combined):%d\n", res);
++ }
++
++ if (OK == res) {
++ SET_BIT(priv->dev_state_mask, ACX_STATE_FW_LOADED);
++ break;
++ }
++ printk("acx: firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ vfree((void *) apfw_image);
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx_s_upload_radio
++**
++** Uploads the appropriate radio module firmware
++** into the card.
++*/
++int
++acx_s_upload_radio(wlandevice_t *priv)
++{
++ acx_ie_memmap_t mm;
++ firmware_image_t *radio_image = NULL;
++ acx_cmd_radioinit_t radioinit;
++ int res = NOT_OK;
++ int try;
++ u32 offset;
++ u32 size;
++ char filename[sizeof("tiacx1NNrNN")];
++
++ if (!priv->need_radio_fw) return OK;
++
++ FN_ENTER;
++
++ acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP);
++ offset = le32_to_cpu(mm.CodeEnd);
++
++ sprintf(filename, "tiacx1%02dr%02X",
++ IS_ACX111(priv)*11,
++ priv->radio_type);
++ radio_image = acx_s_read_fw(priv->dev, filename, &size);
++ if (!radio_image) {
++ printk("acx: can't load radio module '%s'\n", filename);
++ goto fail;
++ }
++
++ acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ for (try = 1; try <= 5; try++) {
++ res = acx_s_write_fw(priv, radio_image, offset);
++ acxlog(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res);
++ if (OK == res) {
++ res = acx_s_validate_fw(priv, radio_image, offset);
++ acxlog(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res);
++ }
++
++ if (OK == res)
++ break;
++ printk("acx: radio firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
++ radioinit.offset = cpu_to_le32(offset);
++ /* no endian conversion needed, remains in card CPU area: */
++ radioinit.len = radio_image->size;
++
++ vfree(radio_image);
++
++ if (OK != res)
++ goto fail;
++
++ /* will take a moment so let's have a big timeout */
++ acx_s_issue_cmd_timeo(priv, ACX1xx_CMD_RADIOINIT,
++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000));
++
++ res = acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP);
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++void write_reg(wlandevice_t *priv, int reg, u32 value){
++ acx_write_reg32(priv,IO_ACX_HW_SLAVE_REG_ADDR,priv->io[reg]);
++ acx_write_reg16(priv,IO_ACX_HW_SLAVE_REG_DATA,value);
++}
++
++u32 read_reg(wlandevice_t *priv, int reg){
++ u32 value;
++
++ acx_write_reg32(priv,IO_ACX_HW_SLAVE_REG_ADDR,reg);
++ value = acx_read_reg32(priv,IO_ACX_HW_SLAVE_REG_DATA);
++
++ return value;
++}
++
++
++/***********************************************************************
++** acx_l_reset_mac
++**
++** Arguments:
++** wlandevice: private device that contains card device
++** Side effects:
++** MAC will be reset
++** Call context:
++** acx_reset_dev
++** Comment:
++** resets onboard acx100 MAC
++**
++** Requires lock to be taken
++*/
++static void
++acx_l_reset_mac(wlandevice_t *priv)
++{
++ u16 temp;
++
++ FN_ENTER;
++
++ /* Pocket PC driver setting this register
++ * with 2.
++ */
++ acx_write_reg16(priv,IO_ACX_PCI_ARB_CFG,0x2);
++
++ msleep(300);
++
++ /* halt eCPU */
++ acxlog(L_DEBUG, "%s: Halt eCPU ...\n", __func__);
++ temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1;
++ acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp);
++ acx_write_flush(priv);
++
++ test(priv);
++
++ /* now do soft reset of eCPU */
++ temp = acx_read_reg16(priv, IO_ACX_SOFT_RESET) | 0x1;
++ acxlog(L_DEBUG, "%s: enable soft reset...\n", __func__);
++ acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp);
++ acx_write_flush(priv);
++
++ test(priv);
++
++ /* now reset bit again */
++ acxlog(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__);
++ /* deassert eCPU reset */
++ acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp & ~0x1);
++
++ /* now start a burst read from initial flash EEPROM */
++ temp = acx_read_reg16(priv, IO_ACX_EE_START) | 0x1;
++ acx_write_reg16(priv, IO_ACX_EE_START, temp);
++ acx_write_flush(priv);
++
++ test(priv);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_s_verify_init
++*/
++static int
++acx_s_verify_init(wlandevice_t *priv)
++{
++ int result = NOT_OK;
++ int timer;
++
++ FN_ENTER;
++
++ for (timer = 40; timer > 0; timer--) {
++ u16 irqstat = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqstat & HOST_INT_FCS_THRESHOLD) {
++ result = OK;
++ acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD);
++ break;
++ }
++ /* HZ / 50 resulted in 24 schedules for ACX100 on my machine,
++ * so better schedule away longer for greater efficiency,
++ * decrease loop count */
++ acx_s_msleep(50);
++ }
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++
++/***********************************************************************
++** A few low-level helpers
++**
++** Note: these functions are not protected by lock
++** and thus are never allowed to be called from IRQ.
++** Also they must not race with fw upload which uses same hw regs
++*/
++
++/***********************************************************************
++** acx_read_info_status
++*/
++/* Info mailbox format:
++2 bytes: type
++2 bytes: status
++more bytes may follow
++ docs say about status:
++ 0x0000 info available (set by hw)
++ 0x0001 information received (must be set by host)
++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw)
++ but in practice we've seen:
++ 0x9000 when we did not set status to 0x0001 on prev message
++ 0x1001 when we did set it
++ 0x0000 was never seen
++ conclusion: this is really a bitfield:
++ 0x1000 is 'info available' bit
++ 'mailbox overflowed' bit is 0x8000, not 0x1000
++ value of 0x0000 probably means that there is no message at all
++ P.S. I dunno how in hell hw is supposed to notice that messages are lost -
++ it does NOT clear bit 0x0001, and this bit will probably stay forever set
++ after we set it once. Let's hope this will be fixed in firmware someday
++*/
++static void
++acx_read_info_status(wlandevice_t *priv)
++{
++ u32 value;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1);
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
++ acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS));
++
++ /* make sure we only read the data once all cfg registers are written: */
++ acx_write_flush(priv);
++ value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ priv->info_type = (u16)value;
++ priv->info_status = (value >> 16);
++
++ /* inform hw that we have read this info message */
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, priv->info_type | 0x00010000);
++ acx_write_flush(priv);
++ /* now bother hw to notice it: */
++ acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_INFOACK);
++ acx_write_flush(priv);
++
++ acxlog(L_CTL, "info_type 0x%04X, info_status 0x%04X\n",
++ priv->info_type, priv->info_status);
++}
++
++
++/***********************************************************************
++** acx_write_cmd_type_or_status
++*/
++static void
++acx_write_cmd_type_or_status(wlandevice_t *priv, u32 val)
++{
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
++ acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS));
++
++ /* make sure we only write the data once all config registers are written */
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, val);
++ acx_write_flush(priv);
++}
++static inline void
++acx_write_cmd_type(wlandevice_t *priv, u32 val)
++{
++ acx_write_cmd_type_or_status(priv, val);
++}
++static inline void
++acx_write_cmd_status(wlandevice_t *priv, u32 val)
++{
++ acx_write_cmd_type_or_status(priv, val<<16);
++}
++
++
++/***********************************************************************
++** acx_read_cmd_status
++*/
++static void
++acx_read_cmd_status(wlandevice_t *priv)
++{
++ u32 value;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
++ acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS));
++
++ /* make sure we only read the data once all config registers are written */
++ acx_write_flush(priv);
++ value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ priv->cmd_type = (u16)value;
++ priv->cmd_status = (value >> 16);
++
++ acxlog(L_CTL, "cmd_type 0x%04X, cmd_status 0x%04X [%s]\n",
++ priv->cmd_type, priv->cmd_status,
++ acx_cmd_status_str(priv->cmd_status));
++}
++
++/***********************************************************************
++** acx_s_reset_dev
++**
++** Arguments:
++** netdevice that contains the wlandevice priv variable
++** Returns:
++** NOT_OK on fail
++** OK on success
++** Side effects:
++** device is hard reset
++** Call context:
++** acx_probe_pci
++** Comment:
++** This resets the acx100 device using low level hardware calls
++** as well as uploads and verifies the firmware to the card
++*/
++static int
++acx_s_reset_dev(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ const char* msg = "";
++ unsigned long flags;
++ int result = NOT_OK;
++ u16 hardware_info;
++ u16 ecpu_ctrl;
++
++ FN_ENTER;
++
++ /* we're doing a reset, so hardware is unavailable */
++
++ /* reset the device to make sure the eCPU is stopped
++ * to upload the firmware correctly */
++
++ acx_lock(priv, flags);
++
++ acx_l_reset_mac(priv);
++
++ ecpu_ctrl = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) & 1;
++ if (!ecpu_ctrl) {
++ msg = "eCPU is already running. ";
++ goto fail_unlock;
++ }
++
++#ifdef WE_DONT_NEED_THAT_DO_WE
++ if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 2) {
++ /* eCPU most likely means "embedded CPU" */
++ msg = "eCPU did not start after boot from flash. ";
++ goto fail_unlock;
++ }
++
++ /* check sense on reset flags */
++ if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 0x10) {
++ printk("%s: eCPU did not start after boot (SOR), "
++ "is this fatal?\n", dev->name);
++ }
++#endif
++ /* scan, if any, is stopped now, setting corresponding IRQ bit */
++ priv->irq_status |= HOST_INT_SCAN_COMPLETE;
++
++ acx_unlock(priv, flags);
++
++ /* without this delay acx100 may fail to report hardware_info
++ ** (see below). Most probably eCPU runs some init code */
++ acx_s_msleep(10);
++
++ /* Need to know radio type before fw load */
++ hardware_info = acx_read_reg16(priv, IO_ACX_EEPROM_INFORMATION);
++ priv->form_factor = hardware_info & 0xff;
++ priv->radio_type = hardware_info >> 8;
++
++ /* load the firmware */
++ if (OK != acx_s_upload_fw(priv)){
++ printk("Failed to load firmware\n");
++ goto fail;
++ }
++
++ acx_s_msleep(10);
++
++ /* now start eCPU by clearing bit */
++ acxlog(L_DEBUG, "booted eCPU up and waiting for completion...\n");
++ //acx_write_reg16(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1);
++ write_reg(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1);
++
++ /* wait for eCPU bootup */
++ if (OK != acx_s_verify_init(priv)) {
++ msg = "timeout waiting for eCPU. ";
++ goto fail;
++ }
++
++ acxlog(L_DEBUG, "eCPU has woken up, card is ready to be configured\n");
++
++ if (IS_ACX111(priv)) {
++ acxlog(L_DEBUG, "cleaning up cmd mailbox access area\n");
++ acx_write_cmd_status(priv, 0);
++ acx_read_cmd_status(priv);
++ if (priv->cmd_status) {
++ msg = "error cleaning cmd mailbox area. ";
++ goto fail;
++ }
++ }
++
++ /* TODO what is this one doing ?? adapt for acx111 */
++ if ((OK != acx_read_eeprom_area(priv)) && IS_ACX100(priv)) {
++ /* does "CIS" mean "Card Information Structure"?
++ * If so, then this would be a PCMCIA message...
++ */
++ msg = "CIS error. ";
++ goto fail;
++ }
++
++ result = OK;
++ FN_EXIT1(result);
++ return result;
++
++/* Finish error message. Indicate which function failed */
++fail_unlock:
++ acx_unlock(priv, flags);
++fail:
++ printk("acx: %sreset_dev() FAILED\n", msg);
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_init_mboxes
++*/
++void
++acx_init_mboxes(wlandevice_t *priv)
++{
++ u32 cmd_offs, info_offs;
++
++ FN_ENTER;
++
++ cmd_offs = acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS);
++ info_offs = acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS);
++ priv->cmd_area = (u8 *) cmd_offs + 0x4;
++ priv->info_area = (u8 *) info_offs + 0x4;
++ acxlog(L_DEBUG, "iobase2=%p\n"
++ "cmd_mbox_offset=%X cmd_area=%p\n"
++ "info_mbox_offset=%X info_area=%p\n",
++ priv->iobase2,
++ cmd_offs, priv->cmd_area,
++ info_offs, priv->info_area);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_issue_cmd_timeo
++* Excecutes a command in the command mailbox
++*
++* Arguments:
++* *pcmdparam = an pointer to the data. The data mustn't include
++* the 4 byte command header!
++*
++* NB: we do _not_ take lock inside, so be sure to not touch anything
++* which may interfere with IRQ handler operation
++*
++* TODO: busy wait is a bit silly, so:
++* 1) stop doing many iters - go to sleep after first
++* 2) go to waitqueue based approach: wait, not poll!
++*----------------------------------------------------------------*/
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxpci_s_issue_cmd_timeo(
++ wlandevice_t *priv,
++ unsigned int cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout)
++{
++#else
++int
++acxpci_s_issue_cmd_timeo_debug(
++ wlandevice_t *priv,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout,
++ const char* cmdstr)
++{
++ unsigned long start = jiffies;
++#endif
++ const char *devname;
++ unsigned counter;
++ u16 irqtype;
++ u16 cmd_status;
++
++ FN_ENTER;
++
++ devname = priv->netdev->name;
++ if (!devname || !devname[0])
++ devname = "acx";
++
++ acxlog(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n",
++ cmdstr, buflen, timeout,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ if (!(priv->dev_state_mask & ACX_STATE_FW_LOADED)) {
++ printk("%s: "FUNC"(): firmware is not loaded yet, "
++ "cannot execute commands!\n", devname);
++ goto bad;
++ }
++
++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) {
++ printk("input pdr (len=%u):\n", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++
++ /* wait for firmware to become idle for our command submission */
++ counter = 199; /* in ms */
++ do {
++ acx_read_cmd_status(priv);
++ /* Test for IDLE state */
++ if (!priv->cmd_status)
++ break;
++ if (counter % 10 == 0) {
++ /* we waited 10 iterations, no luck. Sleep 10 ms */
++ acx_s_msleep(10);
++ }
++ } while (--counter);
++
++ if (!counter) {
++ /* the card doesn't get idle, we're in trouble */
++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n",
++ devname, priv->cmd_status);
++ goto bad;
++ } else if (counter < 190) { /* if waited >10ms... */
++ acxlog(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. "
++ "Please report\n", 199 - counter);
++ }
++
++ /* now write the parameters of the command if needed */
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++#if CMD_DISCOVERY
++ if (cmd == ACX1xx_CMD_INTERROGATE)
++ acx_mailbox_fill(priv, (unsigned int) priv->cmd_area, 0xAA, buflen);
++#endif
++ acx_mailbox_write(priv,
++ (unsigned int) priv->cmd_area,
++ buffer, (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen);
++ }
++
++ /* now write the actual command type */
++ priv->cmd_type = cmd;
++ acx_write_cmd_type(priv, cmd);
++ /* execute command */
++ acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_CMD);
++ acx_write_flush(priv);
++
++ /* wait for firmware to process command */
++
++ /* Ensure nonzero and not too large timeout.
++ ** Also converts e.g. 100->99, 200->199
++ ** which is nice but not essential */
++ timeout = (timeout-1) | 1;
++ if (unlikely(timeout > 1199))
++ timeout = 1199;
++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */
++ priv->irq_status &= ~HOST_INT_CMD_COMPLETE;
++
++ /* we schedule away sometimes (timeout can be large) */
++ counter = timeout;
++ do {
++ if (!priv->irqs_active) { /* IRQ disabled: poll */
++ irqtype = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ acx_write_reg16(priv, IO_ACX_IRQ_ACK,
++ HOST_INT_CMD_COMPLETE);
++ break;
++ }
++ } else { /* Wait when IRQ will set the bit */
++ irqtype = priv->irq_status;
++ if (irqtype & HOST_INT_CMD_COMPLETE)
++ break;
++ }
++
++ if (counter % 10 == 0) {
++ /* we waited 10 iterations, no luck. Sleep 10 ms */
++ acx_s_msleep(10);
++ }
++ } while (--counter);
++
++ /* save state for debugging */
++ acx_read_cmd_status(priv);
++ cmd_status = priv->cmd_status;
++
++ /* put the card in IDLE state */
++ priv->cmd_status = 0;
++ acx_write_cmd_status(priv, 0);
++
++ if (!counter) { /* timed out! */
++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. "
++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms "
++ "cmd_status:%d (%s)\n",
++ devname, (priv->irqs_active) ? "waiting" : "polling",
++ irqtype, priv->irq_status, timeout,
++ cmd_status, acx_cmd_status_str(cmd_status));
++ goto bad;
++ } else if (timeout - counter > 30) { /* if waited >30ms... */
++ acxlog(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. "
++ "count:%d. Please report\n",
++ (priv->irqs_active) ? "waited" : "polled",
++ timeout - counter, counter);
++ }
++
++ if (1 != cmd_status) { /* it is not a 'Success' */
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). "
++ "Took %dms of %d\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status),
++ timeout - counter, timeout);
++ /* zero out result buffer */
++ if (buffer && buflen)
++ memset(buffer, 0, buflen);
++ goto bad;
++ }
++
++ /* read in result parameters if needed */
++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) {
++ //memcpy(buffer, priv->cmd_area, buflen);
++ acx_mailbox_read(priv, (unsigned int) priv->cmd_area, buffer, buflen);
++ if (acx_debug & L_DEBUG) {
++ printk("output buffer (len=%u): ", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++ }
++/* ok: */
++ acxlog(L_CTL, FUNC"(%s): took %ld jiffies to complete\n",
++ cmdstr, jiffies - start);
++ FN_EXIT1(OK);
++ return OK;
++
++bad:
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_get_firmware_version
++*----------------------------------------------------------------*/
++static void
++acx_s_get_firmware_version(wlandevice_t *priv)
++{
++ fw_ver_t fw;
++ u8 hexarr[4] = { 0, 0, 0, 0 };
++ int hexidx = 0, val = 0;
++ const char *num;
++ char c;
++
++ FN_ENTER;
++
++ acx_s_interrogate(priv, &fw, ACX1xx_IE_FWREV);
++ memcpy(priv->firmware_version, fw.fw_id, FW_ID_SIZE);
++ priv->firmware_version[FW_ID_SIZE] = '\0';
++ acxlog(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n",
++ priv->firmware_version, fw.hw_id);
++
++ if (strncmp(fw.fw_id, "Rev ", 4) != 0) {
++ printk("acx: strange firmware version string "
++ "'%s', please report\n", priv->firmware_version);
++ priv->firmware_numver = 0x01090407; /* assume 1.9.4.7 */
++ } else {
++ num = &fw.fw_id[4];
++ while (1) {
++ c = *num++;
++ if ((c == '.') || (c == '\0')) {
++ hexarr[hexidx++] = val;
++ if ((hexidx > 3) || (c == '\0')) /* end? */
++ break;
++ val = 0;
++ continue;
++ }
++ if ((c >= '0') && (c <= '9'))
++ c -= '0';
++ else
++ c = c - 'a' + (char)10;
++ val = val*16 + c;
++ }
++
++ priv->firmware_numver = (u32)(
++ (hexarr[0] << 24) + (hexarr[1] << 16)
++ + (hexarr[2] << 8) + hexarr[3]);
++ acxlog(L_DEBUG, "firmware_numver 0x%08X\n", priv->firmware_numver);
++ }
++ if (IS_ACX111(priv)) {
++ if (priv->firmware_numver == 0x00010011) {
++ /* This one does not survive floodpinging */
++ printk("acx: firmware '%s' is known to be buggy, "
++ "please upgrade\n", priv->firmware_version);
++ }
++ if (priv->firmware_numver == 0x02030131) {
++ /* With this one, all rx packets look mangled
++ ** Most probably we simply do not know how to use it
++ ** properly */
++ printk("acx: firmware '%s' does not work well "
++ "with this driver\n", priv->firmware_version);
++ }
++ }
++
++ priv->firmware_id = le32_to_cpu(fw.hw_id);
++
++ /* we're able to find out more detailed chip names now */
++ switch (priv->firmware_id & 0xffff0000) {
++ case 0x01010000:
++ case 0x01020000:
++ priv->chip_name = name_tnetw1100a;
++ break;
++ case 0x01030000:
++ priv->chip_name = name_tnetw1100b;
++ break;
++ case 0x03000000:
++ case 0x03010000:
++ priv->chip_name = name_tnetw1130;
++ break;
++ default:
++ printk("acx: unknown chip ID 0x%08X, "
++ "please report\n", priv->firmware_id);
++ break;
++ }
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_display_hardware_details
++*
++* Arguments:
++* priv: ptr to wlandevice that contains all the details
++* displayed by this function
++* Call context:
++* acx_probe_pci
++* Comment:
++* This function will display strings to the system log according
++* to device form_factor and radio type. It will needed to be
++*----------------------------------------------------------------*/
++static void
++acx_display_hardware_details(wlandevice_t *priv)
++{
++ const char *radio_str, *form_str;
++
++ FN_ENTER;
++
++ switch (priv->radio_type) {
++ case RADIO_MAXIM_0D:
++ /* hmm, the DWL-650+ seems to have two variants,
++ * according to a windows driver changelog comment:
++ * RFMD and Maxim. */
++ radio_str = "Maxim";
++ break;
++ case RADIO_RFMD_11:
++ radio_str = "RFMD";
++ break;
++ case RADIO_RALINK_15:
++ radio_str = "Ralink";
++ break;
++ case RADIO_RADIA_16:
++ radio_str = "Radia";
++ break;
++ case RADIO_UNKNOWN_17:
++ /* TI seems to have a radio which is
++ * additionally 802.11a capable, too */
++ radio_str = "802.11a/b/g radio?! Please report";
++ break;
++ case RADIO_UNKNOWN_19:
++ radio_str = "A radio used by Safecom cards?! Please report";
++ break;
++ default:
++ radio_str = "UNKNOWN, please report the radio type name!";
++ break;
++ }
++
++ switch (priv->form_factor) {
++ case 0x00:
++ form_str = "unspecified";
++ break;
++ case 0x01:
++ form_str = "(mini-)PCI / CardBus";
++ break;
++ case 0x02:
++ form_str = "USB";
++ break;
++ case 0x03:
++ form_str = "Compact Flash";
++ break;
++ default:
++ form_str = "UNKNOWN, Please report";
++ break;
++ }
++
++ printk("acx: form factor 0x%02X (%s), "
++ "radio type 0x%02X (%s), EEPROM version 0x%02X, "
++ "uploaded firmware '%s' (0x%08X)\n",
++ priv->form_factor, form_str, priv->radio_type, radio_str,
++ priv->eeprom_version, priv->firmware_version,
++ priv->firmware_id);
++
++ FN_EXIT0;
++}
++
++/***********************************************************************
++*/
++#ifdef NONESSENTIAL_FEATURES
++typedef struct device_id {
++ unsigned char id[6];
++ char *descr;
++ char *type;
++} device_id_t;
++
++static const device_id_t
++device_ids[] =
++{
++ {
++ {'G', 'l', 'o', 'b', 'a', 'l'},
++ NULL,
++ NULL,
++ },
++ {
++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
++ "uninitialized",
++ "SpeedStream SS1021 or Gigafast WF721-AEX"
++ },
++ {
++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85},
++ "non-standard",
++ "DrayTek Vigor 520"
++ },
++ {
++ {'?', '?', '?', '?', '?', '?'},
++ "non-standard",
++ "Level One WPC-0200"
++ },
++ {
++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ "empty",
++ "DWL-650+ variant"
++ }
++};
++
++static void
++acx_show_card_eeprom_id(wlandevice_t *priv)
++{
++ unsigned char buffer[CARD_EEPROM_ID_SIZE];
++ int i;
++
++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE);
++ /* use direct EEPROM access */
++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) {
++ if (OK != acx_read_eeprom_offset(priv,
++ ACX100_EEPROM_ID_OFFSET + i,
++ &buffer[i]))
++ {
++ printk("acx: reading EEPROM FAILED\n");
++ break;
++ }
++ }
++
++ for (i = 0; i < VEC_SIZE(device_ids); i++) {
++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) {
++ if (device_ids[i].descr) {
++ printk("acx: EEPROM card ID string check "
++ "found %s card ID: is this %s?\n",
++ device_ids[i].descr, device_ids[i].type);
++ }
++ break;
++ }
++ }
++ if (i == VEC_SIZE(device_ids)) {
++ printk("acx: EEPROM card ID string check found "
++ "unknown card: expected 'Global', got '%.*s\'. "
++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer);
++ }
++}
++#endif /* NONESSENTIAL_FEATURES */
++
++
++/***********************************************************************
++*/
++static void
++acx_s_device_chain_add(struct net_device *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ down(&root_acx_dev_sem);
++ priv->prev_nd = root_acx_dev.newest;
++ root_acx_dev.newest = dev;
++ priv->netdev = dev;
++ up(&root_acx_dev_sem);
++}
++
++static void
++acx_s_device_chain_remove(struct net_device *dev)
++{
++ struct net_device *querydev;
++ struct net_device *olderdev;
++ struct net_device *newerdev;
++
++ down(&root_acx_dev_sem);
++ querydev = root_acx_dev.newest;
++ newerdev = NULL;
++ while (querydev) {
++ olderdev = ((wlandevice_t*)netdev_priv(querydev))->prev_nd;
++ if (0 == strcmp(querydev->name, dev->name)) {
++ if (!newerdev) {
++ /* if we were at the beginning of the
++ * list, then it's the list head that
++ * we need to update to point at the
++ * next older device */
++ root_acx_dev.newest = olderdev;
++ } else {
++ /* it's the device that is newer than us
++ * that we need to update to point at
++ * the device older than us */
++ ((wlandevice_t*)netdev_priv(newerdev))->
++ prev_nd = olderdev;
++ }
++ break;
++ }
++ /* "newerdev" is actually the device of the old iteration,
++ * but since the list starts (root_acx_dev.newest)
++ * with the newest devices,
++ * it's newer than the ones following.
++ * Oh the joys of iterating from newest to oldest :-\ */
++ newerdev = querydev;
++
++ /* keep checking old devices for matches until we hit the end
++ * of the list */
++ querydev = olderdev;
++ }
++ up(&root_acx_dev_sem);
++}
++
++
++/***********************************************************************
++** acx_free_desc_queues
++**
++** Releases the queues that have been allocated, the
++** others have been initialised to NULL so this
++** function can be used if only part of the queues were allocated.
++*/
++static inline void
++acx_free_coherent(struct pci_dev *hwdev, size_t size,
++ void *vaddr, dma_addr_t dma_handle)
++{
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53)
++ dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev,
++ size, vaddr, dma_handle);
++#else
++ pci_free_consistent(hwdev, size, vaddr, dma_handle);
++#endif
++}
++
++void
++acx_free_desc_queues(wlandevice_t *priv)
++{
++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \
++ if (ptr) { \
++ acx_free_coherent(0, size, ptr, phyaddr); \
++ ptr = NULL; \
++ size = 0; \
++ }
++
++ FN_ENTER;
++
++ ACX_FREE_QUEUE(priv->txhostdesc_area_size, priv->txhostdesc_start, priv->txhostdesc_startphy);
++ ACX_FREE_QUEUE(priv->txbuf_area_size, priv->txbuf_start, priv->txbuf_startphy);
++
++ priv->txdesc_start = NULL;
++
++ ACX_FREE_QUEUE(priv->rxhostdesc_area_size, priv->rxhostdesc_start, priv->rxhostdesc_startphy);
++ ACX_FREE_QUEUE(priv->rxbuf_area_size, priv->rxbuf_start, priv->rxbuf_startphy);
++
++ priv->rxdesc_start = NULL;
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_delete_dma_regions
++*----------------------------------------------------------------*/
++static void
++acx_s_delete_dma_regions(wlandevice_t *priv)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands
++ * here instead? Or are we that much down the road that it's no
++ * longer possible here? */
++ acx_write_reg16(priv, IO_ACX_ENABLE, 0);
++
++ acx_s_msleep(100);
++
++ acx_lock(priv, flags);
++ acx_free_desc_queues(priv);
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_probe_pci
++*
++* Probe routine called when a PCI device w/ matching ID is found.
++* Here's the sequence:
++* - Allocate the PCI resources.
++* - Read the PCMCIA attribute memory to make sure we have a WLAN card
++* - Reset the MAC
++* - Initialize the dev and wlan data
++* - Initialize the MAC
++*
++* Arguments:
++* pdev ptr to pci device structure containing info about
++* pci configuration.
++* id ptr to the device id entry that matched this device.
++*
++* Returns:
++* zero - success
++* negative - failed
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static const u16
++IO_ACX100[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0004, /* IO_ACX_HW_SLAVE_REG_ADDR */
++ 0x0008, /* IO_ACX_HW_SLAVE_REG_DATA */
++ 0x000c, /* IO_ACX_HW_SLAVE_REG_REG_CTRL */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++ 0x0024, /* IO_ACX_CHIPID */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x007c, /* IO_ACX_INT_TRIG */
++ 0x0098, /* IO_ACX_IRQ_MASK */
++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00ac, /* IO_ACX_IRQ_ACK */
++ 0x00b0, /* IO_ACX_HINT_TRIG */
++
++ 0x0104, /* IO_ACX_ENABLE */
++
++ 0x0250, /* IO_ACX_EEPROM_CTL */
++ 0x0254, /* IO_ACX_EEPROM_ADDR */
++ 0x0258, /* IO_ACX_EEPROM_DATA */
++ 0x025c, /* IO_ACX_EEPROM_CFG */
++
++ 0x0268, /* IO_ACX_PHY_ADDR */
++ 0x026c, /* IO_ACX_PHY_DATA */
++ 0x0270, /* IO_ACX_PHY_CTL */
++
++ 0x0290, /* IO_ACX_GPIO_OE */
++
++ 0x0294, /* IO_ACX_GPIO_IN */
++ 0x0298, /* IO_ACX_GPIO_OUT */
++ 0x029c, /* IO_ACX_GPIO_PD */
++ 0x02a0, /* IO_ACX_GPIO_CFG */
++
++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x02d0, /* IO_ACX_EE_START */
++ 0x02d4, /* IO_ACX_SOR_CFG */
++ 0x02d8, /* IO_ACX_ECPU_CTRL */
++
++ 0x0804, /* IO_ACX_HI_CTL */
++ 0x0808, /* IO_ACX_LPWR_MGN */
++
++ 0x010c, /* IO_ACX_PCI_ARB_CFG */
++
++};
++
++static void
++acx_netdev_init(struct net_device *dev) {}
++
++//FIXME: do the same for USB
++static int
++acx_change_mtu(struct net_device *dev, int mtu)
++{
++ enum {
++ MIN_MTU = 256,
++ MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN)
++ };
++
++ if (mtu < MIN_MTU || mtu > MAX_MTU)
++ return -EINVAL;
++
++ dev->mtu = mtu;
++ return 0;
++}
++
++
++static int acx_enable(struct platform_device *pdev){
++ unsigned long flags;
++
++ /* We need to disabe the interrupt
++ * around reset.
++ */
++ local_irq_save(flags);
++
++ /* Now lets turn on and
++ * reset the device.
++ */
++ pca9535_gpio_write(GPIO6, LOW);
++ pca9535_gpio_write(GPIO12, LOW);
++
++ /* TODO: someother configuration needs
++ * to be done here.
++ */
++
++ // Now we can enable interrupt.
++ local_irq_restore(flags);
++
++
++ return 0;
++}
++
++static inline void test(wlandevice_t *priv) {
++ printk("===============================\n");
++ printk("Reset : %04x\n",acx_read_reg16(priv, IO_ACX_SOFT_RESET));
++ printk("eCPU CTL: %04x\n",acx_read_reg16(priv, IO_ACX_ECPU_CTRL));
++ printk("SOR CFG : %08x\n",acx_read_reg32(priv, IO_ACX_SOR_CFG));
++ printk("EE START: %08x\n",acx_read_reg32(priv, IO_ACX_EE_START));
++ printk("HI CTRL : %08x\n",acx_read_reg32(priv, IO_ACX_HI_CTRL));
++// printk("Info : %08x\n",acx_read_reg32(priv, IO_ACX_EEPROM_INFORMATION));
++ printk("GPIO OE : %04x\n",acx_read_reg16(priv, IO_ACX_GPIO_OE));
++ printk("GPIO OUT: %04x\n",acx_read_reg16(priv, IO_ACX_GPIO_OUT));
++ printk("GPIO IN : %04x\n",acx_read_reg16(priv, IO_ACX_GPIO_IN));
++ printk("GPIO PD : %04x\n",acx_read_reg16(priv, IO_ACX_GPIO_PD));
++ printk("GPIO CFG: %04x\n",acx_read_reg16(priv, IO_ACX_GPIO_CFG));
++ printk("Chip ID : %08x\n",acx_read_reg32(priv, IO_ACX_CHIPID));
++
++/*
++ printk("HW CTRL : %08x\n",acx_read_reg32(priv,IO_ACX_HW_SLAVE_REG_CTRL));
++ printk("HW ADDR : %08x\n",acx_read_reg32(priv,IO_ACX_HW_SLAVE_REG_ADDR));
++ printk("HW DATA : %08x\n",acx_read_reg32(priv,IO_ACX_HW_SLAVE_REG_DATA));
++*/
++
++ msleep(50);
++
++ printk("==============================\n");
++}
++
++static int __init acx_probe(struct net_device *ndev, void __iomem *addr, struct resource *res){
++ wlandevice_t *priv = netdev_priv(ndev);
++ int ret = 0;
++
++ ether_setup(ndev);
++
++ /* now that device init was successful, fill remaining fields... */
++ ndev->open = &acx_e_open;
++ ndev->stop = &acx_e_close;
++ ndev->hard_start_xmit = &acx_i_start_xmit;
++ ndev->get_stats = &acx_e_get_stats;
++ ndev->get_wireless_stats = &acx_e_get_wireless_stats;
++#if WIRELESS_EXT >= 13
++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++#else
++ ndev->do_ioctl = &acx_e_ioctl_old;
++#endif
++ ndev->set_multicast_list = &acx_i_set_multicast_list;
++ ndev->tx_timeout = &acx_i_tx_timeout;
++ ndev->change_mtu = &acx_change_mtu;
++ ndev->watchdog_timeo = 4 * HZ;
++
++ spin_lock_init(&priv->lock); /* initial state: unlocked */
++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */
++ sema_init(&priv->sem, 1); /* initial state: 1 (upped) */
++
++ priv->iobase = (unsigned char *) addr;
++ priv->iobase2 = 0;
++ priv->membase = res->start;
++ priv->chip_type = CHIPTYPE_ACX100;
++ priv->chip_name = name_acx100;
++ priv->io = IO_ACX100;
++ priv->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
++ spin_lock_init(&priv->lock);
++
++ test(priv);
++
++#ifdef NONESSENTIAL_FEATURES
++ acx_show_card_eeprom_id(priv);
++#endif /* NONESSENTIAL_FEATURES */
++
++ /* now we have our device, so make sure the kernel doesn't try
++ * to send packets even though we're not associated to a network yet */
++ acx_stop_queue(ndev, "after setup");
++
++ /* register new dev in linked list */
++ acx_s_device_chain_add(ndev);
++
++ if((ret = acx_s_reset_dev(ndev)) != OK){
++ /* Failed to reset device */
++ ret = -EIO;
++ goto reset_fail;
++ }
++
++ /* ok, basic setup is finished, now start initialising the card */
++#if 0
++ hardware_info = acx_read_reg16(priv, IO_ACX_EEPROM_INFORMATION);
++ priv->form_factor = (u8)(hardware_info & 0xff);
++ priv->radio_type = (u8)(hardware_info >> 8 & 0xff);
++#endif
++
++ if (OK != acx_read_eeprom_offset(priv, 0x05, &priv->eeprom_version)) {
++ ret = -EIO;
++ goto fail_read_eeprom_version;
++ }
++
++ if (OK != acx_s_init_mac(ndev)) {
++ acxlog(L_DEBUG | L_INIT,
++ "Danger Will Robinson, MAC did not come back\n");
++ ret = -EIO;
++ goto fail_init_mac;
++ }
++
++ if (OK != acx_s_set_defaults(priv)) {
++ printk("acx: set_defaults() FAILED\n");
++ goto fail_set_defaults;
++ }
++
++ /* needs to be after acx_init_mac() due to necessary init stuff */
++ acx_s_get_firmware_version(priv);
++
++ acx_display_hardware_details(priv);
++
++ /* ...and register the card, AFTER everything else has been set up,
++ * since otherwise an ioctl could step on our feet due to
++ * firmware operations happening in parallel or uninitialized data */
++ ret = register_netdev(ndev);
++ if (OK != ret) {
++ printk(KERN_ERR
++ "%s: Register net device of %s FAILED: %d\n",
++ __func__, ndev->name, ret);
++ ret = -EIO;
++ goto fail_register_netdev;
++ }
++ acx_carrier_off(ndev, "on probe");
++
++#ifdef CONFIG_PROC_FS
++ if (OK != acx_proc_register_entries(ndev)) {
++ ret = -EIO;
++ goto fail_proc_register_entries;
++ }
++#endif
++
++#if CMD_DISCOVERY
++ great_inquisitor(priv);
++#endif
++
++
++ return 0;
++
++#ifdef CONFIG_PROC_FS
++fail_proc_register_entries:
++#endif
++
++ if (priv->dev_state_mask & ACX_STATE_IFACE_UP)
++ acx_s_down(ndev);
++ unregister_netdev(ndev);
++
++fail_register_netdev:
++ acx_s_delete_dma_regions(priv);
++fail_set_defaults:
++fail_init_mac:
++fail_read_eeprom_version:
++reset_fail:
++ acx_s_device_chain_remove(ndev);
++
++ return ret;
++}
++
++static void dumpRegs(char *msg, unsigned char *base) {
++ u32 val = 0;
++ u16 addr = 0;
++
++ printk("============= [ %s ] ===============\n",msg);
++
++ for(addr = 0; addr <= 0x0be0; addr+=4){
++ if(addr >= 0x0710 && addr <= 0x0740){
++ val = 0;
++ printk("Reg[%04x] : %08x\n",addr, val);
++ msleep(50);
++ continue;
++ }
++
++ val = readw((u8 *) base + addr) + (readw((u8 *) base + addr + 2) << 16);
++
++ printk("Reg[%04x] : %08x\n",addr, val);
++ msleep(50);
++ }
++
++ printk("=====================================\n");
++}
++
++
++static int __init acx_drv_probe(struct device *dev){
++ struct platform_device *pdev = to_platform_device(dev);
++ struct net_device *ndev = NULL;
++ struct resource *res = NULL;
++ unsigned int __iomem *addr = NULL;
++ wlandevice_t *priv = NULL;
++ int ret;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ printk(KERN_ERR "Device not found\n");
++ ret = -ENODEV;
++ goto out;
++ }
++
++
++ if (!request_mem_region(res->start, SZ_32M, CARDNAME)) {
++ printk(KERN_ERR "Device busy\n");
++ ret = -EBUSY;
++ goto out;
++ }
++
++ // Create new ethernet device
++ ndev = alloc_netdev(sizeof(wlandevice_t), "wlan%d", acx_netdev_init);
++ if (!ndev) {
++ printk("%s: could not allocate device.\n", CARDNAME);
++ ret = -ENOMEM;
++ goto out_release_io;
++ }
++ SET_MODULE_OWNER(ndev);
++ SET_NETDEV_DEV(ndev, dev);
++
++ // Get driver private data
++ priv = netdev_priv(ndev);
++
++ // Cleanup the private area
++ memset((void *) priv, 0, sizeof(wlandevice_t));
++
++ ndev->dma = (unsigned char)-1;
++ ndev->irq = platform_get_irq(pdev, 0);
++ priv->dev = dev;
++
++ // Enable device
++ acx_enable(pdev);
++
++ /* Create IOMEM port so we
++ * can use to to read and write
++ * from card registers.
++ */
++ if(!(addr = ioremap(res->start, SZ_32M))){
++ printk(KERN_ERR "Failed to map ioport\n");
++ ret = -ENOMEM;
++ goto free_device;
++ }
++
++ dev_set_drvdata(dev, ndev);
++
++ //dumpRegs("After",addr);
++
++
++ if((ret = acx_probe(ndev, addr,res))){
++ /* Something wrong we couldn't
++ * talke to the device.
++ */
++ printk(KERN_ERR "Failed to probe ACX\n");
++ goto out_iounmap;
++ }
++
++ printk("acx "WLAN_RELEASE": net device %s, driver compiled "
++ "against wireless extensions %d and Linux %s\n",
++ ndev->name, WIRELESS_EXT, UTS_RELEASE);
++
++ return 0;
++
++out_iounmap:
++ dev_set_drvdata(dev, NULL);
++ iounmap(addr);
++free_device:
++ free_netdev(ndev);
++out_release_io:
++ release_mem_region(res->start, SZ_32M);
++out:
++ return ret;
++}
++
++static int acx_remove(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct net_device *ndev = (dev? dev_get_drvdata(dev) : NULL);
++ wlandevice_t *priv = (ndev? (wlandevice_t *) netdev_priv(ndev) : NULL);
++ struct resource *res = NULL;
++
++ if(!ndev){
++ printk(KERN_ERR "Invalid network device structure\n");
++ return 0;
++ }
++
++ dev_set_drvdata(dev, NULL);
++ unregister_netdev(ndev);
++
++#if 0
++ free_irq(ndev->irq, ndev);
++#endif
++
++ iounmap(priv->iobase);
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ release_mem_region(res->start, SZ_32M);
++
++ free_netdev(ndev);
++
++ return 0;
++}
++
++/***********************************************************************
++*/
++#ifdef CONFIG_PM
++static int if_was_up = 0; /* FIXME: HACK, do it correctly sometime instead */
++static int
++acx_suspend(struct device *dev, pm_message_t state, u32 level)
++{
++#if 0
++ struct net_device *ndev = pci_get_drvdata(pdev);
++ wlandevice_t *priv = netdev_priv(dev);
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ printk("acx: experimental suspend handler called for %p\n", priv);
++ if (netif_device_present(ndev)) {
++ if_was_up = 1;
++ acx_s_down(ndev);
++ }
++ else
++ if_was_up = 0;
++
++ netif_device_detach(ndev); /* This one cannot sleep */
++ acx_s_delete_dma_regions(priv);
++
++ acx_sem_unlock(priv);
++
++ FN_EXIT0;
++#endif
++ return OK;
++}
++
++static int
++acx_resume(struct device *dev, u32 level)
++{
++#if 0
++ struct platform_device *pdev = to_platform_device(dev);
++ struct net_device *ndev;
++ wlandevice_t *priv;
++
++ printk(KERN_WARNING "rsm: resume\n");
++ ndev = dev_get_drvdata(dev);
++ printk(KERN_WARNING "rsm: got dev\n");
++
++ if (!netif_running(ndev))
++ return 0;
++
++ priv = netdev_priv(ndev);
++
++ acx_sem_lock(priv);
++
++ printk(KERN_WARNING "rsm: got priv\n");
++ FN_ENTER;
++ printk("acx: experimental resume handler called for %p!\n", priv);
++ // TODO
++ acxlog(L_DEBUG, "rsm: power state set\n");
++
++ acxlog(L_DEBUG, "rsm: PCI state restored\n");
++ acx_s_reset_dev(ndev);
++ acxlog(L_DEBUG, "rsm: device reset done\n");
++
++ if (OK != acx_s_init_mac(ndev)) {
++ printk("rsm: init_mac FAILED\n");
++ goto fail;
++ }
++ acxlog(L_DEBUG, "rsm: init MAC done\n");
++
++ if (1 == if_was_up)
++ acx_s_up(ndev);
++ acxlog(L_DEBUG, "rsm: acx up\n");
++
++ /* now even reload all card parameters as they were before suspend,
++ * and possibly be back in the network again already :-)
++ * FIXME: should this be done in that scheduled task instead?? */
++ if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
++ acx_s_update_card_settings(priv, 0, 1);
++ acxlog(L_DEBUG, "rsm: settings updated\n");
++ netif_device_attach(ndev);
++ acxlog(L_DEBUG, "rsm: device attached\n");
++fail: /* we need to return OK here anyway, right? */
++ acx_sem_unlock(priv);
++ FN_EXIT0;
++#endif
++ return OK;
++}
++#endif /* CONFIG_PM */
++
++
++/*----------------------------------------------------------------
++* acx_s_up
++*
++* Side effects:
++* - Enables on-card interrupt requests
++* - calls acx_start
++* Call context:
++* - process thread
++* Comment:
++* This function is called by acx_open (when ifconfig sets the
++* device as up).
++*----------------------------------------------------------------*/
++static void
++acx_s_up(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++ acx_l_enable_irq(priv);
++ acx_unlock(priv, flags);
++
++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers
++ ** used to use it. But we don't do that anymore, our OS
++ ** has reliable software timers */
++ init_timer(&priv->mgmt_timer);
++ priv->mgmt_timer.function = acx_i_timer;
++ priv->mgmt_timer.data = (unsigned long)priv;
++
++ /* Need to set ACX_STATE_IFACE_UP first, or else
++ ** timer won't be started by acx_set_status() */
++ SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* actual scan cmd will happen in start() */
++ acx_set_status(priv, ACX_STATUS_1_SCANNING); break;
++ case ACX_MODE_3_AP:
++ case ACX_MODE_MONITOR:
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); break;
++ }
++
++ acx_s_start(priv);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_down
++*
++* Side effects:
++* - disables on-card interrupt request
++* Call context:
++* process thread
++* Comment:
++* this disables the netdevice
++*----------------------------------------------------------------*/
++static void
++acx_s_down(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Disable IRQs first, so that IRQs cannot race with us */
++ acx_lock(priv, flags);
++ acx_l_disable_irq(priv);
++ acx_unlock(priv, flags);
++
++ /* we really don't want to have an asynchronous tasklet disturb us
++ ** after something vital for its job has been shut down, so
++ ** end all remaining work now.
++ **
++ ** NB: carrier_off (done by set_status below) would lead to
++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK().
++ ** That's why we do FLUSH first.
++ **
++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK()
++ ** waits for acx_e_after_interrupt_task to complete if it is running
++ ** on another CPU, but acx_e_after_interrupt_task
++ ** will sleep on sem forever, because it is taken by us!
++ ** Work around that by temporary sem unlock.
++ ** This will fail miserably if we'll be hit by concurrent
++ ** iwconfig or something in between. TODO! */
++ acx_sem_unlock(priv);
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(priv);
++
++ /* This is possible:
++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task ->
++ ** -> set_status(ASSOCIATED) -> wake_queue()
++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK
++ ** lock/unlock is just paranoia, maybe not needed */
++ acx_lock(priv, flags);
++ acx_stop_queue(dev, "during close");
++ acx_set_status(priv, ACX_STATUS_0_STOPPED);
++ acx_unlock(priv, flags);
++
++ /* kernel/timer.c says it's illegal to del_timer_sync()
++ ** a timer which restarts itself. We guarantee this cannot
++ ** ever happen because acx_i_timer() never does this if
++ ** status is ACX_STATUS_0_STOPPED */
++ del_timer_sync(&priv->mgmt_timer);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_open
++*
++* WLAN device open method. Called from p80211netdev when kernel
++* device open (start) method is called in response to the
++* SIOCSIFFLAGS ioctl changing the flags bit IFF_UP
++* from clear to set.
++*
++* Returns:
++* 0 success
++* >0 f/w reported error
++* <0 driver reported error
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static int
++acx_e_open(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result = OK;
++
++ FN_ENTER;
++
++ acxlog(L_INIT, "module count++\n");
++ WLAN_MOD_INC_USE_COUNT;
++
++ acx_sem_lock(priv);
++
++ acx_init_task_scheduler(priv);
++
++ /* request shared IRQ handler */
++ if (request_irq(dev->irq, acx_i_interrupt, SA_SHIRQ, dev->name, dev)) {
++ printk("%s: request_irq FAILED\n", dev->name);
++ result = -EAGAIN;
++ goto done;
++ }
++ acxlog(L_DEBUG|L_IRQ, "request_irq %d successful\n", dev->irq);
++
++ /* ifup device */
++ acx_s_up(dev);
++
++ /* We don't currently have to do anything else.
++ * The setup of the MAC should be subsequently completed via
++ * the mlme commands.
++ * Higher layers know we're ready from dev->start==1 and
++ * dev->tbusy==0. Our rx path knows to pass up received/
++ * frames because of dev->flags&IFF_UP is true.
++ */
++done:
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_close
++*
++* WLAN device close method. Called from network core when kernel
++* device close method is called in response to the
++* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
++* from set to clear.
++* (i.e. called for "ifconfig DEV down")
++*
++* Returns:
++* 0 success
++* >0 f/w reported error
++* <0 driver reported error
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static int
++acx_e_close(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ /* ifdown device */
++ CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++ if (netif_device_present(dev)) {
++ acx_s_down(dev);
++ }
++
++ /* disable all IRQs, release shared IRQ handler */
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff);
++ acx_write_reg16(priv, IO_ACX_FEMR, 0x0);
++ free_irq(dev->irq, dev);
++
++ /* We currently don't have to do anything else.
++ * Higher layers know we're not ready from dev->start==0 and
++ * dev->tbusy==1. Our rx path knows to not pass up received
++ * frames because of dev->flags&IFF_UP is false.
++ */
++ acxlog(L_INIT, "module count--\n");
++ WLAN_MOD_DEC_USE_COUNT;
++
++ acx_sem_unlock(priv);
++
++ acxlog(L_INIT, "closed device\n");
++ FN_EXIT0;
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_i_tx_timeout
++*
++* Called from network core. Must not sleep!
++*----------------------------------------------------------------*/
++static void
++acx_i_tx_timeout(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ unsigned int tx_num_cleaned;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++
++ /* clean processed tx descs, they may have been completely full */
++ tx_num_cleaned = acx_l_clean_tx_desc(priv);
++
++ /* nothing cleaned, yet (almost) no free buffers available?
++ * --> clean all tx descs, no matter which status!!
++ * Note that I strongly suspect that doing emergency cleaning
++ * may confuse the firmware. This is a last ditch effort to get
++ * ANYTHING to work again...
++ *
++ * TODO: it's best to simply reset & reinit hw from scratch...
++ */
++ if ((priv->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) {
++ printk("%s: FAILED to free any of the many full tx buffers. "
++ "Switching to emergency freeing. "
++ "Please report!\n", dev->name);
++ acx_l_clean_tx_desc_emergency(priv);
++ }
++
++ if (acx_queue_stopped(dev) && (ACX_STATUS_4_ASSOCIATED == priv->status))
++ acx_wake_queue(dev, "after tx timeout");
++
++ /* stall may have happened due to radio drift, so recalib radio */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* do unimportant work last */
++ printk("%s: tx timeout!\n", dev->name);
++ priv->stats.tx_errors++;
++
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_get_stats
++*----------------------------------------------------------------*/
++static struct net_device_stats*
++acx_e_get_stats(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ return &priv->stats;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_get_wireless_stats
++*----------------------------------------------------------------*/
++static struct iw_statistics*
++acx_e_get_wireless_stats(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ return &priv->wstats;
++}
++
++
++/*----------------------------------------------------------------
++* acx_i_set_multicast_list
++* FIXME: most likely needs refinement
++*----------------------------------------------------------------*/
++static void
++acx_i_set_multicast_list(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++
++ /* firmwares don't have allmulti capability,
++ * so just use promiscuous mode instead in this case. */
++ if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
++ SET_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ CLEAR_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(priv->set_mask, SET_RXCONFIG);
++ /* let kernel know in case *we* needed to set promiscuous */
++ dev->flags |= (IFF_PROMISC|IFF_ALLMULTI);
++ } else {
++ CLEAR_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ SET_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(priv->set_mask, SET_RXCONFIG);
++ dev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
++ }
++
++ /* cannot update card settings directly here, atomic context */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++static void
++acx_l_update_link_quality_led(wlandevice_t *priv)
++{
++ int qual;
++
++ qual = acx_signal_determine_quality(priv->wstats.qual.level, priv->wstats.qual.noise);
++ if (qual > priv->brange_max_quality)
++ qual = priv->brange_max_quality;
++
++ if (time_after(jiffies, priv->brange_time_last_state_change +
++ (HZ/2 - HZ/2 * (unsigned long) qual/priv->brange_max_quality ) )) {
++ acx_l_power_led(priv, (priv->brange_last_state == 0));
++ priv->brange_last_state ^= 1; /* toggle */
++ priv->brange_time_last_state_change = jiffies;
++ }
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_enable_irq
++*----------------------------------------------------------------*/
++static void
++acx_l_enable_irq(wlandevice_t *priv)
++{
++ FN_ENTER;
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask);
++ acx_write_reg16(priv, IO_ACX_FEMR, 0x8000);
++ priv->irqs_active = 1;
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_disable_irq
++*----------------------------------------------------------------*/
++static void
++acx_l_disable_irq(wlandevice_t *priv)
++{
++ FN_ENTER;
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask_off);
++ acx_write_reg16(priv, IO_ACX_FEMR, 0x0);
++ priv->irqs_active = 0;
++ FN_EXIT0;
++}
++
++/* scan is complete. all frames now on the receive queue are valid */
++#define INFO_SCAN_COMPLETE 0x0001
++#define INFO_WEP_KEY_NOT_FOUND 0x0002
++/* hw has been reset as the result of a watchdog timer timeout */
++#define INFO_WATCH_DOG_RESET 0x0003
++/* failed to send out NULL frame from PS mode notification to AP */
++/* recommended action: try entering 802.11 PS mode again */
++#define INFO_PS_FAIL 0x0004
++/* encryption/decryption process on a packet failed */
++#define INFO_IV_ICV_FAILURE 0x0005
++
++static void
++acx_l_handle_info_irq(wlandevice_t *priv)
++{
++#if ACX_DEBUG
++ static const char * const info_type_msg[] = {
++ "(unknown)",
++ "scan complete",
++ "WEP key not found",
++ "internal watchdog reset was done",
++ "failed to send powersave (NULL frame) notification to AP",
++ "encrypt/decrypt on a packet has failed",
++ "TKIP tx keys disabled",
++ "TKIP rx keys disabled",
++ "TKIP rx: key ID not found",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "TKIP IV value exceeds thresh"
++ };
++#endif
++ acx_read_info_status(priv);
++ acxlog(L_IRQ, "got Info IRQ: status 0x%04X type 0x%04X: %s\n",
++ priv->info_status, priv->info_type,
++ info_type_msg[(priv->info_type >= VEC_SIZE(info_type_msg)) ?
++ 0 : priv->info_type]
++ );
++}
++
++
++/*----------------------------------------------------------------
++* acx_i_interrupt
++*
++* IRQ handler (atomic context, must not sleep, blah, blah)
++*----------------------------------------------------------------*/
++static void
++acx_log_unusual_irq(u16 irqtype) {
++ /*
++ if (!printk_ratelimit())
++ return;
++ */
++
++ printk("acx: got");
++ if (irqtype & HOST_INT_RX_DATA) {
++ printk(" Rx_Data");
++ }
++ /* HOST_INT_TX_COMPLETE */
++ if (irqtype & HOST_INT_TX_XFER) {
++ printk(" Tx_Xfer");
++ }
++ /* HOST_INT_RX_COMPLETE */
++ if (irqtype & HOST_INT_DTIM) {
++ printk(" DTIM");
++ }
++ if (irqtype & HOST_INT_BEACON) {
++ printk(" Beacon");
++ }
++ if (irqtype & HOST_INT_TIMER) {
++ acxlog(L_IRQ, " Timer");
++ }
++ if (irqtype & HOST_INT_KEY_NOT_FOUND) {
++ printk(" Key_Not_Found");
++ }
++ if (irqtype & HOST_INT_IV_ICV_FAILURE) {
++ printk(" IV_ICV_Failure");
++ }
++ /* HOST_INT_CMD_COMPLETE */
++ /* HOST_INT_INFO */
++ if (irqtype & HOST_INT_OVERFLOW) {
++ printk(" Overflow");
++ }
++ if (irqtype & HOST_INT_PROCESS_ERROR) {
++ printk(" Process_Error");
++ }
++ /* HOST_INT_SCAN_COMPLETE */
++ if (irqtype & HOST_INT_FCS_THRESHOLD) {
++ printk(" FCS_Threshold");
++ }
++ if (irqtype & HOST_INT_UNKNOWN) {
++ printk(" Unknown");
++ }
++ printk(" IRQ(s)\n");
++}
++
++static irqreturn_t
++acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ wlandevice_t *priv;
++ unsigned long flags;
++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY;
++ u16 irqtype, unmasked;
++
++ priv = (wlandevice_t *) (((netdevice_t *) dev_id)->priv);
++
++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway.
++ * I am paranoid */
++ acx_lock(priv, flags);
++
++ unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR);
++ if (unlikely(0xffff == unmasked)) {
++ /* 0xffff value hints at missing hardware,
++ * so don't do anything.
++ * FIXME: that's not very clean - maybe we are able to
++ * establish a flag which definitely tells us that some
++ * hardware is absent and which we could check here?
++ * Hmm, but other drivers do the very same thing... */
++ acxlog(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n");
++ goto none;
++ }
++
++ /* We will check only "interesting" IRQ types */
++ irqtype = unmasked & ~priv->irq_mask;
++ if (!irqtype) {
++ /* We are on a shared IRQ line and it wasn't our IRQ */
++ acxlog(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n",
++ unmasked, priv->irq_mask);
++ goto none;
++ }
++
++ /* Done here because IRQ_NONEs taking three lines of log
++ ** drive me crazy */
++ FN_ENTER;
++
++#define IRQ_ITERATE 1
++#if IRQ_ITERATE
++if (jiffies != priv->irq_last_jiffies) {
++ priv->irq_loops_this_jiffy = 0;
++ priv->irq_last_jiffies = jiffies;
++}
++
++/* safety condition; we'll normally abort loop below
++ * in case no IRQ type occurred */
++while (--irqcount) {
++#endif
++ /* ACK all IRQs asap */
++ acx_write_reg16(priv, IO_ACX_IRQ_ACK, 0xffff);
++
++ acxlog(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n",
++ unmasked, priv->irq_mask, irqtype);
++
++ /* Handle most important IRQ types first */
++ if (irqtype & HOST_INT_RX_COMPLETE) {
++ acxlog(L_IRQ, "got Rx_Complete IRQ\n");
++ acx_l_process_rx_desc(priv);
++ }
++ if (irqtype & HOST_INT_TX_COMPLETE) {
++ acxlog(L_IRQ, "got Tx_Complete IRQ\n");
++ /* don't clean up on each Tx complete, wait a bit
++ * unless we're going towards full, in which case
++ * we do it immediately, too (otherwise we might lockup
++ * with a full Tx buffer if we go into
++ * acx_l_clean_tx_desc() at a time when we won't wakeup
++ * the net queue in there for some reason...) */
++ if (priv->tx_free <= TX_START_CLEAN) {
++#if TX_CLEANUP_IN_SOFTIRQ
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_TX_CLEANUP);
++#else
++ acx_l_clean_tx_desc(priv);
++#endif
++ }
++ }
++
++ /* Less frequent ones */
++ if (irqtype & (0
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_SCAN_COMPLETE
++ )) {
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ acxlog(L_IRQ, "got Command_Complete IRQ\n");
++ /* save the state for the running issue_cmd() */
++ SET_BIT(priv->irq_status, HOST_INT_CMD_COMPLETE);
++ }
++ if (irqtype & HOST_INT_INFO) {
++ acx_l_handle_info_irq(priv);
++ }
++ if (irqtype & HOST_INT_SCAN_COMPLETE) {
++ acxlog(L_IRQ, "got Scan_Complete IRQ\n");
++ /* need to do that in process context */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ /* remember that fw is not scanning anymore */
++ SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
++ }
++ }
++
++ /* These we just log, but either they happen rarely
++ * or we keep them masked out */
++ if (irqtype & (0
++ | HOST_INT_RX_DATA
++ /* | HOST_INT_TX_COMPLETE */
++ | HOST_INT_TX_XFER
++ /* | HOST_INT_RX_COMPLETE */
++ | HOST_INT_DTIM
++ | HOST_INT_BEACON
++ | HOST_INT_TIMER
++ | HOST_INT_KEY_NOT_FOUND
++ | HOST_INT_IV_ICV_FAILURE
++ /* | HOST_INT_CMD_COMPLETE */
++ /* | HOST_INT_INFO */
++ | HOST_INT_OVERFLOW
++ | HOST_INT_PROCESS_ERROR
++ /* | HOST_INT_SCAN_COMPLETE */
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ )) {
++ acx_log_unusual_irq(irqtype);
++ }
++
++#if IRQ_ITERATE
++ unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR);
++ irqtype = unmasked & ~priv->irq_mask;
++ /* Bail out if no new IRQ bits or if all are masked out */
++ if (!irqtype)
++ break;
++
++ if (unlikely(++priv->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) {
++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n");
++ /* Looks like card floods us with IRQs! Try to stop that */
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff);
++ /* This will short-circuit all future attempts to handle IRQ.
++ * We cant do much more... */
++ priv->irq_mask = 0;
++ break;
++ }
++}
++#endif
++ /* Routine to perform blink with range */
++ if (unlikely(priv->led_power == 2))
++ acx_l_update_link_quality_led(priv);
++
++/* handled: */
++ /* acx_write_flush(priv); - not needed, last op was read anyway */
++ acx_unlock(priv, flags);
++ FN_EXIT0;
++ return IRQ_HANDLED;
++
++none:
++ acx_unlock(priv, flags);
++ return IRQ_NONE;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_power_led
++*----------------------------------------------------------------*/
++void
++acx_l_power_led(wlandevice_t *priv, int enable)
++{
++ u16 gpio_pled = IS_ACX111(priv) ? 0x0040 : 0x0800;
++
++ /* A hack. Not moving message rate limiting to priv->xxx
++ * (it's only a debug message after all) */
++ static int rate_limit = 0;
++
++ if (rate_limit++ < 3)
++ acxlog(L_IOCTL, "Please report in case toggling the power "
++ "LED doesn't work for your card!\n");
++ if (enable)
++ acx_write_reg16(priv, IO_ACX_GPIO_OUT,
++ acx_read_reg16(priv, IO_ACX_GPIO_OUT) & ~gpio_pled);
++ else
++ acx_write_reg16(priv, IO_ACX_GPIO_OUT,
++ acx_read_reg16(priv, IO_ACX_GPIO_OUT) | gpio_pled);
++}
++
++
++/***********************************************************************
++** Ioctls
++*/
++
++/***********************************************************************
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++#if ACX_DEBUG
++ wlandevice_t *priv = netdev_priv(dev);
++ rxdesc_t *rxdesc;
++ txdesc_t *txdesc;
++ rxhostdesc_t *rxhostdesc;
++ txhostdesc_t *txhostdesc;
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ unsigned long flags;
++ int i;
++ char memmap[0x34];
++ char rxconfig[0x8];
++ char fcserror[0x8];
++ char ratefallback[0x5];
++
++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) )
++ return OK;
++ /* using printk() since we checked debug flag already */
++
++ acx_sem_lock(priv);
++
++ if (!IS_ACX111(priv)) {
++ printk("acx111-specific function called "
++ "with non-acx111 chip, aborting\n");
++ goto end_ok;
++ }
++
++ /* get Acx111 Memory Configuration */
++ memset(&memconf, 0, sizeof(memconf));
++ /* BTW, fails with 12 (Write only) error code.
++ ** Retained for easy testing of issue_cmd error handling :) */
++ acx_s_interrogate(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG);
++
++ /* get Acx111 Queue Configuration */
++ memset(&queueconf, 0, sizeof(queueconf));
++ acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++
++ /* get Acx111 Memory Map */
++ memset(memmap, 0, sizeof(memmap));
++ acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP);
++
++ /* get Acx111 Rx Config */
++ memset(rxconfig, 0, sizeof(rxconfig));
++ acx_s_interrogate(priv, &rxconfig, ACX1xx_IE_RXCONFIG);
++
++ /* get Acx111 fcs error count */
++ memset(fcserror, 0, sizeof(fcserror));
++ acx_s_interrogate(priv, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT);
++
++ /* get Acx111 rate fallback */
++ memset(ratefallback, 0, sizeof(ratefallback));
++ acx_s_interrogate(priv, &ratefallback, ACX1xx_IE_RATE_FALLBACK);
++
++ /* force occurrence of a beacon interrupt */
++ /* TODO: comment why is this necessary */
++ acx_write_reg16(priv, IO_ACX_HINT_TRIG, HOST_INT_BEACON);
++
++ /* dump Acx111 Mem Configuration */
++ printk("dump mem config:\n"
++ "data read: %d, struct size: %d\n"
++ "Number of stations: %1X\n"
++ "Memory block size: %1X\n"
++ "tx/rx memory block allocation: %1X\n"
++ "count rx: %X / tx: %X queues\n"
++ "options %1X\n"
++ "fragmentation %1X\n"
++ "Rx Queue 1 Count Descriptors: %X\n"
++ "Rx Queue 1 Host Memory Start: %X\n"
++ "Tx Queue 1 Count Descriptors: %X\n"
++ "Tx Queue 1 Attributes: %X\n",
++ memconf.len, (int) sizeof(memconf),
++ memconf.no_of_stations,
++ memconf.memory_block_size,
++ memconf.tx_rx_memory_block_allocation,
++ memconf.count_rx_queues, memconf.count_tx_queues,
++ memconf.options,
++ memconf.fragmentation,
++ memconf.rx_queue1_count_descs,
++ acx2cpu(memconf.rx_queue1_host_rx_start),
++ memconf.tx_queue1_count_descs,
++ memconf.tx_queue1_attributes);
++
++ /* dump Acx111 Queue Configuration */
++ printk("dump queue head:\n"
++ "data read: %d, struct size: %d\n"
++ "tx_memory_block_address (from card): %X\n"
++ "rx_memory_block_address (from card): %X\n"
++ "rx1_queue address (from card): %X\n"
++ "tx1_queue address (from card): %X\n"
++ "tx1_queue attributes (from card): %X\n",
++ queueconf.len, (int) sizeof(queueconf),
++ queueconf.tx_memory_block_address,
++ queueconf.rx_memory_block_address,
++ queueconf.rx1_queue_address,
++ queueconf.tx1_queue_address,
++ queueconf.tx1_attributes);
++
++ /* dump Acx111 Mem Map */
++ printk("dump mem map:\n"
++ "data read: %d, struct size: %d\n"
++ "Code start: %X\n"
++ "Code end: %X\n"
++ "WEP default key start: %X\n"
++ "WEP default key end: %X\n"
++ "STA table start: %X\n"
++ "STA table end: %X\n"
++ "Packet template start: %X\n"
++ "Packet template end: %X\n"
++ "Queue memory start: %X\n"
++ "Queue memory end: %X\n"
++ "Packet memory pool start: %X\n"
++ "Packet memory pool end: %X\n"
++ "iobase: %p\n"
++ "iobase2: %p\n",
++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap),
++ *((u32 *)&memmap[0x04]),
++ *((u32 *)&memmap[0x08]),
++ *((u32 *)&memmap[0x0C]),
++ *((u32 *)&memmap[0x10]),
++ *((u32 *)&memmap[0x14]),
++ *((u32 *)&memmap[0x18]),
++ *((u32 *)&memmap[0x1C]),
++ *((u32 *)&memmap[0x20]),
++ *((u32 *)&memmap[0x24]),
++ *((u32 *)&memmap[0x28]),
++ *((u32 *)&memmap[0x2C]),
++ *((u32 *)&memmap[0x30]),
++ priv->iobase,
++ priv->iobase2);
++
++ /* dump Acx111 Rx Config */
++ printk("dump rx config:\n"
++ "data read: %d, struct size: %d\n"
++ "rx config: %X\n"
++ "rx filter config: %X\n",
++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig),
++ *((u16 *)&rxconfig[0x04]),
++ *((u16 *)&rxconfig[0x06]));
++
++ /* dump Acx111 fcs error */
++ printk("dump fcserror:\n"
++ "data read: %d, struct size: %d\n"
++ "fcserrors: %X\n",
++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror),
++ *((u32 *)&fcserror[0x04]));
++
++ /* dump Acx111 rate fallback */
++ printk("dump rate fallback:\n"
++ "data read: %d, struct size: %d\n"
++ "ratefallback: %X\n",
++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback),
++ *((u8 *)&ratefallback[0x04]));
++
++ /* protect against IRQ */
++ acx_lock(priv, flags);
++
++ /* dump acx111 internal rx descriptor ring buffer */
++ rxdesc = priv->rxdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump internal rxdesc %d:\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n"
++ "RxStatus (dynamic) 0x%X\n"
++ "Mod/Pre (dynamic) 0x%X\n",
++ i,
++ rxdesc,
++ acx2cpu(rxdesc->pNextDesc),
++ acx2cpu(rxdesc->ACXMemPtr),
++ rxdesc->Ctl_8,
++ rxdesc->rate,
++ rxdesc->error,
++ rxdesc->SNR);
++ rxdesc++;
++ }
++
++ /* dump host rx descriptor ring buffer */
++
++ rxhostdesc = priv->rxhostdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump host rxdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ rxhostdesc,
++ acx2cpu(rxhostdesc->data_phy),
++ rxhostdesc->data_offset,
++ le16_to_cpu(rxhostdesc->Ctl_16),
++ le16_to_cpu(rxhostdesc->length),
++ acx2cpu(rxhostdesc->desc_phy_next),
++ rxhostdesc->Status);
++ rxhostdesc++;
++ }
++
++ /* dump acx111 internal tx descriptor ring buffer */
++ txdesc = priv->txdesc_start;
++
++ /* loop over complete transmit pool */
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ printk("\ndump internal txdesc %d:\n"
++ "size 0x%X\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "host mem pointer (dynamic) 0x%X\n"
++ "length (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "CTL2 (dynamic) 0x%X\n"
++ "Status (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n",
++ i,
++ (int) sizeof(struct txdesc),
++ txdesc,
++ acx2cpu(txdesc->pNextDesc),
++ acx2cpu(txdesc->AcxMemPtr),
++ acx2cpu(txdesc->HostMemPtr),
++ le16_to_cpu(txdesc->total_length),
++ txdesc->Ctl_8,
++ txdesc->Ctl2_8, txdesc->error,
++ txdesc->u.r1.rate);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++
++ /* dump host tx descriptor ring buffer */
++
++ txhostdesc = priv->txhostdesc_start;
++
++ /* loop over complete host send pool */
++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) {
++ printk("\ndump host txdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ txhostdesc,
++ acx2cpu(txhostdesc->data_phy),
++ txhostdesc->data_offset,
++ le16_to_cpu(txhostdesc->Ctl_16),
++ le16_to_cpu(txhostdesc->length),
++ acx2cpu(txhostdesc->desc_phy_next),
++ le32_to_cpu(txhostdesc->Status));
++ txhostdesc++;
++ }
++
++ /* acx_write_reg16(priv, 0xb4, 0x4); */
++
++ acx_unlock(priv, flags);
++end_ok:
++
++ acx_sem_unlock(priv);
++#endif /* ACX_DEBUG */
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acx100pci_ioctl_set_phy_amp_bias(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ u16 gpio_old;
++
++ if (!IS_ACX100(priv)) {
++ /* WARNING!!!
++ * Removing this check *might* damage
++ * hardware, since we're tweaking GPIOs here after all!!!
++ * You've been warned...
++ * WARNING!!! */
++ printk("acx: sorry, setting bias level for non-acx100 "
++ "is not supported yet\n");
++ return OK;
++ }
++
++ if (*extra > 7) {
++ printk("acx: invalid bias parameter, range is 0-7\n");
++ return -EINVAL;
++ }
++
++ acx_sem_lock(priv);
++
++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]:
++ * IRQ handler uses it to update LED */
++ acx_lock(priv, flags);
++ gpio_old = acx_read_reg16(priv, IO_ACX_GPIO_OUT);
++ acx_write_reg16(priv, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8));
++ acx_unlock(priv, flags);
++
++ acxlog(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old);
++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n",
++ dev->name,
++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra);
++
++ acx_sem_unlock(priv);
++
++ return OK;
++}
++
++
++/***************************************************************
++** acxpci_l_alloc_tx
++** Actually returns a txdesc_t* ptr
++*/
++tx_t*
++acxpci_l_alloc_tx(wlandevice_t* priv)
++{
++ struct txdesc *txdesc;
++ u8 ctl8;
++
++ FN_ENTER;
++
++ txdesc = get_txdesc(priv, priv->tx_head);
++ ctl8 = txdesc->Ctl_8;
++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_DONE))) {
++ /* whoops, descr at current index is not free, so probably
++ * ring buffer already full */
++ /* FIXME: this causes a deadlock situation (endless
++ * loop) in case the current descriptor remains busy,
++ * so handle it a bit better in the future!! */
++ printk("acx: BUG: tx_head->Ctl8=0x%02X, (0x%02X & "
++ "0x"DESC_CTL_DONE_STR") != 0x"DESC_CTL_HOSTOWN_STR
++ ": failed to find free tx descr\n",
++ ctl8, ctl8);
++ txdesc = NULL;
++ goto end;
++ }
++
++ priv->tx_free--;
++ acxlog(L_BUFT, "tx: got desc %u, %u remain\n",
++ priv->tx_head, priv->tx_free);
++
++/*
++ * This comment is probably not entirely correct, needs further discussion
++ * (restored commented-out code below to fix Tx ring buffer overflow,
++ * since it's much better to have a slightly less efficiently used ring
++ * buffer rather than one which easily overflows):
++ *
++ * This doesn't do anything other than limit our maximum number of
++ * buffers used at a single time (we might as well just declare
++ * TX_STOP_QUEUE less descriptors when we open up.) We should just let it
++ * slide here, and back off TX_STOP_QUEUE in acx_l_clean_tx_desc, when given the
++ * opportunity to let the queue start back up.
++ */
++ if (priv->tx_free < TX_STOP_QUEUE) {
++ acxlog(L_BUF, "stop queue (%u tx desc left)\n",
++ priv->tx_free);
++ acx_stop_queue(priv->netdev, NULL);
++ }
++
++ /* returning current descriptor, so advance to next free one */
++ priv->tx_head = (priv->tx_head + 1) % TX_CNT;
++end:
++ FN_EXIT0;
++
++ return (tx_t*)txdesc;
++}
++
++
++/***********************************************************************
++*/
++void*
++acxpci_l_get_txbuf(wlandevice_t *priv, tx_t* tx_opaque)
++{
++ return acx_get_txhostdesc(priv, (txdesc_t*)tx_opaque)->data;
++}
++
++
++/***********************************************************************
++** acxpci_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++*/
++void
++acxpci_l_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int len)
++{
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txhostdesc_t *hostdesc1, *hostdesc2;
++ client_t *clt;
++ u8 Ctl_8, Ctl2_8;
++
++ FN_ENTER;
++
++ /* fw doesn't tx such packets anyhow */
++ if (len < WLAN_HDR_A3_LEN)
++ goto end;
++
++ hostdesc1 = acx_get_txhostdesc(priv, txdesc);
++ hostdesc2 = hostdesc1 + 1;
++
++ /* modify flag status in separate variable to be able to write it back
++ * in one big swoop later (also in order to have less device memory
++ * accesses) */
++ Ctl_8 = txdesc->Ctl_8;
++ Ctl2_8 = txdesc->Ctl2_8;
++
++ /* DON'T simply set Ctl field to 0 here globally,
++ * it needs to maintain a consistent flag status (those are state flags!!),
++ * otherwise it may lead to severe disruption. Only set or reset particular
++ * flags at the exact moment this is needed...
++ * FIXME: what about Ctl2? Equally problematic? */
++
++ /* let chip do RTS/CTS handshaking before sending
++ * in case packet size exceeds threshold */
++ if (len > priv->rts_threshold)
++ SET_BIT(Ctl2_8, DESC_CTL2_RTS);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS);
++
++#ifdef DEBUG_WEP
++ if (priv->wep_enabled)
++ SET_BIT(Ctl2_8, DESC_CTL2_WEP);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_WEP);
++#endif
++
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(priv, ((wlan_hdr_t*)hostdesc1->data)->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = priv->ap_client;
++ break;
++#if 0
++/* testing was done on acx111: */
++ case ACX_MODE_MONITOR:
++ SET_BIT(Ctl2_8, 0
++/* sends CTS to self before packet */
++ + DESC_CTL2_SEQ /* don't increase sequence field */
++/* not working (looks like good fcs is still added) */
++ + DESC_CTL2_FCS /* don't add the FCS */
++/* not tested */
++ + DESC_CTL2_MORE_FRAG
++/* not tested */
++ + DESC_CTL2_RETRY /* don't increase retry field */
++/* not tested */
++ + DESC_CTL2_POWER /* don't increase power mgmt. field */
++/* no effect */
++ + DESC_CTL2_WEP /* encrypt this frame */
++/* not tested */
++ + DESC_CTL2_DUR /* don't increase duration field */
++ );
++ /* fallthrough */
++#endif
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ if (unlikely(clt && !clt->rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* used in tx cleanup routine for auto rate and accounting: */
++ acx_put_txc(priv, txdesc, clt);
++
++ txdesc->total_length = cpu_to_le16(len);
++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN);
++ if (IS_ACX111(priv)) {
++ u16 rate_cur = clt ? clt->rate_cur : priv->rate_bcast;
++ /* note that if !txdesc->do_auto, txrate->cur
++ ** has only one nonzero bit */
++ txdesc->u.r2.rate111 = cpu_to_le16(
++ rate_cur
++ /* WARNING: I was never able to make it work with prism54 AP.
++ ** It was falling down to 1Mbit where shortpre is not applicable,
++ ** and not working at all at "5,11 basic rates only" setting.
++ ** I even didn't see tx packets in radio packet capture.
++ ** Disabled for now --vda */
++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */
++ );
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ /* should add this to rate111 above as necessary */
++ | (clt->pbcc511 ? RATE111_PBCC511 : 0)
++#endif
++ hostdesc1->length = cpu_to_le16(len);
++ } else { /* ACX100 */
++ u8 rate_100 = clt ? clt->rate_100 : priv->rate_bcast100;
++ txdesc->u.r1.rate = rate_100;
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ if (clt->pbcc511) {
++ if (n == RATE100_5 || n == RATE100_11)
++ n |= RATE100_PBCC511;
++ }
++
++ if (clt->shortpre && (clt->cur != RATE111_1))
++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */
++#endif
++ /* set autodma and reclaim and 1st mpdu */
++ SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG);
++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN);
++ }
++ /* don't need to clean ack/rts statistics here, already
++ * done on descr cleanup */
++
++ /* clears Ctl DESC_CTL_HOSTOWN bit, thus telling that the descriptors
++ * are now owned by the acx100; do this as LAST operation */
++ CLEAR_BIT(Ctl_8, DESC_CTL_HOSTOWN);
++ /* flush writes before we release hostdesc to the adapter here */
++ wmb();
++ CLEAR_BIT(hostdesc1->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++ CLEAR_BIT(hostdesc2->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++
++ /* write back modified flags */
++ txdesc->Ctl2_8 = Ctl2_8;
++ txdesc->Ctl_8 = Ctl_8;
++
++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */
++//TODO: should it be a mmiowb() instead? we are protecting against race with write[bwl]()
++ /* flush writes before we tell the adapter that it's its turn now */
++ wmb();
++ acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ acx_write_flush(priv);
++
++ /* log the packet content AFTER sending it,
++ * in order to not delay sending any further than absolutely needed
++ * Do separate logs for acx100/111 to have human-readable rates */
++ if (unlikely(acx_debug & (L_XFER|L_DATA))) {
++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc;
++ if (IS_ACX111(priv))
++ printk("tx: pkt (%s): len %d "
++ "rate %04X%s status %u\n",
++ acx_get_packet_type_string(le16_to_cpu(fc)), len,
++ le16_to_cpu(txdesc->u.r2.rate111),
++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "",
++ priv->status);
++ else
++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n",
++ acx_get_packet_type_string(fc), len,
++ txdesc->u.r1.rate,
++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "",
++ priv->status);
++
++ if (acx_debug & L_DATA) {
++ printk("tx: 802.11 [%d]: ", len);
++ acx_dump_bytes(hostdesc1->data, len);
++ }
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++static void
++acx_l_handle_tx_error(wlandevice_t *priv, u8 error, unsigned int finger)
++{
++ const char *err = "unknown error";
++
++ /* hmm, should we handle this as a mask
++ * of *several* bits?
++ * For now I think only caring about
++ * individual bits is ok... */
++ switch (error) {
++ case 0x01:
++ err = "no Tx due to error in other fragment";
++ priv->wstats.discard.fragment++;
++ break;
++ case 0x02:
++ err = "Tx aborted";
++ priv->stats.tx_aborted_errors++;
++ break;
++ case 0x04:
++ err = "Tx desc wrong parameters";
++ priv->wstats.discard.misc++;
++ break;
++ case 0x08:
++ err = "WEP key not found";
++ priv->wstats.discard.misc++;
++ break;
++ case 0x10:
++ err = "MSDU lifetime timeout? - try changing "
++ "'iwconfig retry lifetime XXX'";
++ priv->wstats.discard.misc++;
++ break;
++ case 0x20:
++ err = "excessive Tx retries due to either distance "
++ "too high or unable to Tx or Tx frame error - "
++ "try changing 'iwconfig txpower XXX' or "
++ "'sens'itivity or 'retry'";
++ priv->wstats.discard.retries++;
++ /* FIXME: set (GETSET_TX|GETSET_RX) here
++ * (this seems to recalib radio on ACX100)
++ * after some more jiffies passed??
++ * But OTOH Tx error 0x20 also seems to occur on
++ * overheating, so I'm not sure whether we
++ * actually want that, since people maybe won't notice
++ * then that their hardware is slowly getting
++ * cooked...
++ * Or is it still a safe long distance from utter
++ * radio non-functionality despite many radio
++ * recalibs
++ * to final destructive overheating of the hardware?
++ * In this case we really should do recalib here...
++ * I guess the only way to find out is to do a
++ * potentially fatal self-experiment :-\
++ * Or maybe only recalib in case we're using Tx
++ * rate auto (on errors switching to lower speed
++ * --> less heat?) or 802.11 power save mode? */
++
++ /* ok, just do it.
++ * ENABLE_TX|ENABLE_RX helps, so even do
++ * DISABLE_TX and DISABLE_RX in order to perhaps
++ * have more impact. */
++ if (++priv->retry_errors_msg_ratelimit % 4 == 0) {
++ if (priv->retry_errors_msg_ratelimit <= 20)
++ printk("%s: several excessive Tx "
++ "retry errors occurred, attempting "
++ "to recalibrate radio. Radio "
++ "drift might be caused by increasing "
++ "card temperature, please check the card "
++ "before it's too late!\n",
++ priv->netdev->name);
++ if (priv->retry_errors_msg_ratelimit == 20)
++ printk("disabling above "
++ "notification message\n");
++
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ break;
++ case 0x40:
++ err = "Tx buffer overflow";
++ priv->stats.tx_fifo_errors++;
++ break;
++ case 0x80:
++ err = "DMA error";
++ priv->wstats.discard.misc++;
++ break;
++ }
++ priv->stats.tx_errors++;
++ if (priv->stats.tx_errors <= 20)
++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n",
++ priv->netdev->name, error, finger, err);
++ else
++ printk("%s: tx error 0x%02X, buf %02u!\n",
++ priv->netdev->name, error, finger);
++}
++
++
++/***********************************************************************
++*/
++/* Theory of operation:
++** client->rate_cap is a bitmask of rates client is capable of.
++** client->rate_cfg is a bitmask of allowed (configured) rates.
++** It is set as a result of iwconfig rate N [auto]
++** or iwpriv set_rates "N,N,N N,N,N" commands.
++** It can be fixed (e.g. 0x0080 == 18Mbit only),
++** auto (0x00ff == 18Mbit or any lower value),
++** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_).
++**
++** client->rate_cur is a value for rate111 field in tx descriptor.
++** It is always set to txrate_cfg sans zero or more most significant
++** bits. This routine handles selection of new rate_cur value depending on
++** outcome of last tx event.
++**
++** client->rate_100 is a precalculated rate value for acx100
++** (we can do without it, but will need to calculate it on each tx).
++**
++** You cannot configure mixed usage of 5.5 and/or 11Mbit rate
++** with PBCC and CCK modulation. Either both at CCK or both at PBCC.
++** In theory you can implement it, but so far it is considered not worth doing.
++**
++** 22Mbit, of course, is PBCC always. */
++
++/* maps acx100 tx descr rate field to acx111 one */
++static u16
++rate100to111(u8 r)
++{
++ switch (r) {
++ case RATE100_1: return RATE111_1;
++ case RATE100_2: return RATE111_2;
++ case RATE100_5:
++ case (RATE100_5 | RATE100_PBCC511): return RATE111_5;
++ case RATE100_11:
++ case (RATE100_11 | RATE100_PBCC511): return RATE111_11;
++ case RATE100_22: return RATE111_22;
++ default:
++ printk("acx: unexpected acx100 txrate: %u! "
++ "Please report\n", r);
++ return RATE111_2;
++ }
++}
++
++
++static void
++acx_l_handle_txrate_auto(wlandevice_t *priv, struct client *txc,
++ unsigned int idx, u8 rate100, u16 rate111, u8 error)
++{
++ u16 sent_rate;
++ u16 cur = txc->rate_cur;
++ int slower_rate_was_used;
++
++ /* FIXME: need to implement some kind of rate success memory
++ * which stores the success percentage per rate, to be taken
++ * into account when considering allowing a new rate, since it
++ * doesn't really help to stupidly count fallback/stepup,
++ * since one invalid rate will spoil the party anyway
++ * (such as 22M in case of 11M-only peers) */
++
++ /* vda: hmm. current code will do this:
++ ** 1. send packets at 11 Mbit, stepup++
++ ** 2. will try to send at 22Mbit. hardware will see no ACK,
++ ** retries at 11Mbit, success. code notes that used rate
++ ** is lower. stepup = 0, fallback++
++ ** 3. repeat step 2 fallback_count times. Fall back to
++ ** 11Mbit. go to step 1.
++ ** If stepup_count is large (say, 16) and fallback_count
++ ** is small (3), this wouldn't be too bad wrt throughput */
++
++ /* do some preparations, i.e. calculate the one rate that was
++ * used to send this packet */
++ if (IS_ACX111(priv)) {
++ sent_rate = 1 << highest_bit(rate111 & RATE111_ALL);
++ } else {
++ sent_rate = rate100to111(rate100);
++ }
++ /* sent_rate has only one bit set now, corresponding to tx rate
++ * which was used by hardware to tx this particular packet */
++
++ /* now do the actual auto rate management */
++ acxlog(L_DEBUG, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X "
++ "__=%u/%u ^^=%u/%u\n",
++ (txc->ignore_count > 0) ? "[IGN] " : "",
++ txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg,
++ txc->fallback_count, priv->fallback_threshold,
++ txc->stepup_count, priv->stepup_threshold
++ );
++
++ /* we need to ignore old packets already in the tx queue since
++ * they use older rate bytes configured before our last rate change,
++ * otherwise our mechanism will get confused by interpreting old data.
++ * Do it here only, in order to have the logging above */
++ if (txc->ignore_count) {
++ txc->ignore_count--;
++ return;
++ }
++
++ /* true only if the only nonzero bit in sent_rate is
++ ** less significant than highest nonzero bit in cur */
++ slower_rate_was_used = ( cur > ((sent_rate<<1)-1) );
++
++ if (slower_rate_was_used || (error & 0x30)) {
++ txc->stepup_count = 0;
++ if (++txc->fallback_count <= priv->fallback_threshold)
++ return;
++ txc->fallback_count = 0;
++
++ /* clear highest 1 bit in cur */
++ sent_rate = RATE111_54;
++ while (!(cur & sent_rate)) sent_rate >>= 1;
++ CLEAR_BIT(cur, sent_rate);
++
++ if (cur) { /* we can't disable all rates! */
++ acxlog(L_XFER, "tx: falling back to ratemask %04X\n", cur);
++ txc->rate_cur = cur;
++ txc->ignore_count = TX_CNT - priv->tx_free;
++ }
++ } else if (!slower_rate_was_used) {
++ txc->fallback_count = 0;
++ if (++txc->stepup_count <= priv->stepup_threshold)
++ return;
++ txc->stepup_count = 0;
++
++ /* sanitize. Sort of not needed, but I dont trust hw that much...
++ ** what if it can report bogus tx rates sometimes? */
++ while (!(cur & sent_rate)) sent_rate >>= 1;
++ /* try to find a higher sent_rate that isn't yet in our
++ * current set, but is an allowed cfg */
++ while (1) {
++ sent_rate <<= 1;
++ if (sent_rate > txc->rate_cfg)
++ /* no higher rates allowed by config */
++ return;
++ if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate))
++ /* found */
++ break;
++ /* not found, try higher one */
++ }
++ SET_BIT(cur, sent_rate);
++ acxlog(L_XFER, "tx: stepping up to ratemask %04X\n", cur);
++ txc->rate_cur = cur;
++ /* FIXME: totally bogus - we could be sending to many peers at once... */
++ txc->ignore_count = TX_CNT - priv->tx_free;
++ }
++
++ /* calculate acx100 style rate byte if needed */
++ if (IS_ACX100(priv)) {
++ txc->rate_100 = bitpos2rate100[highest_bit(cur)];
++ }
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_log_txbuffer
++*----------------------------------------------------------------*/
++#if !ACX_DEBUG
++static inline void acx_l_log_txbuffer(const wlandevice_t *priv) {}
++#else
++static void
++acx_l_log_txbuffer(wlandevice_t *priv)
++{
++ txdesc_t *txdesc;
++ int i;
++
++ /* no FN_ENTER here, we don't want that */
++ /* no locks here, since it's entirely non-critical code */
++ txdesc = priv->txdesc_start;
++ if (!txdesc) return;
++ for (i = 0; i < TX_CNT; i++) {
++ if ((txdesc->Ctl_8 & DESC_CTL_DONE) == DESC_CTL_DONE)
++ printk("tx: buf %d done\n", i);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++}
++#endif
++
++
++/*----------------------------------------------------------------
++* acx_l_clean_tx_desc
++*
++* This function resets the txdescs' status when the ACX100
++* signals the TX done IRQ (txdescs have been processed), starting with
++* the pool index of the descriptor which we would use next,
++* in order to make sure that we can be as fast as possible
++* in filling new txdescs.
++* Oops, now we have our own index, so everytime we get called we know
++* where the next packet to be cleaned is.
++* Hmm, still need to loop through the whole ring buffer now,
++* since we lost sync for some reason when ping flooding or so...
++* (somehow we don't get the IRQ for acx_l_clean_tx_desc any more when
++* too many packets are being sent!)
++* FIXME: currently we only process one packet, but this gets out of
++* sync for some reason when ping flooding, so we need to loop,
++* but the previous smart loop implementation causes the ping latency
++* to rise dramatically (~3000 ms), at least on CardBus PheeNet WL-0022.
++* Dunno what to do :-\
++*----------------------------------------------------------------*/
++unsigned int
++acx_l_clean_tx_desc(wlandevice_t *priv)
++{
++ txdesc_t *txdesc;
++ struct client *txc;
++ int finger;
++ int num_cleaned;
++ int to_process;
++ u16 r111;
++ u8 error, ack_failures, rts_failures, rts_ok, r100;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_DEBUG))
++ acx_l_log_txbuffer(priv);
++
++ acxlog(L_BUFT, "tx: cleaning up bufs from %u\n", priv->tx_tail);
++
++ finger = priv->tx_tail;
++ num_cleaned = 0;
++ to_process = TX_CNT;
++ do {
++ txdesc = get_txdesc(priv, finger);
++
++ /* abort if txdesc is not marked as "Tx finished" and "owned" */
++ if ((txdesc->Ctl_8 & DESC_CTL_DONE) != DESC_CTL_DONE) {
++ /* we do need to have at least one cleaned,
++ * otherwise we wouldn't get called in the first place
++ */
++ if (num_cleaned)
++ break;
++ }
++
++ /* remember descr values... */
++ error = txdesc->error;
++ ack_failures = txdesc->ack_failures;
++ rts_failures = txdesc->rts_failures;
++ rts_ok = txdesc->rts_ok;
++ r100 = txdesc->u.r1.rate;
++ r111 = txdesc->u.r2.rate111;
++
++#if WIRELESS_EXT > 13 /* wireless_send_event() and IWEVTXDROP are WE13 */
++ /* need to check for certain error conditions before we
++ * clean the descriptor: we still need valid descr data here */
++ if (unlikely(0x30 & error)) {
++ /* only send IWEVTXDROP in case of retry or lifetime exceeded;
++ * all other errors mean we screwed up locally */
++ union iwreq_data wrqu;
++ wlan_hdr_t *hdr;
++ txhostdesc_t *hostdesc;
++
++ hostdesc = acx_get_txhostdesc(priv, txdesc);
++ hdr = (wlan_hdr_t *)hostdesc->data;
++ MAC_COPY(wrqu.addr.sa_data, hdr->a1);
++ wireless_send_event(priv->netdev, IWEVTXDROP, &wrqu, NULL);
++ }
++#endif
++ /* ...and free the descr */
++ txdesc->error = 0;
++ txdesc->ack_failures = 0;
++ txdesc->rts_failures = 0;
++ txdesc->rts_ok = 0;
++ /* signal host owning it LAST, since ACX already knows that this
++ * descriptor is finished since it set Ctl_8 accordingly:
++ * if _OWN is set at the beginning instead, our own get_tx
++ * might choose a Tx desc that isn't fully cleared
++ * (in case of bad locking). */
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ priv->tx_free++;
++ num_cleaned++;
++
++ if ((priv->tx_free >= TX_START_QUEUE)
++ && (priv->status == ACX_STATUS_4_ASSOCIATED)
++ && (acx_queue_stopped(priv->netdev))
++ ) {
++ acxlog(L_BUF, "tx: wake queue (avail. Tx desc %u)\n",
++ priv->tx_free);
++ acx_wake_queue(priv->netdev, NULL);
++ }
++
++ /* do error checking, rate handling and logging
++ * AFTER having done the work, it's faster */
++
++ /* do rate handling */
++ txc = acx_get_txc(priv, txdesc);
++ if (txc && priv->rate_auto) {
++ acx_l_handle_txrate_auto(priv, txc, finger, r100, r111, error);
++ }
++
++ if (unlikely(error))
++ acx_l_handle_tx_error(priv, error, finger);
++
++ if (IS_ACX111(priv))
++ acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n",
++ finger, ack_failures, rts_failures, rts_ok, r111);
++ else
++ acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n",
++ finger, ack_failures, rts_failures, rts_ok, r100);
++
++ /* update pointer for descr to be cleaned next */
++ finger = (finger + 1) % TX_CNT;
++ } while (--to_process);
++
++ /* remember last position */
++ priv->tx_tail = finger;
++/* end: */
++ FN_EXIT1(num_cleaned);
++ return num_cleaned;
++}
++
++/* clean *all* Tx descriptors, and regardless of their previous state.
++ * Used for brute-force reset handling. */
++void
++acx_l_clean_tx_desc_emergency(wlandevice_t *priv)
++{
++ txdesc_t *txdesc;
++ unsigned int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc = get_txdesc(priv, i);
++
++ /* free it */
++ txdesc->ack_failures = 0;
++ txdesc->rts_failures = 0;
++ txdesc->rts_ok = 0;
++ txdesc->error = 0;
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ }
++
++ priv->tx_free = TX_CNT;
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_log_rxbuffer
++*
++* Called from IRQ context only
++*----------------------------------------------------------------*/
++#if !ACX_DEBUG
++static inline void acx_l_log_rxbuffer(const wlandevice_t *priv) {}
++#else
++static void
++acx_l_log_rxbuffer(const wlandevice_t *priv)
++{
++ const struct rxhostdesc *rxhostdesc;
++ int i;
++
++ /* no FN_ENTER here, we don't want that */
++
++ rxhostdesc = priv->rxhostdesc_start;
++ if (!rxhostdesc) return;
++ for (i = 0; i < RX_CNT; i++) {
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ printk("rx: buf %d full\n", i);
++ rxhostdesc++;
++ }
++}
++#endif
++
++
++/***************************************************************
++** acx_l_process_rx_desc
++**
++** Called directly and only from the IRQ handler
++*/
++void
++acx_l_process_rx_desc(wlandevice_t *priv)
++{
++ rxhostdesc_t *hostdesc;
++ /* unsigned int curr_idx; */
++ unsigned int count = 0;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_BUFR)) {
++ acx_l_log_rxbuffer(priv);
++ }
++
++ /* First, have a loop to determine the first descriptor that's
++ * full, just in case there's a mismatch between our current
++ * rx_tail and the full descriptor we're supposed to handle. */
++ while (1) {
++ /* curr_idx = priv->rx_tail; */
++ hostdesc = &priv->rxhostdesc_start[priv->rx_tail];
++ priv->rx_tail = (priv->rx_tail + 1) % RX_CNT;
++ if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) {
++ /* found it! */
++ break;
++ }
++ count++;
++ if (unlikely(count > RX_CNT)) {
++ /* hmm, no luck: all descriptors empty, bail out */
++ goto end;
++ }
++ }
++
++ /* now process descriptors, starting with the first we figured out */
++ while (1) {
++ acxlog(L_BUFR, "rx: tail=%u Ctl_16=%04X Status=%08X\n",
++ priv->rx_tail, hostdesc->Ctl_16, hostdesc->Status);
++
++ acx_l_process_rxbuf(priv, hostdesc->data);
++
++ hostdesc->Status = 0;
++ /* flush all writes before adapter sees CTL_HOSTOWN change */
++ wmb();
++ /* Host no longer owns this, needs to be LAST */
++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++
++ /* ok, descriptor is handled, now check the next descriptor */
++ /* curr_idx = priv->rx_tail; */
++ hostdesc = &priv->rxhostdesc_start[priv->rx_tail];
++
++ /* if next descriptor is empty, then bail out */
++ /* FIXME: is this check really entirely correct?? */
++ /*
++//FIXME: inconsistent with check in prev while() loop
++ if (!(hostdesc->Ctl & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) */
++ if (!(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ break;
++
++ priv->rx_tail = (priv->rx_tail + 1) % RX_CNT;
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_create_tx_host_desc_queue
++*----------------------------------------------------------------*/
++static inline void*
++acx_alloc_coherent(struct device *dev, size_t size,
++ dma_addr_t *dma_handle, int flag)
++{
++ printk("Size : %ld\n",size);
++ dev->coherent_dma_mask = 0xffffffff;
++ return dma_alloc_coherent(dev, size, dma_handle, flag);
++}
++
++static void*
++allocate(wlandevice_t *priv, size_t size, dma_addr_t *phy, const char *msg)
++{
++ void *ptr = acx_alloc_coherent(priv->dev, size, phy, GFP_KERNEL);
++ if (ptr) {
++ acxlog(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
++ msg, (int)size, ptr, (unsigned long long)*phy);
++ memset(ptr, 0, size);
++ return ptr;
++ }
++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n",
++ msg, (int)size);
++ return NULL;
++}
++
++static int
++acx_s_create_tx_host_desc_queue(wlandevice_t *priv)
++{
++ txhostdesc_t *hostdesc;
++ u8 *txbuf;
++ dma_addr_t hostdesc_phy;
++ dma_addr_t txbuf_phy;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate TX buffer */
++ priv->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS;
++ priv->txbuf_start = allocate(priv, priv->txbuf_area_size,
++ &priv->txbuf_startphy, "txbuf_start");
++ if (!priv->txbuf_start)
++ goto fail;
++
++ /* allocate the TX host descriptor queue pool */
++ priv->txhostdesc_area_size = TX_CNT * 2*sizeof(txhostdesc_t);
++ priv->txhostdesc_start = allocate(priv, priv->txhostdesc_area_size,
++ &priv->txhostdesc_startphy, "txhostdesc_start");
++ if (!priv->txhostdesc_start)
++ goto fail;
++ /* check for proper alignment of TX host descriptor pool */
++ if ((long) priv->txhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++/* Each tx frame buffer is accessed by hardware via
++** txdesc -> txhostdesc(s) -> framebuffer(s)
++** We use only one txhostdesc per txdesc, but it looks like
++** acx111 is buggy: it accesses second txhostdesc
++** (via hostdesc.desc_phy_next field) even if
++** txdesc->length == hostdesc->length and thus
++** entire packet was placed into first txhostdesc.
++** Due to this bug acx111 hangs unless second txhostdesc
++** has hostdesc.length = 3 (or larger)
++** Storing NULL into hostdesc.desc_phy_next
++** doesn't seem to help.
++*/
++/* It is not known whether we need to have 'extra' second
++** txhostdescs for acx100. Maybe it is acx111-only bug.
++*/
++ hostdesc = priv->txhostdesc_start;
++ hostdesc_phy = priv->txhostdesc_startphy;
++ txbuf = priv->txbuf_start;
++ txbuf_phy = priv->txbuf_startphy;
++
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(txhostdesc_t);
++ if (!(i & 1)) {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* done by memset(0): hostdesc->data_offset = 0; */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_HDR_A3_LEN;
++ txbuf_phy += WLAN_HDR_A3_LEN;
++ } else {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* done by memset(0): hostdesc->data_offset = 0; */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ...; */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ }
++ hostdesc++;
++ }
++ hostdesc--;
++ hostdesc->desc_phy_next = cpu2acx(priv->txhostdesc_startphy);
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_tx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acx_s_create_rx_host_desc_queue
++*/
++/* the whole size of a data buffer (header plus data body)
++ * plus 32 bytes safety offset at the end */
++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32)
++
++static int
++acx_s_create_rx_host_desc_queue(wlandevice_t *priv)
++{
++ rxhostdesc_t *hostdesc;
++ rxbuffer_t *rxbuf;
++ dma_addr_t hostdesc_phy;
++ dma_addr_t rxbuf_phy;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate the RX host descriptor queue pool */
++ priv->rxhostdesc_area_size = RX_CNT * sizeof(rxhostdesc_t);
++ priv->rxhostdesc_start = allocate(priv, priv->rxhostdesc_area_size,
++ &priv->rxhostdesc_startphy, "rxhostdesc_start");
++ if (!priv->rxhostdesc_start)
++ goto fail;
++ /* check for proper alignment of RX host descriptor pool */
++ if ((long) priv->rxhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ /* allocate Rx buffer pool which will be used by the acx
++ * to store the whole content of the received frames in it */
++ priv->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE;
++ priv->rxbuf_start = allocate(priv, priv->rxbuf_area_size,
++ &priv->rxbuf_startphy, "rxbuf_start");
++ if (!priv->rxbuf_start)
++ goto fail;
++
++ rxbuf = priv->rxbuf_start;
++ rxbuf_phy = priv->rxbuf_startphy;
++ hostdesc = priv->rxhostdesc_start;
++ hostdesc_phy = priv->rxhostdesc_startphy;
++
++ /* don't make any popular C programming pointer arithmetic mistakes
++ * here, otherwise I'll kill you...
++ * (and don't dare asking me why I'm warning you about that...) */
++ for (i = 0; i < RX_CNT; i++) {
++ hostdesc->data = rxbuf;
++ hostdesc->data_phy = cpu2acx(rxbuf_phy);
++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE);
++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++ rxbuf++;
++ rxbuf_phy += sizeof(rxbuffer_t);
++ hostdesc_phy += sizeof(rxhostdesc_t);
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc++;
++ }
++ hostdesc--;
++ hostdesc->desc_phy_next = cpu2acx(priv->rxhostdesc_startphy);
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_rx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acx_s_create_hostdesc_queues
++*/
++int
++acx_s_create_hostdesc_queues(wlandevice_t *priv)
++{
++ int result;
++ result = acx_s_create_tx_host_desc_queue(priv);
++ if (OK != result) return result;
++ result = acx_s_create_rx_host_desc_queue(priv);
++ return result;
++}
++
++
++/***************************************************************
++** acx_create_tx_desc_queue
++*/
++static void
++acx_create_tx_desc_queue(wlandevice_t *priv, u32 tx_queue_start)
++{
++ txdesc_t *txdesc;
++ txhostdesc_t *hostdesc;
++ dma_addr_t hostmemptr;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ priv->txdesc_size = sizeof(txdesc_t);
++
++ if (IS_ACX111(priv)) {
++ /* the acx111 txdesc is 4 bytes larger */
++ priv->txdesc_size = sizeof(txdesc_t) + 4;
++ }
++
++#if 0
++ priv->txdesc_start = (txdesc_t *) (priv->iobase2 + tx_queue_start);
++
++ acxlog(L_DEBUG, "priv->iobase2=%p\n"
++ "tx_queue_start=%08X\n"
++ "priv->txdesc_start=%p\n",
++ priv->iobase2,
++ tx_queue_start,
++ priv->txdesc_start);
++
++ priv->tx_free = TX_CNT;
++#endif
++ /* done by memset: priv->tx_head = 0; */
++ /* done by memset: priv->tx_tail = 0; */
++ txdesc = priv->txdesc_start;
++ mem_offs = tx_queue_start;
++ hostmemptr = priv->txhostdesc_startphy;
++ hostdesc = priv->txhostdesc_start;
++
++#if 0
++ if (IS_ACX111(priv)) {
++ /* ACX111 has a preinitialized Tx buffer! */
++ /* loop over whole send pool */
++ /* FIXME: do we have to do the hostmemptr stuff here?? */
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc->HostMemPtr = ptr2acx(hostmemptr);
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ /* reserve two (hdr desc and payload desc) */
++ hostdesc += 2;
++ hostmemptr += 2 * sizeof(txhostdesc_t);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++ } else {
++ /* ACX100 Tx buffer needs to be initialized by us */
++ /* clear whole send pool. sizeof is safe here (we are acx100) */
++ memset(priv->txdesc_start, 0, TX_CNT * sizeof(txdesc_t));
++
++ /* loop over whole send pool */
++ for (i = 0; i < TX_CNT; i++) {
++ acxlog(L_DEBUG, "configure card tx descriptor: 0x%p, "
++ "size: 0x%X\n", txdesc, priv->txdesc_size);
++
++ /* pointer to hostdesc memory */
++ /* FIXME: type-incorrect assignment, might cause trouble
++ * in some cases */
++ txdesc->HostMemPtr = ptr2acx(hostmemptr);
++ /* initialise ctl */
++ txdesc->Ctl_8 = DESC_CTL_INIT;
++ txdesc->Ctl2_8 = 0;
++ /* point to next txdesc */
++ txdesc->pNextDesc = cpu2acx(mem_offs + priv->txdesc_size);
++ /* reserve two (hdr desc and payload desc) */
++ hostdesc += 2;
++ hostmemptr += 2 * sizeof(txhostdesc_t);
++ /* go to the next one */
++ mem_offs += priv->txdesc_size;
++ /* ++ is safe here (we are acx100) */
++ txdesc++;
++ }
++ /* go back to the last one */
++ txdesc--;
++ /* and point to the first making it a ring buffer */
++ txdesc->pNextDesc = cpu2acx(tx_queue_start);
++ }
++#endif
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acx_create_rx_desc_queue
++*/
++static void
++acx_create_rx_desc_queue(wlandevice_t *priv, u32 rx_queue_start)
++{
++ rxdesc_t *rxdesc;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ /* done by memset: priv->rx_tail = 0; */
++
++ /* ACX111 doesn't need any further config: preconfigures itself.
++ * Simply print ring buffer for debugging */
++ if (IS_ACX111(priv)) {
++ /* rxdesc_start already set here */
++
++ priv->rxdesc_start = (rxdesc_t *) ((u8 *)priv->iobase2 + rx_queue_start);
++
++ rxdesc = priv->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ acxlog(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc);
++ rxdesc = priv->rxdesc_start = (rxdesc_t *)
++ (priv->iobase2 + acx2cpu(rxdesc->pNextDesc));
++ }
++ } else {
++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */
++ /* rxdesc_start should be right AFTER Tx pool */
++ priv->rxdesc_start = (rxdesc_t *)
++ ((u8 *) priv->txdesc_start + (TX_CNT * sizeof(txdesc_t)));
++ /* NB: sizeof(txdesc_t) above is valid because we know
++ ** we are in if(acx100) block. Beware of cut-n-pasting elsewhere!
++ ** acx111's txdesc is larger! */
++
++ memset(priv->rxdesc_start, 0, RX_CNT * sizeof(rxdesc_t));
++
++ /* loop over whole receive pool */
++ rxdesc = priv->rxdesc_start;
++ mem_offs = rx_queue_start;
++ for (i = 0; i < RX_CNT; i++) {
++ acxlog(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc);
++ rxdesc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA;
++ /* point to next rxdesc */
++ rxdesc->pNextDesc = cpu2acx(mem_offs + sizeof(rxdesc_t));
++ /* go to the next one */
++ mem_offs += sizeof(rxdesc_t);
++ rxdesc++;
++ }
++ /* go to the last one */
++ rxdesc--;
++
++ /* and point to the first making it a ring buffer */
++ rxdesc->pNextDesc = cpu2acx(rx_queue_start);
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acx_create_desc_queues
++*/
++void
++acx_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start)
++{
++ acx_create_tx_desc_queue(priv, tx_queue_start);
++ acx_create_rx_desc_queue(priv, rx_queue_start);
++}
++
++
++/***************************************************************
++** acxpci_s_proc_diag_output
++*/
++char*
++acxpci_s_proc_diag_output(char *p, wlandevice_t *priv)
++{
++ const char *rtl, *thd, *ttl;
++ rxhostdesc_t *rxhostdesc;
++ txdesc_t *txdesc;
++ int i;
++
++ FN_ENTER;
++
++ p += sprintf(p, "** Rx buf **\n");
++ rxhostdesc = priv->rxhostdesc_start;
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ rtl = (i == priv->rx_tail) ? " [tail]" : "";
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)) )
++ p += sprintf(p, "%02u FULL%s\n", i, rtl);
++ else
++ p += sprintf(p, "%02u empty%s\n", i, rtl);
++ rxhostdesc++;
++ }
++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", priv->tx_free,
++ acx_queue_stopped(priv->netdev) ? "STOPPED" : "running");
++ txdesc = priv->txdesc_start;
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ thd = (i == priv->tx_head) ? " [head]" : "";
++ ttl = (i == priv->tx_tail) ? " [tail]" : "";
++ if (txdesc->Ctl_8 & DESC_CTL_ACXDONE)
++ p += sprintf(p, "%02u DONE (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ else
++ if (!(txdesc->Ctl_8 & DESC_CTL_HOSTOWN))
++ p += sprintf(p, "%02u TxWait (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ else
++ p += sprintf(p, "%02u empty (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++ p += sprintf(p,
++ "\n"
++ "** PCI data **\n"
++ "txbuf_start %p, txbuf_area_size %u, txbuf_startphy %08llx\n"
++ "txdesc_size %u, txdesc_start %p\n"
++ "txhostdesc_start %p, txhostdesc_area_size %u, txhostdesc_startphy %08llx\n"
++ "rxdesc_start %p\n"
++ "rxhostdesc_start %p, rxhostdesc_area_size %u, rxhostdesc_startphy %08llx\n"
++ "rxbuf_start %p, rxbuf_area_size %u, rxbuf_startphy %08llx\n",
++ priv->txbuf_start, priv->txbuf_area_size, (u64)priv->txbuf_startphy,
++ priv->txdesc_size, priv->txdesc_start,
++ priv->txhostdesc_start, priv->txhostdesc_area_size, (u64)priv->txhostdesc_startphy,
++ priv->rxdesc_start,
++ priv->rxhostdesc_start, priv->rxhostdesc_area_size, (u64)priv->rxhostdesc_startphy,
++ priv->rxbuf_start, priv->rxbuf_area_size, (u64)priv->rxbuf_startphy);
++
++ FN_EXIT0;
++ return p;
++}
++
++
++/***********************************************************************
++*/
++int
++acx_proc_eeprom_output(char *buf, wlandevice_t *priv)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < 0x400; i++) {
++ acx_read_eeprom_offset(priv, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++void
++acx_set_interrupt_mask(wlandevice_t *priv)
++{
++ if (IS_ACX111(priv)) {
++ priv->irq_mask = (u16) ~(0
++ /* | HOST_INT_RX_DATA */
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ | HOST_INT_RX_COMPLETE
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ | HOST_INT_IV_ICV_FAILURE
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ | HOST_INT_FCS_THRESHOLD
++ /* | HOST_INT_UNKNOWN */
++ );
++ priv->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */
++ } else {
++ priv->irq_mask = (u16) ~(0
++ /* | HOST_INT_RX_DATA */
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ | HOST_INT_RX_COMPLETE
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ /* | HOST_INT_IV_ICV_FAILURE */
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ /* | HOST_INT_FCS_THRESHOLD */
++ /* | HOST_INT_UNKNOWN */
++ );
++ priv->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */
++ }
++}
++
++
++/***********************************************************************
++*/
++int
++acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
++{
++ /* since it can be assumed that at least the Maxim radio has a
++ * maximum power output of 20dBm and since it also can be
++ * assumed that these values drive the DAC responsible for
++ * setting the linear Tx level, I'd guess that these values
++ * should be the corresponding linear values for a dBm value,
++ * in other words: calculate the values from that formula:
++ * Y [dBm] = 10 * log (X [mW])
++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm)
++ * and you're done...
++ * Hopefully that's ok, but you never know if we're actually
++ * right... (especially since Windows XP doesn't seem to show
++ * actual Tx dBm values :-P) */
++
++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the
++ * values are EXACTLY mW!!! Not sure about RFMD and others,
++ * though... */
++ static const u8 dbm2val_maxim[21] = {
++ 63, 63, 63, 62,
++ 61, 61, 60, 60,
++ 59, 58, 57, 55,
++ 53, 50, 47, 43,
++ 38, 31, 23, 13,
++ 0
++ };
++ static const u8 dbm2val_rfmd[21] = {
++ 0, 0, 0, 1,
++ 2, 2, 3, 3,
++ 4, 5, 6, 8,
++ 10, 13, 16, 20,
++ 25, 32, 41, 50,
++ 63
++ };
++ const u8 *table;
++
++ switch (priv->radio_type) {
++ case RADIO_MAXIM_0D:
++ table = &dbm2val_maxim[0];
++ break;
++ case RADIO_RFMD_11:
++ case RADIO_RALINK_15:
++ table = &dbm2val_rfmd[0];
++ break;
++ default:
++ printk("%s: unknown/unsupported radio type, "
++ "cannot modify tx power level yet!\n",
++ priv->netdev->name);
++ return NOT_OK;
++ }
++ printk("%s: changing radio power level to %u dBm (%u)\n",
++ priv->netdev->name, level_dbm, table[level_dbm]);
++ acxpci_s_write_phy_reg(priv, 0x11, table[level_dbm]);
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_init_module
++*
++* Module initialization routine, called once at module load time.
++*
++* Returns:
++* 0 - success
++* ~0 - failure, module is unloaded.
++*
++* Call context:
++* process thread (insmod or modprobe)
++----------------------------------------------------------------*/
++int __init
++acxcfi_e_init_module(void)
++{
++ int res;
++
++ FN_ENTER;
++
++ printk("acx: compiled to use 16bit I/O access only "
++ "(compatibility mode)\n");
++
++ acxlog(L_INIT, "running on a little-endian CPU\n");
++
++ acxlog(L_INIT,
++ "Compact Flash module " WLAN_RELEASE " initialized, "
++ "waiting for cards to probe...\n");
++
++ /*
++ * Now let configure device
++ * GPIOs
++ */
++ pca9535_gpio_direction(GPIO6, GPIO_OUTPUT);
++ pca9535_gpio_direction(GPIO12, GPIO_OUTPUT);
++
++ if ((omap_request_gpio(11)) < 0) {
++ printk("Error requesting gpio 11\n");
++ return -EIO;
++ }
++ omap_set_gpio_direction (11, 1);
++ omap_set_gpio_dataout (11, 1);
++
++ res = driver_register(&acx_driver);
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/*----------------------------------------------------------------
++* acx_cleanup_module
++*
++* Called at module unload time. This is our last chance to
++* clean up after ourselves.
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++void __exit
++acxcfi_e_cleanup_module(void)
++{
++ struct net_device *dev;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Since the whole module is about to be unloaded,
++ * we recursively shutdown all cards we handled instead
++ * of doing it in remove_pci() (which will be activated by us
++ * via pci_unregister_driver at the end).
++ * remove_pci() might just get called after a card eject,
++ * that's why hardware operations have to be done here instead
++ * when the hardware is available. */
++
++ down(&root_acx_dev_sem);
++
++ dev = root_acx_dev.newest;
++ while (dev != NULL) {
++ /* doh, netdev_priv() doesn't have const! */
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ /* disable both Tx and Rx to shut radio down properly */
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++
++#ifdef REDUNDANT
++ /* put the eCPU to sleep to save power
++ * Halting is not possible currently,
++ * since not supported by all firmware versions */
++ acx_s_issue_cmd(priv, ACX100_CMD_SLEEP, NULL, 0);
++#endif
++ acx_lock(priv, flags);
++
++ /* disable power LED to save power :-) */
++ acxlog(L_INIT, "switching off power LED to save power :-)\n");
++ acx_l_power_led(priv, 0);
++
++ /* stop our eCPU */
++ if (IS_ACX111(priv)) {
++ /* FIXME: does this actually keep halting the eCPU?
++ * I don't think so...
++ */
++ acx_l_reset_mac(priv);
++ } else {
++ u16 temp;
++
++ /* halt eCPU */
++ temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1;
++ acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp);
++ acx_write_flush(priv);
++ }
++
++ acx_unlock(priv, flags);
++
++ acx_sem_unlock(priv);
++
++ dev = priv->prev_nd;
++ }
++
++ up(&root_acx_dev_sem);
++
++ omap_free_gpio(11);
++ driver_unregister(&acx_driver);
++
++ FN_EXIT0;
++}
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/common.c bt_kernel/drivers/net/wireless/tiacx/common.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/common.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/common.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,6590 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/proc_fs.h>
++#include <linux/if_arp.h>
++#include <linux/rtnetlink.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#include <linux/pm.h>
++#include <linux/vmalloc.h>
++#include <asm/bug.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif /* WE >= 13 */
++
++#include "acx.h"
++
++
++#define rdtscl(a)
++
++/***********************************************************************
++*/
++static client_t *acx_l_sta_list_alloc(wlandevice_t *priv);
++static client_t *acx_l_sta_list_get_from_hash(wlandevice_t *priv, const u8 *address);
++
++static int acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf);
++static int acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf);
++/* static int acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala); */
++static int acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf);
++static void acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req);
++static void acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req);
++static void acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req);
++static void acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req);
++static int acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req, const rxbuffer_t *rxbuf);
++static int acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req);
++static int acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req);
++static int acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req);
++static int acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req);
++static int acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req);
++static int acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason);
++static int acx_l_transmit_authen1(wlandevice_t *priv);
++static int acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req, client_t *clt);
++static int acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req);
++static int acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req);
++static int acx_l_transmit_assoc_req(wlandevice_t *priv);
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG
++unsigned int acx_debug = L_ASSOC|L_INIT;
++#endif
++#if USE_FW_LOADER_LEGACY
++static char *firmware_dir;
++#endif
++#if SEPARATE_DRIVER_INSTANCES
++static int card;
++#endif
++
++/* introduced earlier than 2.6.10, but takes more memory, so don't use it
++ * if there's no compile warning by kernel */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
++
++#if ACX_DEBUG
++/* parameter is 'debug', corresponding var is acx_debug */
++module_param_named(debug, acx_debug, uint, 0);
++#endif
++#if USE_FW_LOADER_LEGACY
++module_param(firmware_dir, charp, 0);
++#endif
++
++#else
++
++#if ACX_DEBUG
++/* doh, 2.6.x screwed up big time: here the define has its own ";"
++ * ("double ; detected"), yet in 2.4.x it DOESN'T (the sane thing to do),
++ * grrrrr! */
++MODULE_PARM(acx_debug, "i");
++#endif
++#if USE_FW_LOADER_LEGACY
++MODULE_PARM(firmware_dir, "s");
++#endif
++
++#endif
++
++#if ACX_DEBUG
++MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)");
++#endif
++#if USE_FW_LOADER_LEGACY
++MODULE_PARM_DESC(firmware_dir, "Directory to load acx100 firmware files from");
++#endif
++#if SEPARATE_DRIVER_INSTANCES
++MODULE_PARM(card, "i");
++MODULE_PARM_DESC(card, "Associate only with card-th acx100 card from this driver instance");
++#endif
++
++/* Shoundn't be needed now, acx.firmware_dir= should work */
++#if 0 /* USE_FW_LOADER_LEGACY */
++static int __init
++acx_get_firmware_dir(const char *str)
++{
++ /* I've seen other drivers just pass the string pointer,
++ * so hopefully that's safe */
++ firmware_dir = str;
++ return OK;
++}
++__setup("acx_firmware_dir=", acx_get_firmware_dir);
++#endif
++
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("Dual MPL/GPL");
++#endif
++/* USB had this: MODULE_AUTHOR("Martin Wawro <martin.wawro AT uni-dortmund.de>"); */
++MODULE_AUTHOR("ACX100 Open Source Driver development team");
++MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)");
++
++
++/***********************************************************************
++*/
++/* Probably a number of acx's itermediate buffers for USB transfers,
++** not to be confused with number of descriptors in tx/rx rings
++** (which are not directly accessible to host in USB devices) */
++#define USB_RX_CNT 10
++#define USB_TX_CNT 10
++
++
++/***********************************************************************
++*/
++
++/* minutes to wait until next radio recalibration: */
++#define RECALIB_PAUSE 5
++
++const u8 reg_domain_ids[] =
++ { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 };
++/* stupid workaround for the fact that in C the size of an external array
++ * cannot be determined from within a second file */
++const u8 reg_domain_ids_len = sizeof(reg_domain_ids);
++static const u16 reg_domain_channel_masks[] =
++ { 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc };
++
++
++/***********************************************************************
++** Debugging support
++*/
++#ifdef PARANOID_LOCKING
++static unsigned max_lock_time;
++static unsigned max_sem_time;
++
++void
++acx_lock_unhold() { max_lock_time = 0; }
++void
++acx_sem_unhold() { max_sem_time = 0; }
++
++static inline const char*
++sanitize_str(const char *s)
++{
++ const char* t = strrchr(s, '/');
++ if (t) return t + 1;
++ return s;
++}
++
++void
++acx_lock_debug(wlandevice_t *priv, const char* where)
++{
++ int count = 100*1000*1000;
++ where = sanitize_str(where);
++ while (--count) {
++ if (!spin_is_locked(&priv->lock)) break;
++ cpu_relax();
++ }
++ if (!count) {
++ printk(KERN_EMERG "LOCKUP: already taken at %s!\n", priv->last_lock);
++ BUG();
++ }
++ priv->last_lock = where;
++ rdtscl(priv->lock_time);
++}
++void
++acx_unlock_debug(wlandevice_t *priv, const char* where)
++{
++#ifdef SMP
++ if (!spin_is_locked(&priv->lock)) {
++ where = sanitize_str(where);
++ printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where);
++ BUG();
++ }
++#endif
++ if (acx_debug & L_LOCK) {
++ unsigned diff;
++ rdtscl(diff);
++ diff -= priv->lock_time;
++ if (diff > max_lock_time) {
++ where = sanitize_str(where);
++ printk("max lock hold time %d CPU ticks from %s "
++ "to %s\n", diff, priv->last_lock, where);
++ max_lock_time = diff;
++ }
++ }
++}
++void
++acx_down_debug(wlandevice_t *priv, const char* where)
++{
++ int sem_count;
++ int count = 5000/5;
++ where = sanitize_str(where);
++
++ while (--count) {
++ sem_count = atomic_read(&priv->sem.count);
++ if (sem_count) break;
++ msleep(5);
++ }
++ if (!count) {
++ printk(KERN_EMERG "D STATE at %s! last sem at %s\n",
++ where, priv->last_sem);
++ dump_stack();
++ }
++ priv->last_sem = where;
++ priv->sem_time = jiffies;
++ down(&priv->sem);
++ if (acx_debug & L_LOCK) {
++ printk("%s: sem_down %d -> %d\n",
++ where, sem_count, atomic_read(&priv->sem.count));
++ }
++}
++void
++acx_up_debug(wlandevice_t *priv, const char* where)
++{
++ int sem_count = atomic_read(&priv->sem.count);
++ if (sem_count) {
++ where = sanitize_str(where);
++ printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count);
++ dump_stack();
++ }
++ if (acx_debug & L_LOCK) {
++ unsigned diff = jiffies - priv->sem_time;
++ if (diff > max_sem_time) {
++ where = sanitize_str(where);
++ printk("max sem hold time %d jiffies from %s "
++ "to %s\n", diff, priv->last_sem, where);
++ max_sem_time = diff;
++ }
++ }
++ up(&priv->sem);
++ if (acx_debug & L_LOCK) {
++ where = sanitize_str(where);
++ printk("%s: sem_up %d -> %d\n",
++ where, sem_count, atomic_read(&priv->sem.count));
++ }
++}
++#endif /* PARANOID_LOCKING */
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG > 1
++
++static int acx_debug_func_indent;
++#define DEBUG_TSC 0
++#define FUNC_INDENT_INCREMENT 2
++
++#if DEBUG_TSC
++#define TIMESTAMP(d) unsigned long d; rdtscl(d)
++#else
++#define TIMESTAMP(d) unsigned long d = jiffies
++#endif
++
++static const char
++spaces[] = " " " "; /* Nx10 spaces */
++
++void
++log_fn_enter(const char *funcname)
++{
++ int indent;
++ TIMESTAMP(d);
++
++ indent = acx_debug_func_indent;
++ if (indent >= sizeof(spaces))
++ indent = sizeof(spaces)-1;
++
++ printk("%08ld %s==> %s\n",
++ d % 100000000,
++ spaces + (sizeof(spaces)-1) - indent,
++ funcname
++ );
++
++ acx_debug_func_indent += FUNC_INDENT_INCREMENT;
++}
++void
++log_fn_exit(const char *funcname)
++{
++ int indent;
++ TIMESTAMP(d);
++
++ acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
++
++ indent = acx_debug_func_indent;
++ if (indent >= sizeof(spaces))
++ indent = sizeof(spaces)-1;
++
++ printk("%08ld %s<== %s\n",
++ d % 100000000,
++ spaces + (sizeof(spaces)-1) - indent,
++ funcname
++ );
++}
++void
++log_fn_exit_v(const char *funcname, int v)
++{
++ int indent;
++ TIMESTAMP(d);
++
++ acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
++
++ indent = acx_debug_func_indent;
++ if (indent >= sizeof(spaces))
++ indent = sizeof(spaces)-1;
++
++ printk("%08ld %s<== %s: %08X\n",
++ d % 100000000,
++ spaces + (sizeof(spaces)-1) - indent,
++ funcname,
++ v
++ );
++}
++#endif /* ACX_DEBUG > 1 */
++
++
++/***********************************************************************
++** Basically a msleep with logging
++*/
++void
++acx_s_msleep(int ms)
++{
++ FN_ENTER;
++ msleep(ms);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** Not inlined: it's larger than it seems
++*/
++void
++acx_print_mac(const char *head, const u8 *mac, const char *tail)
++{
++ printk("%s"MACSTR"%s", head, MAC(mac), tail);
++}
++
++
++/***********************************************************************
++** acx_get_status_name
++*/
++static const char*
++acx_get_status_name(u16 status)
++{
++ static const char * const str[] = {
++ "STOPPED", "SCANNING", "WAIT_AUTH",
++ "AUTHENTICATED", "ASSOCIATED", "INVALID??"
++ };
++ return str[(status < VEC_SIZE(str)) ? status : VEC_SIZE(str)-1];
++}
++
++
++/***********************************************************************
++** acx_get_packet_type_string
++*/
++#if ACX_DEBUG
++const char*
++acx_get_packet_type_string(u16 fc)
++{
++ static const char * const mgmt_arr[] = {
++ "MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq",
++ "MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp",
++ "MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM",
++ "MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen"
++ };
++ static const char * const ctl_arr[] = {
++ "CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd",
++ "CTL/CFEndCFAck"
++ };
++ static const char * const data_arr[] = {
++ "DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll",
++ "DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck",
++ "DATA/CFPoll", "DATA/CFAck/CFPoll"
++ };
++ const char *str = "UNKNOWN";
++ u8 fstype = (WF_FC_FSTYPE & fc) >> 4;
++ u8 ctl;
++
++ switch (WF_FC_FTYPE & fc) {
++ case WF_FTYPE_MGMT:
++ str = "MGMT/UNKNOWN";
++ if (fstype < VEC_SIZE(mgmt_arr))
++ str = mgmt_arr[fstype];
++ break;
++ case WF_FTYPE_CTL:
++ ctl = fstype - 0x0a;
++ str = "CTL/UNKNOWN";
++ if (ctl < VEC_SIZE(ctl_arr))
++ str = ctl_arr[ctl];
++ break;
++ case WF_FTYPE_DATA:
++ str = "DATA/UNKNOWN";
++ if (fstype < VEC_SIZE(data_arr))
++ str = data_arr[fstype];
++ break;
++ }
++ return str;
++}
++#endif
++
++
++/***********************************************************************
++** acx_cmd_status_str
++*/
++const char*
++acx_cmd_status_str(unsigned int state)
++{
++ static const char * const cmd_error_strings[] = {
++ "Idle",
++ "Success",
++ "Unknown Command",
++ "Invalid Information Element",
++ "Channel rejected",
++ "Channel invalid in current regulatory domain",
++ "MAC invalid",
++ "Command rejected (read-only information element)",
++ "Command rejected",
++ "Already asleep",
++ "TX in progress",
++ "Already awake",
++ "Write only",
++ "RX in progress",
++ "Invalid parameter",
++ "Scan in progress",
++ "Failed"
++ };
++ return state < VEC_SIZE(cmd_error_strings) ?
++ cmd_error_strings[state] : "UNKNOWN REASON";
++}
++
++
++/***********************************************************************
++** get_status_string
++*/
++static const char*
++get_status_string(unsigned int status)
++{
++ /* A bit shortened, but hopefully still understandable */
++ static const char * const status_str[] = {
++ /* 0 */ "Successful",
++ /* 1 */ "Unspecified failure",
++ /* 2 */ "reserved",
++ /* 3 */ "reserved",
++ /* 4 */ "reserved",
++ /* 5 */ "reserved",
++ /* 6 */ "reserved",
++ /* 7 */ "reserved",
++ /* 8 */ "reserved",
++ /* 9 */ "reserved",
++ /*10 */ "Cannot support all requested capabilities in Capability Information field",
++ /*11 */ "Reassoc denied (reason outside of 802.11b scope)",
++ /*12 */ "Assoc denied (reason outside of 802.11b scope), maybe MAC filtering by peer?",
++ /*13 */ "Responding station doesnt support specified auth algorithm",
++ /*14 */ "Auth rejected: wrong transaction sequence number",
++ /*15 */ "Auth rejected: challenge failure",
++ /*16 */ "Auth rejected: timeout for next frame in sequence",
++ /*17 */ "Assoc denied: too many STAs on this AP",
++ /*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set",
++ /*19 */ "Assoc denied: requesting STA doesnt support Short Preamble",
++ /*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation",
++ /*21 */ "Assoc denied: requesting STA doesnt support Channel Agility"
++ /*22 */ "reserved",
++ /*23 */ "reserved",
++ /*24 */ "reserved",
++ /*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time",
++ /*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM"
++ };
++
++ return status_str[status < VEC_SIZE(status_str) ? status : 2];
++}
++
++
++/***********************************************************************
++*/
++void
++acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr)
++{
++ acxlog(L_ASSOC, "acx: unknown EID %d in mgmt frame at offset %d\n",
++ ie_ptr->eid, (int) ((u8*)ie_ptr - (u8*)hdr));
++ if (acx_debug & (L_DATA|L_ASSOC)) {
++ printk("frame (%s): ",
++ acx_get_packet_type_string(le16_to_cpu(hdr->fc)));
++ acx_dump_bytes(hdr, len);
++ }
++}
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG
++void
++acx_dump_bytes(const void *data, int num)
++{
++ const u8* ptr = (const u8*)data;
++
++ if (num <= 0) {
++ printk("\n");
++ return;
++ }
++
++ while (num >= 16) {
++ printk( "%02X %02X %02X %02X %02X %02X %02X %02X "
++ "%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ ptr[0], ptr[1], ptr[2], ptr[3],
++ ptr[4], ptr[5], ptr[6], ptr[7],
++ ptr[8], ptr[9], ptr[10], ptr[11],
++ ptr[12], ptr[13], ptr[14], ptr[15]);
++ num -= 16;
++ ptr += 16;
++ }
++ if (num > 0) {
++ while (--num > 0)
++ printk("%02X ", *ptr++);
++ printk("%02X\n", *ptr);
++ }
++}
++#endif
++
++
++/***********************************************************************
++** maps acx111 tx descr rate field to acx100 one
++*/
++const u8
++bitpos2rate100[] = {
++ RATE100_1 ,/* 0 */
++ RATE100_2 ,/* 1 */
++ RATE100_5 ,/* 2 */
++ RATE100_2 ,/* 3, should not happen */
++ RATE100_2 ,/* 4, should not happen */
++ RATE100_11 ,/* 5 */
++ RATE100_2 ,/* 6, should not happen */
++ RATE100_2 ,/* 7, should not happen */
++ RATE100_22 ,/* 8 */
++ RATE100_2 ,/* 9, should not happen */
++ RATE100_2 ,/* 10, should not happen */
++ RATE100_2 ,/* 11, should not happen */
++ RATE100_2 ,/* 12, should not happen */
++ RATE100_2 ,/* 13, should not happen */
++ RATE100_2 ,/* 14, should not happen */
++ RATE100_2 ,/* 15, should not happen */
++};
++
++u8
++acx_rate111to100(u16 r) {
++ return bitpos2rate100[highest_bit(r)];
++}
++
++
++/***********************************************************************
++** Calculate level like the feb 2003 windows driver seems to do
++*/
++static u8
++acx_signal_to_winlevel(u8 rawlevel)
++{
++ /* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */
++ u8 winlevel = ((4 + (rawlevel * 5)) / 8);
++
++ if (winlevel > 100)
++ winlevel = 100;
++ return winlevel;
++}
++
++u8
++acx_signal_determine_quality(u8 signal, u8 noise)
++{
++ int qual;
++
++ qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2;
++
++ if (qual > 100)
++ return 100;
++ if (qual < 0)
++ return 0;
++ return qual;
++}
++
++
++/***********************************************************************
++** Interrogate/configure commands
++*/
++static const u16
++CtlLength[] = {
++ 0,
++ ACX100_IE_ACX_TIMER_LEN,
++ ACX1xx_IE_POWER_MGMT_LEN,
++ ACX1xx_IE_QUEUE_CONFIG_LEN,
++ ACX100_IE_BLOCK_SIZE_LEN,
++ ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN,
++ ACX1xx_IE_RATE_FALLBACK_LEN,
++ ACX100_IE_WEP_OPTIONS_LEN,
++ ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */
++ 0,
++ ACX1xx_IE_ASSOC_ID_LEN,
++ 0,
++ ACX111_IE_CONFIG_OPTIONS_LEN,
++ ACX1xx_IE_FWREV_LEN,
++ ACX1xx_IE_FCS_ERROR_COUNT_LEN,
++ ACX1xx_IE_MEDIUM_USAGE_LEN,
++ ACX1xx_IE_RXCONFIG_LEN,
++ 0,
++ 0,
++ ACX1xx_IE_FIRMWARE_STATISTICS_LEN,
++ 0,
++ ACX1xx_IE_FEATURE_CONFIG_LEN,
++ ACX111_IE_KEY_CHOOSE_LEN,
++};
++
++static const u16
++CtlLengthDot11[] = {
++ 0,
++ ACX1xx_IE_DOT11_STATION_ID_LEN,
++ 0,
++ ACX100_IE_DOT11_BEACON_PERIOD_LEN,
++ ACX1xx_IE_DOT11_DTIM_PERIOD_LEN,
++ ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN,
++ ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN,
++ ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN,
++ ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN,
++ 0,
++ ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN,
++ ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN,
++ 0,
++ ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN,
++ ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN,
++ ACX100_IE_DOT11_ED_THRESHOLD_LEN,
++ ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN,
++ 0,
++ 0,
++ 0,
++};
++
++#undef FUNC
++#define FUNC "configure"
++#if !ACX_DEBUG
++int
++acx_s_configure(wlandevice_t *priv, void *pdr, int type)
++{
++#else
++int
++acx_s_configure_debug(wlandevice_t *priv, void *pdr, int type, const char* typestr)
++{
++#endif
++ u16 len;
++ int res;
++
++ if (type < 0x1000)
++ len = CtlLength[type];
++ else
++ len = CtlLengthDot11[type - 0x1000];
++
++ acxlog(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len);
++ if (unlikely(!len)) {
++ acxlog(L_DEBUG, "zero-length type %s?!\n", typestr);
++ }
++
++ ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
++ ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
++ res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIGURE, pdr, len + 4);
++ if (OK != res) {
++#if ACX_DEBUG
++ printk("%s: "FUNC"(type:%s) FAILED\n", priv->netdev->name, typestr);
++#else
++ printk("%s: "FUNC"(type:0x%X) FAILED\n", priv->netdev->name, type);
++#endif
++ /* dump_stack() is already done in issue_cmd() */
++ }
++ return res;
++}
++
++#undef FUNC
++#define FUNC "interrogate"
++#if !ACX_DEBUG
++int
++acx_s_interrogate(wlandevice_t *priv, void *pdr, int type)
++{
++#else
++int
++acx_s_interrogate_debug(wlandevice_t *priv, void *pdr, int type,
++ const char* typestr)
++{
++#endif
++ u16 len;
++ int res;
++
++ if (type < 0x1000)
++ len = CtlLength[type];
++ else
++ len = CtlLengthDot11[type-0x1000];
++ acxlog(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len);
++
++ printk("Type : %08x, Len : %04x\n",type,len);
++
++ ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
++ ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
++ res = acx_s_issue_cmd(priv, ACX1xx_CMD_INTERROGATE, pdr, len + 4);
++ if (OK != res) {
++#if ACX_DEBUG
++ printk("%s: "FUNC"(type:%s) FAILED\n", priv->netdev->name, typestr);
++#else
++ printk("%s: "FUNC"(type:0x%X) FAILED\n", priv->netdev->name, type);
++#endif
++ /* dump_stack() is already done in issue_cmd() */
++ }
++ return res;
++}
++
++#if CMD_DISCOVERY
++void
++great_inquisitor(wlandevice_t *priv)
++{
++ static struct {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ /* 0x200 was too large here: */
++ u8 data[0x100 - 4] ACX_PACKED;
++ } ie;
++ u16 type;
++
++ FN_ENTER;
++
++ /* 0..0x20, 0x1000..0x1020 */
++ for (type = 0; type <= 0x1020; type++) {
++ if (type == 0x21)
++ type = 0x1000;
++ ie.type = cpu_to_le16(type);
++ ie.len = cpu_to_le16(sizeof(ie) - 4);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_INTERROGATE, &ie, sizeof(ie));
++ }
++ FN_EXIT0;
++}
++#endif
++
++
++#ifdef CONFIG_PROC_FS
++/***********************************************************************
++** /proc files
++*/
++/***********************************************************************
++** acx_l_proc_output
++** Generate content for our /proc entry
++**
++** Arguments:
++** buf is a pointer to write output to
++** priv is the usual pointer to our private struct wlandevice
++** Returns:
++** number of bytes actually written to buf
++** Side effects:
++** none
++*/
++static int
++acx_l_proc_output(char *buf, wlandevice_t *priv)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ p += sprintf(p,
++ "acx driver version:\t\t" WLAN_RELEASE "\n"
++ "Wireless extension version:\t" STRING(WIRELESS_EXT) "\n"
++ "chip name:\t\t\t%s (0x%08X)\n"
++ "radio type:\t\t\t0x%02X\n"
++ "form factor:\t\t\t0x%02X\n"
++ "EEPROM version:\t\t\t0x%02X\n"
++ "firmware version:\t\t%s (0x%08X)\n",
++ priv->chip_name, priv->firmware_id,
++ priv->radio_type,
++ priv->form_factor,
++ priv->eeprom_version,
++ priv->firmware_version, priv->firmware_numver);
++
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ struct client *bss = &priv->sta_list[i];
++ if (!bss->used) continue;
++ p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u "
++ "Cap 0x%X SIR %u SNR %u\n",
++ i, MAC(bss->bssid), (char*)bss->essid, bss->channel,
++ bss->cap_info, bss->sir, bss->snr);
++ }
++ p += sprintf(p, "status:\t\t\t%u (%s)\n",
++ priv->status, acx_get_status_name(priv->status));
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_s_proc_diag_output(char *buf, wlandevice_t *priv)
++{
++ char *p = buf;
++ fw_stats_t *fw_stats;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ fw_stats = kmalloc(sizeof(fw_stats_t), GFP_KERNEL);
++ if (!fw_stats) {
++ FN_EXIT1(0);
++ return 0;
++ }
++ memset(fw_stats, 0, sizeof(fw_stats_t));
++
++ acx_lock(priv, flags);
++
++ if (IS_PCI(priv))
++ p = acxpci_s_proc_diag_output(p, priv);
++
++ p += sprintf(p,
++ "\n"
++ "** network status **\n"
++ "dev_state_mask 0x%04X\n"
++ "status %u (%s), "
++ "mode %u, channel %u, "
++ "reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ",
++ priv->dev_state_mask,
++ priv->status, acx_get_status_name(priv->status),
++ priv->mode, priv->channel,
++ priv->reg_dom_id, priv->reg_dom_chanmask
++ );
++ p += sprintf(p,
++ "ESSID \"%s\", essid_active %d, essid_len %d, "
++ "essid_for_assoc \"%s\", nick \"%s\"\n"
++ "WEP ena %d, restricted %d, idx %d\n",
++ priv->essid, priv->essid_active, (int)priv->essid_len,
++ priv->essid_for_assoc, priv->nick,
++ priv->wep_enabled, priv->wep_restricted,
++ priv->wep_current_index);
++ p += sprintf(p, "dev_addr "MACSTR"\n", MAC(priv->dev_addr));
++ p += sprintf(p, "bssid "MACSTR"\n", MAC(priv->bssid));
++ p += sprintf(p, "ap_filter "MACSTR"\n", MAC(priv->ap));
++
++ p += sprintf(p,
++ "\n"
++ "** PHY status **\n"
++ "tx_disabled %d, tx_level_dbm %d\n" /* "tx_level_val %d, tx_level_auto %d\n" */
++ "sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n"
++ "rts_threshold %d, short_retry %d, long_retry %d, msdu_lifetime %d, listen_interval %d, beacon_interval %d\n",
++ priv->tx_disabled, priv->tx_level_dbm, /* priv->tx_level_val, priv->tx_level_auto, */
++ priv->sensitivity, priv->antenna, priv->ed_threshold, priv->cca, priv->preamble_mode,
++ priv->rts_threshold, priv->short_retry, priv->long_retry, priv->msdu_lifetime, priv->listen_interval, priv->beacon_interval);
++
++ acx_unlock(priv, flags);
++
++ if (OK != acx_s_interrogate(priv, fw_stats, ACX1xx_IE_FIRMWARE_STATISTICS))
++ p += sprintf(p,
++ "\n"
++ "** Firmware **\n"
++ "QUERY FAILED!!\n");
++ else {
++ p += sprintf(p,
++ "\n"
++ "** Firmware **\n"
++ "version \"%s\"\n"
++ "tx_desc_overfl %u, rx_OutOfMem %u, rx_hdr_overfl %u, rx_hdr_use_next %u\n"
++ "rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u, rx_dma_req %u\n"
++ "rx_dma_err %u, tx_dma_req %u, tx_dma_err %u, cmd_cplt %u, fiq %u\n"
++ "rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u, irqs %u\n"
++ "acx_trans_procs %u, decrypt_done %u, dma_0_done %u, dma_1_done %u\n",
++ priv->firmware_version,
++ le32_to_cpu(fw_stats->tx_desc_of),
++ le32_to_cpu(fw_stats->rx_oom),
++ le32_to_cpu(fw_stats->rx_hdr_of),
++ le32_to_cpu(fw_stats->rx_hdr_use_next),
++ le32_to_cpu(fw_stats->rx_dropped_frame),
++ le32_to_cpu(fw_stats->rx_frame_ptr_err),
++ le32_to_cpu(fw_stats->rx_xfr_hint_trig),
++ le32_to_cpu(fw_stats->rx_dma_req),
++ le32_to_cpu(fw_stats->rx_dma_err),
++ le32_to_cpu(fw_stats->tx_dma_req),
++ le32_to_cpu(fw_stats->tx_dma_err),
++ le32_to_cpu(fw_stats->cmd_cplt),
++ le32_to_cpu(fw_stats->fiq),
++ le32_to_cpu(fw_stats->rx_hdrs),
++ le32_to_cpu(fw_stats->rx_cmplt),
++ le32_to_cpu(fw_stats->rx_mem_of),
++ le32_to_cpu(fw_stats->rx_rdys),
++ le32_to_cpu(fw_stats->irqs),
++ le32_to_cpu(fw_stats->acx_trans_procs),
++ le32_to_cpu(fw_stats->decrypt_done),
++ le32_to_cpu(fw_stats->dma_0_done),
++ le32_to_cpu(fw_stats->dma_1_done));
++ p += sprintf(p,
++ "tx_exch_complet %u, commands %u, acx_rx_procs %u\n"
++ "hw_pm_mode_changes %u, host_acks %u, pci_pm %u, acm_wakeups %u\n"
++ "wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n"
++ "wep_key_not_found %u, wep_decrypt_fail %u\n",
++ le32_to_cpu(fw_stats->tx_exch_complet),
++ le32_to_cpu(fw_stats->commands),
++ le32_to_cpu(fw_stats->acx_rx_procs),
++ le32_to_cpu(fw_stats->hw_pm_mode_changes),
++ le32_to_cpu(fw_stats->host_acks),
++ le32_to_cpu(fw_stats->pci_pm),
++ le32_to_cpu(fw_stats->acm_wakeups),
++ le32_to_cpu(fw_stats->wep_key_count),
++ le32_to_cpu(fw_stats->wep_default_key_count),
++ le32_to_cpu(fw_stats->dot11_def_key_mib),
++ le32_to_cpu(fw_stats->wep_key_not_found),
++ le32_to_cpu(fw_stats->wep_decrypt_fail));
++ }
++
++ kfree(fw_stats);
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_s_proc_phy_output(char *buf, wlandevice_t *priv)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ /*
++ if (RADIO_RFMD_11 != priv->radio_type) {
++ printk("sorry, not yet adapted for radio types "
++ "other than RFMD, please verify "
++ "PHY size etc. first!\n");
++ goto end;
++ }
++ */
++
++ /* The PHY area is only 0x80 bytes long; further pages after that
++ * only have some page number registers with altered value,
++ * all other registers remain the same. */
++ for (i = 0; i < 0x80; i++) {
++ acx_s_read_phy_reg(priv, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++** acx_e_read_proc_XXXX
++** Handle our /proc entry
++**
++** Arguments:
++** standard kernel read_proc interface
++** Returns:
++** number of bytes written to buf
++** Side effects:
++** none
++*/
++static int
++acx_e_read_proc(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ wlandevice_t *priv = (wlandevice_t *)data;
++ unsigned long flags;
++ int length;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++ acx_lock(priv, flags);
++ /* fill buf */
++ length = acx_l_proc_output(buf, priv);
++ acx_unlock(priv, flags);
++ acx_sem_unlock(priv);
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++static int
++acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ wlandevice_t *priv = (wlandevice_t *)data;
++ int length;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++ /* fill buf */
++ length = acx_s_proc_diag_output(buf, priv);
++ acx_sem_unlock(priv);
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++static int
++acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ wlandevice_t *priv = (wlandevice_t *)data;
++ int length;
++
++ FN_ENTER;
++
++ /* fill buf */
++ length = 0;
++ if (IS_PCI(priv)) {
++ acx_sem_lock(priv);
++ length = acx_proc_eeprom_output(buf, priv);
++ acx_sem_unlock(priv);
++ }
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++static int
++acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ wlandevice_t *priv = (wlandevice_t *)data;
++ int length;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++ /* fill buf */
++ length = acx_s_proc_phy_output(buf, priv);
++ acx_sem_unlock(priv);
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++
++/***********************************************************************
++** /proc files registration
++*/
++static const char * const
++proc_files[] = { "", "_diag", "_eeprom", "_phy" };
++
++static read_proc_t * const
++acx_proc_funcs[] = {
++ acx_e_read_proc,
++ acx_e_read_proc_diag,
++ acx_e_read_proc_eeprom,
++ acx_e_read_proc_phy
++};
++
++static int
++manage_proc_entries(const struct net_device *dev, int remove)
++{
++ /* doh, netdev_priv() doesn't have const! */
++ wlandevice_t *priv = netdev_priv((struct net_device *)dev);
++ char procbuf[80];
++ int i;
++
++ for (i = 0; i < 4; i++) {
++ sprintf(procbuf, "driver/acx_%s", dev->name);
++ strcat(procbuf, proc_files[i]);
++ if (!remove) {
++ acxlog(L_INIT, "creating /proc entry %s\n", procbuf);
++ if (!create_proc_read_entry(procbuf, 0, 0, acx_proc_funcs[i], priv))
++ return NOT_OK;
++ } else {
++ acxlog(L_INIT, "removing /proc entry %s\n", procbuf);
++ remove_proc_entry(procbuf, NULL);
++ }
++ }
++ return OK;
++}
++
++int
++acx_proc_register_entries(const struct net_device *dev)
++{
++ return manage_proc_entries(dev, 0);
++}
++
++int
++acx_proc_unregister_entries(const struct net_device *dev)
++{
++ return manage_proc_entries(dev, 1);
++}
++#endif /* CONFIG_PROC_FS */
++
++
++/***********************************************************************
++** acx_cmd_join_bssid
++**
++** Common code for both acx100 and acx111.
++*/
++/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */
++static const u8
++bitpos2genframe_txrate[] = {
++ 10, /* 0. 1 Mbit/s */
++ 20, /* 1. 2 Mbit/s */
++ 55, /* 2. 5.5 Mbit/s */
++ 0x0B, /* 3. 6 Mbit/s */
++ 0x0F, /* 4. 9 Mbit/s */
++ 110, /* 5. 11 Mbit/s */
++ 0x0A, /* 6. 12 Mbit/s */
++ 0x0E, /* 7. 18 Mbit/s */
++ 220, /* 8. 22 Mbit/s */
++ 0x09, /* 9. 24 Mbit/s */
++ 0x0D, /* 10. 36 Mbit/s */
++ 0x08, /* 11. 48 Mbit/s */
++ 0x0C, /* 12. 54 Mbit/s */
++ 10, /* 13. 1 Mbit/s, should never happen */
++ 10, /* 14. 1 Mbit/s, should never happen */
++ 10, /* 15. 1 Mbit/s, should never happen */
++};
++
++/* Looks scary, eh?
++** Actually, each one compiled into one AND and one SHIFT,
++** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */
++static unsigned int
++rate111to5bits(unsigned int rate)
++{
++ return (rate & 0x7)
++ | ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) )
++ | ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) )
++ ;
++}
++
++static void
++acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid)
++{
++ acx_joinbss_t tmp;
++ int dtim_interval;
++ int i;
++
++ FN_ENTER;
++
++ dtim_interval = (ACX_MODE_0_ADHOC == priv->mode) ?
++ 1 : priv->dtim_interval;
++
++ memset(&tmp, 0, sizeof(tmp));
++
++ for (i = 0; i < ETH_ALEN; i++) {
++ tmp.bssid[i] = bssid[ETH_ALEN-1 - i];
++ }
++
++ tmp.beacon_interval = cpu_to_le16(priv->beacon_interval);
++
++ /* basic rate set. Control frame responses (such as ACK or CTS frames)
++ ** are sent with one of these rates */
++ if (IS_ACX111(priv)) {
++ /* It was experimentally determined that rates_basic
++ ** can take 11g rates as well, not only rates
++ ** defined with JOINBSS_RATES_BASIC111_nnn.
++ ** Just use RATE111_nnn constants... */
++ tmp.u.acx111.dtim_interval = dtim_interval;
++ tmp.u.acx111.rates_basic = cpu_to_le16(priv->rate_basic);
++ acxlog(L_ASSOC, "%s rates_basic %04X, rates_supported %04X\n",
++ __func__, priv->rate_basic, priv->rate_oper);
++ } else {
++ tmp.u.acx100.dtim_interval = dtim_interval;
++ tmp.u.acx100.rates_basic = rate111to5bits(priv->rate_basic);
++ tmp.u.acx100.rates_supported = rate111to5bits(priv->rate_oper);
++ acxlog(L_ASSOC, "%s rates_basic %04X->%02X, "
++ "rates_supported %04X->%02X\n",
++ __func__,
++ priv->rate_basic, tmp.u.acx100.rates_basic,
++ priv->rate_oper, tmp.u.acx100.rates_supported);
++ }
++
++ /* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames
++ ** will be sent (rate/modulation/preamble) */
++ tmp.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(priv->rate_basic)];
++ tmp.genfrm_mod_pre = 0; /* FIXME: was = priv->capab_short (which is always 0); */
++ /* we can use short pre *if* all peers can understand it */
++ /* FIXME #2: we need to correctly set PBCC/OFDM bits here too */
++
++ /* we switch fw to STA mode in MONITOR mode, it seems to be
++ ** the only mode where fw does not emit beacons by itself
++ ** but allows us to send anything (we really want to retain
++ ** ability to tx arbitrary frames in MONITOR mode)
++ */
++ tmp.macmode = (priv->mode != ACX_MODE_MONITOR ? priv->mode : ACX_MODE_2_STA);
++ tmp.channel = priv->channel;
++ tmp.essid_len = priv->essid_len;
++ /* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */
++ memcpy(tmp.essid, priv->essid, tmp.essid_len);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11);
++
++ acxlog(L_ASSOC|L_DEBUG, "BSS_Type = %u\n", tmp.macmode);
++ acxlog_mac(L_ASSOC|L_DEBUG, "JoinBSSID MAC:", priv->bssid, "\n");
++
++ acx_update_capabilities(priv);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_s_cmd_start_scan
++**
++** Issue scan command to the hardware
++*/
++static void
++acx100_s_scan_chan(wlandevice_t *priv)
++{
++ acx100_scan_t s;
++
++ FN_ENTER;
++
++ memset(&s, 0, sizeof(s));
++ s.count = cpu_to_le16(priv->scan_count);
++ s.start_chan = cpu_to_le16(1);
++ s.flags = cpu_to_le16(0x8000);
++ s.max_rate = priv->scan_rate;
++ s.options = priv->scan_mode;
++ s.chan_duration = cpu_to_le16(priv->scan_duration);
++ s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay);
++
++ acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s));
++ FN_EXIT0;
++}
++
++static void
++acx111_s_scan_chan(wlandevice_t *priv)
++{
++ acx111_scan_t s;
++
++ FN_ENTER;
++
++ memset(&s, 0, sizeof(s));
++ s.count = cpu_to_le16(priv->scan_count);
++ s.channel_list_select = 0; /* scan every allowed channel */
++ /*s.channel_list_select = 1;*/ /* scan given channels */
++ s.rate = priv->scan_rate;
++ s.options = priv->scan_mode;
++ s.chan_duration = cpu_to_le16(priv->scan_duration);
++ s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay);
++ /*s.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */
++ s.modulation = 0;
++ /*s.channel_list[0] = 6;
++ s.channel_list[1] = 4;*/
++
++ acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s));
++ FN_EXIT0;
++}
++
++void
++acx_s_cmd_start_scan(wlandevice_t *priv)
++{
++ /* time_before check is 'just in case' thing */
++ if (!(priv->irq_status & HOST_INT_SCAN_COMPLETE)
++ && time_before(jiffies, priv->scan_start + 10*HZ)
++ ) {
++ acxlog(L_INIT, "start_scan: seems like previous scan "
++ "is still running. Not starting anew. Please report\n");
++ return;
++ }
++
++ acxlog(L_INIT, "starting radio scan\n");
++ /* remember that fw is commanded to do scan */
++ priv->scan_start = jiffies;
++ CLEAR_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
++ /* issue it */
++ if (IS_ACX100(priv)) {
++ acx100_s_scan_chan(priv);
++ } else {
++ acx111_s_scan_chan(priv);
++ }
++}
++
++
++/***********************************************************************
++** acx111 feature config
++*/
++static int
++acx111_s_get_feature_config(wlandevice_t *priv,
++ u32 *feature_options, u32 *data_flow_options)
++{
++ struct acx111_ie_feature_config fc;
++
++ if (!IS_ACX111(priv)) {
++ return NOT_OK;
++ }
++
++ memset(&fc, 0, sizeof(fc));
++
++ if (OK != acx_s_interrogate(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) {
++ return NOT_OK;
++ }
++ acxlog(L_DEBUG,
++ "got Feature option:0x%X, DataFlow option: 0x%X\n",
++ fc.feature_options,
++ fc.data_flow_options);
++
++ if (feature_options)
++ *feature_options = le32_to_cpu(fc.feature_options);
++ if (data_flow_options)
++ *data_flow_options = le32_to_cpu(fc.data_flow_options);
++
++ return OK;
++}
++
++static int
++acx111_s_set_feature_config(wlandevice_t *priv,
++ u32 feature_options, u32 data_flow_options,
++ unsigned int mode /* 0 == remove, 1 == add, 2 == set */)
++{
++ struct acx111_ie_feature_config fc;
++
++ if (!IS_ACX111(priv)) {
++ return NOT_OK;
++ }
++
++ if ((mode < 0) || (mode > 2))
++ return NOT_OK;
++
++ if (mode != 2)
++ /* need to modify old data */
++ acx111_s_get_feature_config(priv, &fc.feature_options, &fc.data_flow_options);
++ else {
++ /* need to set a completely new value */
++ fc.feature_options = 0;
++ fc.data_flow_options = 0;
++ }
++
++ if (mode == 0) { /* remove */
++ CLEAR_BIT(fc.feature_options, cpu_to_le32(feature_options));
++ CLEAR_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options));
++ } else { /* add or set */
++ SET_BIT(fc.feature_options, cpu_to_le32(feature_options));
++ SET_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options));
++ }
++
++ acxlog(L_DEBUG,
++ "old: feature 0x%08X dataflow 0x%08X. mode: %u\n"
++ "new: feature 0x%08X dataflow 0x%08X\n",
++ feature_options, data_flow_options, mode,
++ le32_to_cpu(fc.feature_options),
++ le32_to_cpu(fc.data_flow_options));
++
++ if (OK != acx_s_configure(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) {
++ return NOT_OK;
++ }
++
++ return OK;
++}
++
++static inline int
++acx111_s_feature_off(wlandevice_t *priv, u32 f, u32 d)
++{
++ return acx111_s_set_feature_config(priv, f, d, 0);
++}
++static inline int
++acx111_s_feature_on(wlandevice_t *priv, u32 f, u32 d)
++{
++ return acx111_s_set_feature_config(priv, f, d, 1);
++}
++static inline int
++acx111_s_feature_set(wlandevice_t *priv, u32 f, u32 d)
++{
++ return acx111_s_set_feature_config(priv, f, d, 2);
++}
++
++
++/***********************************************************************
++** acx100_s_init_memory_pools
++*/
++static int
++acx100_s_init_memory_pools(wlandevice_t *priv, const acx_ie_memmap_t *mmt)
++{
++ acx100_ie_memblocksize_t MemoryBlockSize;
++ acx100_ie_memconfigoption_t MemoryConfigOption;
++ int TotalMemoryBlocks;
++ int RxBlockNum;
++ int TotalRxBlockSize;
++ int TxBlockNum;
++ int TotalTxBlockSize;
++
++ FN_ENTER;
++
++ /* Let's see if we can follow this:
++ first we select our memory block size (which I think is
++ completely arbitrary) */
++ MemoryBlockSize.size = cpu_to_le16(priv->memblocksize);
++
++ /* Then we alert the card to our decision of block size */
++ if (OK != acx_s_configure(priv, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) {
++ goto bad;
++ }
++
++ /* We figure out how many total blocks we can create, using
++ the block size we chose, and the beginning and ending
++ memory pointers, i.e.: end-start/size */
++ TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / priv->memblocksize;
++
++ acxlog(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n",
++ TotalMemoryBlocks, TotalMemoryBlocks*priv->memblocksize);
++
++ /* MemoryConfigOption.DMA_config bitmask:
++ // access to ACX memory is to be done:
++ 0x00080000 // using PCI conf space?!
++ 0x00040000 // using IO instructions?
++ 0x00000000 // using memory access instructions
++ 0x00020000 // use local memory block linked list (else what?)
++ 0x00010000 // use host indirect descriptors (else host must access ACX memory?)
++ */
++ if (IS_PCI(priv)) {
++ MemoryConfigOption.DMA_config = cpu_to_le32(0x30000);
++ /* Declare start of the Rx host pool */
++ MemoryConfigOption.pRxHostDesc = cpu2acx(priv->rxhostdesc_startphy);
++ acxlog(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n",
++ acx2cpu(MemoryConfigOption.pRxHostDesc),
++ (long)priv->rxhostdesc_startphy);
++ } else {
++ MemoryConfigOption.DMA_config = cpu_to_le32(0x20000);
++ }
++
++ /* 50% of the allotment of memory blocks go to tx descriptors */
++ TxBlockNum = TotalMemoryBlocks / 2;
++ MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum);
++
++ /* and 50% go to the rx descriptors */
++ RxBlockNum = TotalMemoryBlocks - TxBlockNum;
++ MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum);
++
++ /* size of the tx and rx descriptor queues */
++ TotalTxBlockSize = TxBlockNum * priv->memblocksize;
++ TotalRxBlockSize = RxBlockNum * priv->memblocksize;
++ acxlog(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u "
++ "TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum,
++ TotalTxBlockSize, TotalRxBlockSize);
++
++
++ /* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */
++ MemoryConfigOption.rx_mem =
++ cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f);
++
++ /* align the rx descriptor queue to units of 0x20
++ * and offset it by the tx descriptor queue */
++ MemoryConfigOption.tx_mem =
++ cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f);
++ acxlog(L_DEBUG, "rx_mem %08X rx_mem %08X\n",
++ MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem);
++
++ /* alert the device to our decision */
++ if (OK != acx_s_configure(priv, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) {
++ goto bad;
++ }
++
++ /* and tell the device to kick it into gear */
++ if (OK != acx_s_issue_cmd(priv, ACX100_CMD_INIT_MEMORY, NULL, 0)) {
++ goto bad;
++ }
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx100_s_create_dma_regions
++**
++** Note that this fn messes up heavily with hardware, but we cannot
++** lock it (we need to sleep). Not a problem since IRQs can't happen
++*/
++static int
++acx100_s_create_dma_regions(wlandevice_t *priv)
++{
++ acx100_ie_queueconfig_t queueconf;
++ acx_ie_memmap_t memmap;
++ int res = NOT_OK;
++ u32 tx_queue_start, rx_queue_start;
++
++ FN_ENTER;
++
++ /* read out the acx100 physical start address for the queues */
++ if (OK != acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ tx_queue_start = le32_to_cpu(memmap.QueueStart);
++ rx_queue_start = tx_queue_start + TX_CNT * sizeof(txdesc_t);
++
++ acxlog(L_DEBUG, "initializing Queue Indicator\n");
++
++ memset(&queueconf, 0, sizeof(queueconf));
++
++ /* Not needed for PCI, so we can avoid setting them altogether */
++ if (IS_USB(priv)) {
++ queueconf.NumTxDesc = USB_TX_CNT;
++ queueconf.NumRxDesc = USB_RX_CNT;
++ }
++
++ /* calculate size of queues */
++ queueconf.AreaSize = cpu_to_le32(
++ TX_CNT * sizeof(txdesc_t) +
++ RX_CNT * sizeof(rxdesc_t) + 8
++ );
++ queueconf.NumTxQueues = 1; /* number of tx queues */
++ /* sets the beginning of the tx descriptor queue */
++ queueconf.TxQueueStart = memmap.QueueStart;
++ /* done by memset: queueconf.TxQueuePri = 0; */
++ queueconf.RxQueueStart = cpu_to_le32(rx_queue_start);
++ queueconf.QueueOptions = 1; /* auto reset descriptor */
++ /* sets the end of the rx descriptor queue */
++ queueconf.QueueEnd = cpu_to_le32(
++ rx_queue_start + RX_CNT * sizeof(rxdesc_t)
++ );
++ /* sets the beginning of the next queue */
++ queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8);
++ if (OK != acx_s_configure(priv, &queueconf, ACX1xx_IE_QUEUE_CONFIG)) {
++ goto fail;
++ }
++
++#if 0
++ if (IS_PCI(priv)) {
++ /* sets the beginning of the rx descriptor queue, after the tx descrs */
++ if (OK != acx_s_create_hostdesc_queues(priv))
++ goto fail;
++ acx_create_desc_queues(priv, tx_queue_start, rx_queue_start);
++ }
++#endif
++
++ if (OK != acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ memmap.PoolStart = cpu_to_le32(
++ (le32_to_cpu(memmap.QueueEnd) + 4 + 0x1f) & ~0x1f
++ );
++
++ if (OK != acx_s_configure(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ if (OK != acx100_s_init_memory_pools(priv, &memmap)) {
++ goto fail;
++ }
++
++ res = OK;
++ goto end;
++
++fail:
++ acx_s_msleep(1000); /* ? */
++ if (IS_PCI(priv))
++ acx_free_desc_queues(priv);
++end:
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx111_s_create_dma_regions
++**
++** Note that this fn messes up heavily with hardware, but we cannot
++** lock it (we need to sleep). Not a problem since IRQs can't happen
++*/
++#define ACX111_PERCENT(percent) ((percent)/5)
++
++static int
++acx111_s_create_dma_regions(wlandevice_t *priv)
++{
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ u32 tx_queue_start, rx_queue_start;
++
++ FN_ENTER;
++
++ /* Calculate memory positions and queue sizes */
++
++ /* Set up our host descriptor pool + data pool */
++ if (IS_PCI(priv)) {
++ if (OK != acx_s_create_hostdesc_queues(priv))
++ goto fail;
++ }
++
++ memset(&memconf, 0, sizeof(memconf));
++ /* the number of STAs (STA contexts) to support
++ ** NB: was set to 1 and everything seemed to work nevertheless... */
++ memconf.no_of_stations = cpu_to_le16(VEC_SIZE(priv->sta_list));
++ /* specify the memory block size. Default is 256 */
++ memconf.memory_block_size = cpu_to_le16(priv->memblocksize);
++ /* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */
++ memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50);
++ /* set the count of our queues
++ ** NB: struct acx111_ie_memoryconfig shall be modified
++ ** if we ever will switch to more than one rx and/or tx queue */
++ memconf.count_rx_queues = 1;
++ memconf.count_tx_queues = 1;
++ /* 0 == Busmaster Indirect Memory Organization, which is what we want
++ * (using linked host descs with their allocated mem).
++ * 2 == Generic Bus Slave */
++ /* done by memset: memconf.options = 0; */
++ /* let's use 25% for fragmentations and 75% for frame transfers
++ * (specified in units of 5%) */
++ memconf.fragmentation = ACX111_PERCENT(75);
++ /* Rx descriptor queue config */
++ memconf.rx_queue1_count_descs = RX_CNT;
++ memconf.rx_queue1_type = 7; /* must be set to 7 */
++ /* done by memset: memconf.rx_queue1_prio = 0; low prio */
++ if (IS_PCI(priv)) {
++ memconf.rx_queue1_host_rx_start = cpu2acx(priv->rxhostdesc_startphy);
++ }
++ /* Tx descriptor queue config */
++ memconf.tx_queue1_count_descs = TX_CNT;
++ /* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */
++
++ /* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG),
++ ** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh?
++ ** But it is actually correct wrt IE numbers.
++ ** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG)
++ ** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig
++ ** which is 4 bytes larger. what a mess. TODO: clean it up) */
++ if (OK != acx_s_configure(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG)) {
++ goto fail;
++ }
++
++ acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++
++ tx_queue_start = le32_to_cpu(queueconf.tx1_queue_address);
++ rx_queue_start = le32_to_cpu(queueconf.rx1_queue_address);
++
++ acxlog(L_INIT, "dump queue head (from card):\n"
++ "len: %u\n"
++ "tx_memory_block_address: %X\n"
++ "rx_memory_block_address: %X\n"
++ "tx1_queue address: %X\n"
++ "rx1_queue address: %X\n",
++ le16_to_cpu(queueconf.len),
++ le32_to_cpu(queueconf.tx_memory_block_address),
++ le32_to_cpu(queueconf.rx_memory_block_address),
++ tx_queue_start,
++ rx_queue_start);
++
++ if (IS_PCI(priv))
++ acx_create_desc_queues(priv, tx_queue_start, rx_queue_start);
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ if (IS_PCI(priv))
++ acx_free_desc_queues(priv);
++
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx_s_set_defaults
++** Called from acx_s_init_mac
++*/
++int
++acx_s_set_defaults(wlandevice_t *priv)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* query some settings from the card.
++ * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial
++ * query is REQUIRED, otherwise the card won't work correctly!! */
++ priv->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN;
++ /* Only ACX100 supports ED and CCA */
++ if (IS_ACX100(priv))
++ priv->get_mask |= GETSET_CCA|GETSET_ED_THRESH;
++
++ acx_s_update_card_settings(priv, 0, 0);
++
++ acx_lock(priv, flags);
++
++ /* set our global interrupt mask */
++ if (IS_PCI(priv))
++ acx_set_interrupt_mask(priv);
++
++ priv->led_power = 1; /* LED is active on startup */
++ priv->brange_max_quality = 60; /* LED blink max quality is 60 */
++ priv->brange_time_last_state_change = jiffies;
++
++ /* copy the MAC address we just got from the card
++ * into our MAC address used during current 802.11 session */
++ MAC_COPY(priv->dev_addr, priv->netdev->dev_addr);
++ sprintf(priv->essid, "STA%02X%02X%02X",
++ priv->dev_addr[3], priv->dev_addr[4], priv->dev_addr[5]);
++ priv->essid_len = sizeof("STAxxxxxx") - 1; /* make sure to adapt if changed above! */
++ priv->essid_active = 1;
++
++ /* we have a nick field to waste, so why not abuse it
++ * to announce the driver version? ;-) */
++ strncpy(priv->nick, "acx " WLAN_RELEASE, IW_ESSID_MAX_SIZE);
++
++ if (IS_PCI(priv)) {
++ if (IS_ACX111(priv)) {
++ /* Hope this is correct, only tested with domain 0x30 */
++ acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id);
++ } else if (priv->eeprom_version < 5) {
++ acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id);
++ } else {
++ acx_read_eeprom_offset(priv, 0x171, &priv->reg_dom_id);
++ }
++ }
++
++ priv->channel = 1;
++ /* 0xffff would be better, but then we won't get a "scan complete"
++ * interrupt, so our current infrastructure will fail: */
++ priv->scan_count = 1;
++ priv->scan_mode = ACX_SCAN_OPT_PASSIVE;
++ /* Doesn't work for acx100, do it only for acx111 for now */
++ if (IS_ACX111(priv)) {
++ priv->scan_mode = ACX_SCAN_OPT_ACTIVE;
++ }
++ priv->scan_duration = 100;
++ priv->scan_probe_delay = 200;
++ priv->scan_rate = ACX_SCAN_RATE_1;
++
++ priv->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
++ priv->preamble_mode = 2; /* auto */
++ priv->listen_interval = 100;
++ priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
++ priv->mode = ACX_MODE_2_STA;
++ priv->dtim_interval = DEFAULT_DTIM_INTERVAL;
++
++ priv->msdu_lifetime = DEFAULT_MSDU_LIFETIME;
++ SET_BIT(priv->set_mask, SET_MSDU_LIFETIME);
++
++ priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
++
++ /* use standard default values for retry limits */
++ priv->short_retry = 7; /* max. retries for (short) non-RTS packets */
++ priv->long_retry = 4; /* max. retries for long (RTS) packets */
++ SET_BIT(priv->set_mask, GETSET_RETRY);
++
++ priv->fallback_threshold = 3;
++ priv->stepup_threshold = 10;
++ priv->rate_bcast = RATE111_1;
++ priv->rate_bcast100 = RATE100_1;
++ priv->rate_basic = RATE111_1 | RATE111_2;
++ priv->rate_auto = 1;
++ if (IS_ACX111(priv)) {
++ priv->rate_oper = RATE111_ALL;
++ } else {
++ priv->rate_oper = RATE111_ACX100_COMPAT;
++ }
++
++ /* configure card to do rate fallback when in auto rate mode. */
++ SET_BIT(priv->set_mask, SET_RATE_FALLBACK);
++
++ /* Supported Rates element - the rates here are given in units of
++ * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */
++ acx_l_update_ratevector(priv);
++
++ priv->capab_short = 0;
++ priv->capab_pbcc = 1;
++ priv->capab_agility = 0;
++
++ SET_BIT(priv->set_mask, SET_RXCONFIG);
++
++ /* set some more defaults */
++ if (IS_ACX111(priv)) {
++ /* 30mW (15dBm) is default, at least in my acx111 card: */
++ priv->tx_level_dbm = 15;
++ } else {
++ /* don't use max. level, since it might be dangerous
++ * (e.g. WRT54G people experience
++ * excessive Tx power damage!) */
++ priv->tx_level_dbm = 18;
++ }
++ /* priv->tx_level_auto = 1; */
++ SET_BIT(priv->set_mask, GETSET_TXPOWER);
++
++ if (IS_ACX111(priv)) {
++ /* start with sensitivity level 1 out of 3: */
++ priv->sensitivity = 1;
++ }
++
++ /* better re-init the antenna value we got above */
++ SET_BIT(priv->set_mask, GETSET_ANTENNA);
++
++ priv->ps_wakeup_cfg = 0;
++ priv->ps_listen_interval = 0;
++ priv->ps_options = 0;
++ priv->ps_hangover_period = 0;
++ priv->ps_enhanced_transition_time = 0;
++#ifdef POWER_SAVE_80211
++ SET_BIT(priv->set_mask, GETSET_POWER_80211);
++#endif
++
++ MAC_BCAST(priv->ap);
++
++ acx_unlock(priv, flags);
++ acx_lock_unhold(); // hold time 844814 CPU ticks @2GHz
++
++ acx_s_initialize_rx_config(priv);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** FIXME: this should be solved in a general way for all radio types
++** by decoding the radio firmware module,
++** since it probably has some standard structure describing how to
++** set the power level of the radio module which it controls.
++** Or maybe not, since the radio module probably has a function interface
++** instead which then manages Tx level programming :-\
++*/
++static int
++acx111_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
++{
++ struct acx111_ie_tx_level tx_level;
++
++ /* my acx111 card has two power levels in its configoptions (== EEPROM):
++ * 1 (30mW) [15dBm]
++ * 2 (10mW) [10dBm]
++ * For now, just assume all other acx111 cards have the same.
++ * Ideally we would query it here, but we first need a
++ * standard way to query individual configoptions easily. */
++ if (level_dbm <= 12) {
++ tx_level.level = 2; /* 10 dBm */
++ priv->tx_level_dbm = 10;
++ } else {
++ tx_level.level = 1; /* 15 dBm */
++ priv->tx_level_dbm = 15;
++ }
++ if (level_dbm != priv->tx_level_dbm)
++ acxlog(L_INIT, "acx111 firmware has specific "
++ "power levels only: adjusted %d dBm to %d dBm!\n",
++ level_dbm, priv->tx_level_dbm);
++
++ return acx_s_configure(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
++}
++
++static int
++acx_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
++{
++ if (IS_ACX111(priv)) {
++ return acx111_s_set_tx_level(priv, level_dbm);
++ }
++ if (IS_PCI(priv)) {
++ return acx100_s_set_tx_level(priv, level_dbm);
++ }
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++#ifdef UNUSED
++/* Returns the current tx level (ACX111) */
++static u8
++acx111_s_get_tx_level(wlandevice_t *priv)
++{
++ struct acx111_ie_tx_level tx_level;
++
++ tx_level.level = 0;
++ acx_s_interrogate(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
++ return tx_level.level;
++}
++#endif
++
++
++/***********************************************************************
++** acx_s_init_mac
++*/
++int
++acx_s_init_mac(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ if (IS_PCI(priv)) {
++ priv->memblocksize = 256; /* 256 is default */
++ acx_init_mboxes(priv);
++ /* try to load radio for both ACX100 and ACX111, since both
++ * chips have at least some firmware versions making use of an
++ * external radio module */
++ acx_s_upload_radio(priv);
++ } else {
++ priv->memblocksize = 128;
++ }
++
++ if (IS_ACX111(priv)) {
++ /* for ACX111, the order is different from ACX100
++ 1. init packet templates
++ 2. create station context and create dma regions
++ 3. init wep default keys
++ */
++ if (OK != acx111_s_init_packet_templates(priv))
++ goto fail;
++
++ if (OK != acx111_s_create_dma_regions(priv)) {
++ printk("%s: acx111_create_dma_regions FAILED\n",
++ dev->name);
++ goto fail;
++ }
++#ifdef DEBUG_WEP
++ /* don't decrypt WEP in firmware */
++ if (OK != acx111_s_feature_on(priv, 0, FEATURE2_SNIFFER))
++ goto fail;
++#endif
++ } else {
++ if (OK != acx100_s_init_wep(priv))
++ goto fail;
++ acxlog(L_DEBUG, "between init_wep and init_packet_templates\n");
++ if (OK != acx100_s_init_packet_templates(priv))
++ goto fail;
++
++ if (OK != acx100_s_create_dma_regions(priv)) {
++ printk("%s: acx100_create_dma_regions FAILED\n",
++ dev->name);
++ goto fail;
++ }
++ }
++
++ MAC_COPY(dev->dev_addr, priv->dev_addr);
++ result = OK;
++
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_rxmonitor
++* Called from IRQ context only
++*----------------------------------------------------------------*/
++static void
++acx_l_rxmonitor(wlandevice_t *priv, const rxbuffer_t *rxbuf)
++{
++ wlansniffrm_t *msg;
++ struct sk_buff *skb;
++ void *datap;
++ unsigned int skb_len;
++ int payload_offset;
++
++ FN_ENTER;
++
++ /* we are in big luck: the acx100 doesn't modify any of the fields */
++ /* in the 802.11 frame. just pass this packet into the PF_PACKET */
++ /* subsystem. yeah. */
++ payload_offset = ((u8*)acx_get_wlan_hdr(priv, rxbuf) - (u8*)rxbuf);
++ skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset;
++
++ /* sanity check */
++ if (skb_len > WLAN_A4FR_MAXLEN_WEP) {
++ printk("%s: monitor mode panic: oversized frame!\n",
++ priv->netdev->name);
++ goto end;
++ }
++
++ if (priv->netdev->type == ARPHRD_IEEE80211_PRISM)
++ skb_len += sizeof(*msg);
++
++ /* allocate skb */
++ skb = dev_alloc_skb(skb_len);
++ if (!skb) {
++ printk("%s: no memory for skb (%u bytes)\n",
++ priv->netdev->name, skb_len);
++ goto end;
++ }
++
++ skb_put(skb, skb_len);
++
++ /* when in raw 802.11 mode, just copy frame as-is */
++ if (priv->netdev->type == ARPHRD_IEEE80211)
++ datap = skb->data;
++ else { /* otherwise, emulate prism header */
++ msg = (wlansniffrm_t*)skb->data;
++ datap = msg + 1;
++
++ msg->msgcode = WLANSNIFFFRM;
++ msg->msglen = sizeof(*msg);
++ strncpy(msg->devname, priv->netdev->name, sizeof(msg->devname)-1);
++ msg->devname[sizeof(msg->devname)-1] = '\0';
++
++ msg->hosttime.did = WLANSNIFFFRM_hosttime;
++ msg->hosttime.status = WLANITEM_STATUS_data_ok;
++ msg->hosttime.len = 4;
++ msg->hosttime.data = jiffies;
++
++ msg->mactime.did = WLANSNIFFFRM_mactime;
++ msg->mactime.status = WLANITEM_STATUS_data_ok;
++ msg->mactime.len = 4;
++ msg->mactime.data = rxbuf->time;
++
++ msg->channel.did = WLANSNIFFFRM_channel;
++ msg->channel.status = WLANITEM_STATUS_data_ok;
++ msg->channel.len = 4;
++ msg->channel.data = priv->channel;
++
++ msg->rssi.did = WLANSNIFFFRM_rssi;
++ msg->rssi.status = WLANITEM_STATUS_no_value;
++ msg->rssi.len = 4;
++ msg->rssi.data = 0;
++
++ msg->sq.did = WLANSNIFFFRM_sq;
++ msg->sq.status = WLANITEM_STATUS_no_value;
++ msg->sq.len = 4;
++ msg->sq.data = 0;
++
++ msg->signal.did = WLANSNIFFFRM_signal;
++ msg->signal.status = WLANITEM_STATUS_data_ok;
++ msg->signal.len = 4;
++ msg->signal.data = rxbuf->phy_snr;
++
++ msg->noise.did = WLANSNIFFFRM_noise;
++ msg->noise.status = WLANITEM_STATUS_data_ok;
++ msg->noise.len = 4;
++ msg->noise.data = rxbuf->phy_level;
++
++ msg->rate.did = WLANSNIFFFRM_rate;
++ msg->rate.status = WLANITEM_STATUS_data_ok;
++ msg->rate.len = 4;
++ msg->rate.data = rxbuf->phy_plcp_signal / 5;
++
++ msg->istx.did = WLANSNIFFFRM_istx;
++ msg->istx.status = WLANITEM_STATUS_data_ok;
++ msg->istx.len = 4;
++ msg->istx.data = 0; /* tx=0: it's not a tx packet */
++
++ skb_len -= sizeof(*msg);
++
++ msg->frmlen.did = WLANSNIFFFRM_signal;
++ msg->frmlen.status = WLANITEM_STATUS_data_ok;
++ msg->frmlen.len = 4;
++ msg->frmlen.data = skb_len;
++ }
++
++ memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len);
++
++ skb->dev = priv->netdev;
++ skb->dev->last_rx = jiffies;
++
++ skb->mac.raw = skb->data;
++ skb->ip_summed = CHECKSUM_NONE;
++ skb->pkt_type = PACKET_OTHERHOST;
++ skb->protocol = htons(ETH_P_80211_RAW);
++ netif_rx(skb);
++
++ priv->stats.rx_packets++;
++ priv->stats.rx_bytes += skb->len;
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_rx_ieee802_11_frame
++**
++** Called from IRQ context only
++*/
++
++/* All these contortions are for saner dup logging
++**
++** We want: (a) to know about excessive dups
++** (b) to not spam kernel log about occasional dups
++**
++** 1/64 threshold was chosen by running "ping -A"
++** It gave "rx: 59 DUPs in 2878 packets" only with 4 parallel
++** "ping -A" streams running. */
++static inline int
++acx_l_handle_dup(wlandevice_t *priv, u16 seq)
++{
++ if (priv->dup_count) {
++ priv->nondup_count++;
++ if (time_after(jiffies, priv->dup_msg_expiry)) {
++ /* Log only if more than 1 dup in 64 packets */
++ if (priv->nondup_count/64 < priv->dup_count) {
++ printk(KERN_INFO "%s: rx: %d DUPs in "
++ "%d packets received in 10 secs\n",
++ priv->netdev->name,
++ priv->dup_count,
++ priv->nondup_count);
++ }
++ priv->dup_count = 0;
++ priv->nondup_count = 0;
++ }
++ }
++ if (unlikely(seq == priv->last_seq_ctrl)) {
++ if (!priv->dup_count++)
++ priv->dup_msg_expiry = jiffies + 10*HZ;
++ priv->stats.rx_errors++;
++ return 1; /* a dup */
++ }
++ priv->last_seq_ctrl = seq;
++ return 0;
++}
++
++static int
++acx_l_rx_ieee802_11_frame(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ unsigned int ftype, fstype;
++ const wlan_hdr_t *hdr;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ hdr = acx_get_wlan_hdr(priv, rxbuf);
++
++ /* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */
++ if ((hdr->fc & WF_FC_PVERi) != 0) {
++ printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n");
++ goto end;
++ }
++
++ ftype = hdr->fc & WF_FC_FTYPEi;
++ fstype = hdr->fc & WF_FC_FSTYPEi;
++
++ switch (ftype) {
++ /* check data frames first, for speed */
++ case WF_FTYPE_DATAi:
++ switch (fstype) {
++ case WF_FSTYPE_DATAONLYi:
++ if (acx_l_handle_dup(priv, hdr->seq))
++ break; /* a dup, simply discard it */
++
++ /* TODO:
++ if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) {
++ result = acx_l_process_data_frame_wds(priv, rxbuf);
++ break;
++ }
++ */
++
++ switch (priv->mode) {
++ case ACX_MODE_3_AP:
++ result = acx_l_process_data_frame_master(priv, rxbuf);
++ break;
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ result = acx_l_process_data_frame_client(priv, rxbuf);
++ break;
++ }
++ case WF_FSTYPE_DATA_CFACKi:
++ case WF_FSTYPE_DATA_CFPOLLi:
++ case WF_FSTYPE_DATA_CFACK_CFPOLLi:
++ case WF_FSTYPE_CFPOLLi:
++ case WF_FSTYPE_CFACK_CFPOLLi:
++ /* see above.
++ acx_process_class_frame(priv, rxbuf, 3); */
++ break;
++ case WF_FSTYPE_NULLi:
++ /* acx_l_process_NULL_frame(priv, rxbuf, 3); */
++ break;
++ /* FIXME: same here, see above */
++ case WF_FSTYPE_CFACKi:
++ default:
++ break;
++ }
++ break;
++ case WF_FTYPE_MGMTi:
++ result = acx_l_process_mgmt_frame(priv, rxbuf);
++ break;
++ case WF_FTYPE_CTLi:
++ if (fstype == WF_FSTYPE_PSPOLLi)
++ result = OK;
++ /* this call is irrelevant, since
++ * acx_process_class_frame is a stub, so return
++ * immediately instead.
++ * return acx_process_class_frame(priv, rxbuf, 3); */
++ break;
++ default:
++ break;
++ }
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_l_process_rxbuf
++**
++** NB: used by USB code also
++*/
++void
++acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ struct wlan_hdr *hdr;
++ unsigned int buf_len;
++ unsigned int qual;
++ u16 fc;
++
++ hdr = acx_get_wlan_hdr(priv, rxbuf);
++ /* length of frame from control field to last byte of FCS */
++ buf_len = RXBUF_BYTES_RCVD(rxbuf);
++ fc = le16_to_cpu(hdr->fc);
++
++ if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON)
++ || (acx_debug & L_XFER_BEACON)
++ ) {
++ acxlog(L_XFER|L_DATA, "rx: %s "
++ "time %u len %u signal %u SNR %u macstat %02X "
++ "phystat %02X phyrate %u status %u\n",
++ acx_get_packet_type_string(fc),
++ le32_to_cpu(rxbuf->time),
++ buf_len,
++ acx_signal_to_winlevel(rxbuf->phy_level),
++ acx_signal_to_winlevel(rxbuf->phy_snr),
++ rxbuf->mac_status,
++ rxbuf->phy_stat_baseband,
++ rxbuf->phy_plcp_signal,
++ priv->status);
++ }
++
++ if (unlikely(acx_debug & L_DATA)) {
++ printk("rx: 802.11 buf[%u]: ", buf_len);
++ acx_dump_bytes(hdr, buf_len);
++ }
++
++ /* FIXME: should check for Rx errors (rxbuf->mac_status?
++ * discard broken packets - but NOT for monitor!)
++ * and update Rx packet statistics here */
++
++ if (unlikely(priv->mode == ACX_MODE_MONITOR)) {
++ acx_l_rxmonitor(priv, rxbuf);
++ } else if (likely(buf_len >= WLAN_HDR_A3_LEN)) {
++ acx_l_rx_ieee802_11_frame(priv, rxbuf);
++ } else {
++ acxlog(L_DEBUG|L_XFER|L_DATA,
++ "rx: NOT receiving packet (%s): "
++ "size too small (%u)\n",
++ acx_get_packet_type_string(fc),
++ buf_len);
++ }
++
++ /* Now check Rx quality level, AFTER processing packet.
++ * I tried to figure out how to map these levels to dBm
++ * values, but for the life of me I really didn't
++ * manage to get it. Either these values are not meant to
++ * be expressed in dBm, or it's some pretty complicated
++ * calculation. */
++
++#ifdef FROM_SCAN_SOURCE_ONLY
++ /* only consider packets originating from the MAC
++ * address of the device that's managing our BSSID.
++ * Disable it for now, since it removes information (levels
++ * from different peers) and slows the Rx path. */
++ if (priv->ap_client
++ && mac_is_equal(hdr->a2, priv->ap_client->address)) {
++#endif
++ priv->wstats.qual.level = acx_signal_to_winlevel(rxbuf->phy_level);
++ priv->wstats.qual.noise = acx_signal_to_winlevel(rxbuf->phy_snr);
++#ifndef OLD_QUALITY
++ qual = acx_signal_determine_quality(priv->wstats.qual.level,
++ priv->wstats.qual.noise);
++#else
++ qual = (priv->wstats.qual.noise <= 100) ?
++ 100 - priv->wstats.qual.noise : 0;
++#endif
++ priv->wstats.qual.qual = qual;
++ priv->wstats.qual.updated = 7; /* all 3 indicators updated */
++#ifdef FROM_SCAN_SOURCE_ONLY
++ }
++#endif
++}
++
++
++/***********************************************************************
++** acx_i_start_xmit
++**
++** Called by network core. Can be called outside of process context.
++*/
++int
++acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ tx_t *tx;
++ void *txbuf;
++ unsigned long flags;
++ int txresult = NOT_OK;
++ int len;
++
++ FN_ENTER;
++
++ if (unlikely(!skb)) {
++ /* indicate success */
++ txresult = OK;
++ goto end_no_unlock;
++ }
++ if (unlikely(!priv)) {
++ goto end_no_unlock;
++ }
++
++ acx_lock(priv, flags);
++
++ if (unlikely(!(priv->dev_state_mask & ACX_STATE_IFACE_UP))) {
++ goto end;
++ }
++ if (unlikely(priv->mode == ACX_MODE_OFF)) {
++ goto end;
++ }
++ if (unlikely(acx_queue_stopped(dev))) {
++ acxlog(L_DEBUG, "%s: called when queue stopped\n", __func__);
++ goto end;
++ }
++ if (unlikely(ACX_STATUS_4_ASSOCIATED != priv->status)) {
++ acxlog(L_XFER, "trying to xmit, but not associated yet: "
++ "aborting...\n");
++ /* silently drop the packet, since we're not connected yet */
++ txresult = OK;
++ /* ...but indicate an error nevertheless */
++ priv->stats.tx_errors++;
++ goto end;
++ }
++
++ tx = acx_l_alloc_tx(priv);
++ if (unlikely(!tx)) {
++ printk("%s: start_xmit: txdesc ring is full, dropping tx\n",
++ dev->name);
++ txresult = NOT_OK;
++ goto end;
++ }
++
++ txbuf = acx_l_get_txbuf(priv, tx);
++ if (!txbuf) {
++ /* Card was removed */
++ txresult = NOT_OK;
++ goto end;
++ }
++ len = acx_l_ether_to_txbuf(priv, txbuf, skb);
++ if (len < 0) {
++ /* Error in packet conversion */
++ txresult = NOT_OK;
++ goto end;
++ }
++ acx_l_tx_data(priv, tx, len);
++ dev->trans_start = jiffies;
++
++ txresult = OK;
++ priv->stats.tx_packets++;
++ priv->stats.tx_bytes += skb->len;
++
++end:
++ acx_unlock(priv, flags);
++
++end_no_unlock:
++ if ((txresult == OK) && skb)
++ dev_kfree_skb_any(skb);
++
++ FN_EXIT1(txresult);
++ return txresult;
++}
++
++
++/***********************************************************************
++** acx_l_update_ratevector
++**
++** Updates priv->rate_supported[_len] according to rate_{basic,oper}
++*/
++const u8
++bitpos2ratebyte[] = {
++ DOT11RATEBYTE_1,
++ DOT11RATEBYTE_2,
++ DOT11RATEBYTE_5_5,
++ DOT11RATEBYTE_6_G,
++ DOT11RATEBYTE_9_G,
++ DOT11RATEBYTE_11,
++ DOT11RATEBYTE_12_G,
++ DOT11RATEBYTE_18_G,
++ DOT11RATEBYTE_22,
++ DOT11RATEBYTE_24_G,
++ DOT11RATEBYTE_36_G,
++ DOT11RATEBYTE_48_G,
++ DOT11RATEBYTE_54_G,
++};
++
++void
++acx_l_update_ratevector(wlandevice_t *priv)
++{
++ u16 bcfg = priv->rate_basic;
++ u16 ocfg = priv->rate_oper;
++ u8 *supp = priv->rate_supported;
++ const u8 *dot11 = bitpos2ratebyte;
++
++ FN_ENTER;
++
++ while (ocfg) {
++ if (ocfg & 1) {
++ *supp = *dot11;
++ if (bcfg & 1) {
++ *supp |= 0x80;
++ }
++ supp++;
++ }
++ dot11++;
++ ocfg >>= 1;
++ bcfg >>= 1;
++ }
++ priv->rate_supported_len = supp - priv->rate_supported;
++ if (acx_debug & L_ASSOC) {
++ printk("new ratevector: ");
++ acx_dump_bytes(priv->rate_supported, priv->rate_supported_len);
++ }
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_init
++*----------------------------------------------------------------*/
++static void
++acx_l_sta_list_init(wlandevice_t *priv)
++{
++ FN_ENTER;
++ memset(priv->sta_hash_tab, 0, sizeof(priv->sta_hash_tab));
++ memset(priv->sta_list, 0, sizeof(priv->sta_list));
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_get_from_hash
++*----------------------------------------------------------------*/
++static inline client_t*
++acx_l_sta_list_get_from_hash(wlandevice_t *priv, const u8 *address)
++{
++ return priv->sta_hash_tab[address[5] % VEC_SIZE(priv->sta_hash_tab)];
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_get
++*----------------------------------------------------------------*/
++client_t*
++acx_l_sta_list_get(wlandevice_t *priv, const u8 *address)
++{
++ client_t *client;
++ FN_ENTER;
++ client = acx_l_sta_list_get_from_hash(priv, address);
++ while (client) {
++ if (mac_is_equal(address, client->address)) {
++ client->mtime = jiffies;
++ break;
++ }
++ client = client->next;
++ }
++ FN_EXIT0;
++ return client;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_del
++*----------------------------------------------------------------*/
++void
++acx_l_sta_list_del(wlandevice_t *priv, client_t *victim)
++{
++ client_t *client, *next;
++
++ client = acx_l_sta_list_get_from_hash(priv, victim->address);
++ next = client;
++ /* tricky. next = client on first iteration only,
++ ** on all other iters next = client->next */
++ while (next) {
++ if (next == victim) {
++ client->next = victim->next;
++ /* Overkill */
++ memset(victim, 0, sizeof(*victim));
++ break;
++ }
++ client = next;
++ next = client->next;
++ }
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_alloc
++*
++* Never fails - will evict oldest client if needed
++*----------------------------------------------------------------*/
++static client_t*
++acx_l_sta_list_alloc(wlandevice_t *priv)
++{
++ int i;
++ unsigned long age, oldest_age;
++ client_t *client, *oldest;
++
++ FN_ENTER;
++
++ oldest = &priv->sta_list[0];
++ oldest_age = 0;
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ client = &priv->sta_list[i];
++
++ if (!client->used) {
++ goto found;
++ } else {
++ age = jiffies - client->mtime;
++ if (oldest_age < age) {
++ oldest_age = age;
++ oldest = client;
++ }
++ }
++ }
++ acx_l_sta_list_del(priv, oldest);
++ client = oldest;
++found:
++ memset(client, 0, sizeof(*client));
++ FN_EXIT0;
++ return client;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_add
++*
++* Never fails - will evict oldest client if needed
++*----------------------------------------------------------------*/
++/* In case we will reimplement it differently... */
++#define STA_LIST_ADD_CAN_FAIL 0
++
++static client_t*
++acx_l_sta_list_add(wlandevice_t *priv, const u8 *address)
++{
++ client_t *client;
++ int index;
++
++ FN_ENTER;
++
++ client = acx_l_sta_list_alloc(priv);
++
++ client->mtime = jiffies;
++ MAC_COPY(client->address, address);
++ client->used = CLIENT_EXIST_1;
++ client->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
++ client->auth_step = 1;
++ /* give some tentative peer rate values
++ ** (needed because peer may do auth without probing us first,
++ ** thus we'll have no idea of peer's ratevector yet).
++ ** Will be overwritten by scanning or assoc code */
++ client->rate_cap = priv->rate_basic;
++ client->rate_cfg = priv->rate_basic;
++ client->rate_cur = 1 << lowest_bit(priv->rate_basic);
++
++ index = address[5] % VEC_SIZE(priv->sta_hash_tab);
++ client->next = priv->sta_hash_tab[index];
++ priv->sta_hash_tab[index] = client;
++
++ acxlog_mac(L_ASSOC, "sta_list_add: sta=", address, "\n");
++
++ FN_EXIT0;
++ return client;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_sta_list_get_or_add
++*
++* Never fails - will evict oldest client if needed
++*----------------------------------------------------------------*/
++static client_t*
++acx_l_sta_list_get_or_add(wlandevice_t *priv, const u8 *address)
++{
++ client_t *client = acx_l_sta_list_get(priv, address);
++ if (!client)
++ client = acx_l_sta_list_add(priv, address);
++ return client;
++}
++
++
++/***********************************************************************
++** acx_set_status
++**
++** This function is called in many atomic regions, must not sleep
++**
++** This function does not need locking UNLESS you call it
++** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can
++** wake queue. This can race with stop_queue elsewhere.
++** See acx_stop_queue comment. */
++void
++acx_set_status(wlandevice_t *priv, u16 new_status)
++{
++#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */
++ u16 old_status = priv->status;
++
++ FN_ENTER;
++
++ acxlog(L_ASSOC, "%s(%d):%s\n",
++ __func__, new_status, acx_get_status_name(new_status));
++
++#if WIRELESS_EXT > 13 /* wireless_send_event() and SIOCGIWSCAN */
++ /* wireless_send_event never sleeps */
++ if (ACX_STATUS_4_ASSOCIATED == new_status) {
++ union iwreq_data wrqu;
++
++ wrqu.data.length = 0;
++ wrqu.data.flags = 0;
++ wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL);
++
++ wrqu.data.length = 0;
++ wrqu.data.flags = 0;
++ MAC_COPY(wrqu.ap_addr.sa_data, priv->bssid);
++ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
++ wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL);
++ } else {
++ union iwreq_data wrqu;
++
++ /* send event with empty BSSID to indicate we're not associated */
++ MAC_ZERO(wrqu.ap_addr.sa_data);
++ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
++ wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL);
++ }
++#endif
++
++ priv->status = new_status;
++
++ switch (new_status) {
++ case ACX_STATUS_1_SCANNING:
++ priv->scan_retries = 0;
++ /* 1.0 s initial scan time */
++ acx_set_timer(priv, 1000000);
++ break;
++ case ACX_STATUS_2_WAIT_AUTH:
++ case ACX_STATUS_3_AUTHENTICATED:
++ priv->auth_or_assoc_retries = 0;
++ acx_set_timer(priv, 1500000); /* 1.5 s */
++ break;
++ }
++
++#if QUEUE_OPEN_AFTER_ASSOC
++ if (new_status == ACX_STATUS_4_ASSOCIATED) {
++ if (old_status < ACX_STATUS_4_ASSOCIATED) {
++ /* ah, we're newly associated now,
++ * so let's indicate carrier */
++ acx_carrier_on(priv->netdev, "after association");
++ acx_wake_queue(priv->netdev, "after association");
++ }
++ } else {
++ /* not associated any more, so let's kill carrier */
++ if (old_status >= ACX_STATUS_4_ASSOCIATED) {
++ acx_carrier_off(priv->netdev, "after losing association");
++ acx_stop_queue(priv->netdev, "after losing association");
++ }
++ }
++#endif
++ FN_EXIT0;
++}
++
++
++/*------------------------------------------------------------------------------
++ * acx_i_timer
++ *
++ * Fires up periodically. Used to kick scan/auth/assoc if something goes wrong
++ *----------------------------------------------------------------------------*/
++void
++acx_i_timer(unsigned long address)
++{
++ unsigned long flags;
++ wlandevice_t *priv = (wlandevice_t *)address;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++
++ acxlog(L_DEBUG|L_ASSOC, "%s: priv->status=%d (%s)\n",
++ __func__, priv->status, acx_get_status_name(priv->status));
++
++ switch (priv->status) {
++ case ACX_STATUS_1_SCANNING:
++ /* was set to 0 by set_status() */
++ if (++priv->scan_retries < 7) {
++ acx_set_timer(priv, 1000000);
++ /* used to interrogate for scan status.
++ ** We rely on SCAN_COMPLETE IRQ instead */
++ acxlog(L_ASSOC, "continuing scan (%d sec)\n",
++ priv->scan_retries);
++ } else {
++ acxlog(L_ASSOC, "stopping scan\n");
++ /* send stop_scan cmd when we leave the interrupt context,
++ * and make a decision what to do next (COMPLETE_SCAN) */
++ acx_schedule_after_interrupt_task(priv,
++ ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN);
++ }
++ break;
++ case ACX_STATUS_2_WAIT_AUTH:
++ /* was set to 0 by set_status() */
++ if (++priv->auth_or_assoc_retries < 10) {
++ acxlog(L_ASSOC, "resend authen1 request (attempt %d)\n",
++ priv->auth_or_assoc_retries + 1);
++ acx_l_transmit_authen1(priv);
++ } else {
++ /* time exceeded: fall back to scanning mode */
++ acxlog(L_ASSOC,
++ "authen1 request reply timeout, giving up\n");
++ /* we are a STA, need to find AP anyhow */
++ acx_set_status(priv, ACX_STATUS_1_SCANNING);
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++ /* used to be 1500000, but some other driver uses 2.5s */
++ acx_set_timer(priv, 2500000);
++ break;
++ case ACX_STATUS_3_AUTHENTICATED:
++ /* was set to 0 by set_status() */
++ if (++priv->auth_or_assoc_retries < 10) {
++ acxlog(L_ASSOC, "resend assoc request (attempt %d)\n",
++ priv->auth_or_assoc_retries + 1);
++ acx_l_transmit_assoc_req(priv);
++ } else {
++ /* time exceeded: give up */
++ acxlog(L_ASSOC,
++ "association request reply timeout, giving up\n");
++ /* we are a STA, need to find AP anyhow */
++ acx_set_status(priv, ACX_STATUS_1_SCANNING);
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++ acx_set_timer(priv, 2500000); /* see above */
++ break;
++ case ACX_STATUS_4_ASSOCIATED:
++ default:
++ break;
++ }
++
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++
++/*------------------------------------------------------------------------------
++ * acx_set_timer
++ *
++ * Sets the 802.11 state management timer's timeout.
++ *----------------------------------------------------------------------------*/
++void
++acx_set_timer(wlandevice_t *priv, int timeout_us)
++{
++ FN_ENTER;
++
++ acxlog(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout_us/1000);
++ if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ printk("attempt to set the timer "
++ "when the card interface is not up!\n");
++ goto end;
++ }
++
++ /* first check if the timer was already initialized, THEN modify it */
++ if (priv->mgmt_timer.function) {
++ mod_timer(&priv->mgmt_timer,
++ jiffies + (timeout_us * HZ / 1000000));
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_assocresp
++*
++* We are an AP here
++*----------------------------------------------------------------*/
++static const u8
++dot11ratebyte[] = {
++ DOT11RATEBYTE_1,
++ DOT11RATEBYTE_2,
++ DOT11RATEBYTE_5_5,
++ DOT11RATEBYTE_6_G,
++ DOT11RATEBYTE_9_G,
++ DOT11RATEBYTE_11,
++ DOT11RATEBYTE_12_G,
++ DOT11RATEBYTE_18_G,
++ DOT11RATEBYTE_22,
++ DOT11RATEBYTE_24_G,
++ DOT11RATEBYTE_36_G,
++ DOT11RATEBYTE_48_G,
++ DOT11RATEBYTE_54_G,
++};
++
++static int
++find_pos(const u8 *p, int size, u8 v)
++{
++ int i;
++ for (i = 0; i < size; i++)
++ if (p[i] == v)
++ return i;
++ /* printk a message about strange byte? */
++ return 0;
++}
++
++static void
++add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate)
++{
++ while (len--) {
++ int n = 1 << find_pos(dot11ratebyte,
++ sizeof(dot11ratebyte), *ratevec & 0x7f);
++ if (*ratevec & 0x80)
++ *brate |= n;
++ *orate |= n;
++ ratevec++;
++ }
++}
++
++static int
++acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct assocresp_frame_body *body;
++ u8 *p;
++ const u8 *da;
++ /* const u8 *sa; */
++ const u8 *bssid;
++ client_t *clt;
++
++ FN_ENTER;
++
++ /* sa = req->hdr->a1; */
++ da = req->hdr->a2;
++ bssid = req->hdr->a3;
++
++ clt = acx_l_sta_list_get(priv, da);
++ if (!clt)
++ goto ok;
++
++ /* Assoc without auth is a big no-no */
++ /* Let's be liberal: if already assoc'ed STA sends assoc req again,
++ ** we won't be rude */
++ if (clt->used != CLIENT_AUTHENTICATED_2
++ && clt->used != CLIENT_ASSOCIATED_3) {
++ acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH);
++ goto bad;
++ }
++
++ clt->used = CLIENT_ASSOCIATED_3;
++
++ if (clt->aid == 0) {
++ clt->aid = ++priv->aid;
++ }
++ clt->cap_info = ieee2host16(*(req->cap_info));
++ /* We cheat here a bit. We don't really care which rates are flagged
++ ** as basic by the client, so we stuff them in single ratemask */
++ clt->rate_cap = 0;
++ if (req->supp_rates)
++ add_bits_to_ratemasks(req->supp_rates->rates,
++ req->supp_rates->len, &clt->rate_cap, &clt->rate_cap);
++ if (req->ext_rates)
++ add_bits_to_ratemasks(req->ext_rates->rates,
++ req->ext_rates->len, &clt->rate_cap, &clt->rate_cap);
++ /* We can check that client supports all basic rates,
++ ** and deny assoc if not. But let's be liberal, right? ;) */
++ clt->rate_cfg = clt->rate_cap & priv->rate_oper;
++ if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper);
++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
++ clt->fallback_count = clt->stepup_count = 0;
++ clt->ignore_count = 16;
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto bad;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_ASSOCRESPi;
++ head->dur = req->hdr->dur;
++ MAC_COPY(head->da, da);
++ /* MAC_COPY(head->sa, sa); */
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, bssid);
++ head->seq = req->hdr->seq;
++
++ body->cap_info = host2ieee16(priv->capabilities);
++ body->status = host2ieee16(0);
++ body->aid = host2ieee16(clt->aid);
++ p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len,
++ priv->rate_supported);
++ p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len,
++ priv->rate_supported);
++
++ acx_l_tx_data(priv, tx, p - (u8*)head);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_reassocresp
++
++You may be wondering, just like me, what a hell is ReAuth.
++In practice it was seen sent by STA when STA feels like losing connection.
++
++[802.11]
++
++5.4.2.3 Reassociation
++
++Association is sufficient for no-transition message delivery between
++IEEE 802.11 stations. Additional functionality is needed to support
++BSS-transition mobility. The additional required functionality
++is provided by the reassociation service. Reassociation is a DSS.
++The reassociation service is invoked to 'move' a current association
++from one AP to another. This keeps the DS informed of the current
++mapping between AP and STA as the station moves from BSS to BSS within
++an ESS. Reassociation also enables changing association attributes
++of an established association while the STA remains associated with
++the same AP. Reassociation is always initiated by the mobile STA.
++
++5.4.3.1 Authentication
++...
++A STA may be authenticated with many other STAs at any given instant.
++
++5.4.3.1.1 Preauthentication
++
++Because the authentication process could be time-consuming (depending
++on the authentication protocol in use), the authentication service can
++be invoked independently of the association service. Preauthentication
++is typically done by a STA while it is already associated with an AP
++(with which it previously authenticated). IEEE 802.11 does not require
++that STAs preauthenticate with APs. However, authentication is required
++before an association can be established. If the authentication is left
++until reassociation time, this may impact the speed with which a STA can
++reassociate between APs, limiting BSS-transition mobility performance.
++The use of preauthentication takes the authentication service overhead
++out of the time-critical reassociation process.
++
++5.7.3 Reassociation
++
++For a STA to reassociate, the reassociation service causes the following
++message to occur:
++
++ Reassociation request
++
++* Message type: Management
++* Message subtype: Reassociation request
++* Information items:
++ - IEEE address of the STA
++ - IEEE address of the AP with which the STA will reassociate
++ - IEEE address of the AP with which the STA is currently associated
++ - ESSID
++* Direction of message: From STA to 'new' AP
++
++The address of the current AP is included for efficiency. The inclusion
++of the current AP address facilitates MAC reassociation to be independent
++of the DS implementation.
++
++ Reassociation response
++* Message type: Management
++* Message subtype: Reassociation response
++* Information items:
++ - Result of the requested reassociation. (success/failure)
++ - If the reassociation is successful, the response shall include the AID.
++* Direction of message: From AP to STA
++
++7.2.3.6 Reassociation Request frame format
++
++The frame body of a management frame of subtype Reassociation Request
++contains the information shown in Table 9.
++
++Table 9 Reassociation Request frame body
++Order Information
++1 Capability information
++2 Listen interval
++3 Current AP address
++4 SSID
++5 Supported rates
++
++7.2.3.7 Reassociation Response frame format
++
++The frame body of a management frame of subtype Reassociation Response
++contains the information shown in Table 10.
++
++Table 10 Reassociation Response frame body
++Order Information
++1 Capability information
++2 Status code
++3 Association ID (AID)
++4 Supported rates
++
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct reassocresp_frame_body *body;
++ u8 *p;
++ const u8 *da;
++ /* const u8 *sa; */
++ const u8 *bssid;
++ client_t *clt;
++
++ FN_ENTER;
++
++ /* sa = req->hdr->a1; */
++ da = req->hdr->a2;
++ bssid = req->hdr->a3;
++
++ /* Must be already authenticated, so it must be in the list */
++ clt = acx_l_sta_list_get(priv, da);
++ if (!clt)
++ goto ok;
++
++ /* Assoc without auth is a big no-no */
++ /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */
++ if (clt->used != CLIENT_AUTHENTICATED_2
++ && clt->used != CLIENT_ASSOCIATED_3) {
++ acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH);
++ goto bad;
++ }
++
++ clt->used = CLIENT_ASSOCIATED_3;
++ if (clt->aid == 0) {
++ clt->aid = ++priv->aid;
++ }
++ if (req->cap_info)
++ clt->cap_info = ieee2host16(*(req->cap_info));
++ /* We cheat here a bit. We don't really care which rates are flagged
++ ** as basic by the client, so we stuff them in single ratemask */
++ clt->rate_cap = 0;
++ if (req->supp_rates)
++ add_bits_to_ratemasks(req->supp_rates->rates,
++ req->supp_rates->len, &clt->rate_cap, &clt->rate_cap);
++ if (req->ext_rates)
++ add_bits_to_ratemasks(req->ext_rates->rates,
++ req->ext_rates->len, &clt->rate_cap, &clt->rate_cap);
++ /* We can check that client supports all basic rates,
++ ** and deny assoc if not. But let's be liberal, right? ;) */
++ clt->rate_cfg = clt->rate_cap & priv->rate_oper;
++ if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper);
++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
++ clt->fallback_count = clt->stepup_count = 0;
++ clt->ignore_count = 16;
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto ok;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto ok;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_REASSOCRESPi;
++ head->dur = req->hdr->dur;
++ MAC_COPY(head->da, da);
++ /* MAC_COPY(head->sa, sa); */
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, bssid);
++ head->seq = req->hdr->seq;
++
++ /* IEs: 1. caps */
++ body->cap_info = host2ieee16(priv->capabilities);
++ /* 2. status code */
++ body->status = host2ieee16(0);
++ /* 3. AID */
++ body->aid = host2ieee16(clt->aid);
++ /* 4. supp rates */
++ p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len,
++ priv->rate_supported);
++ /* 5. ext supp rates */
++ p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len,
++ priv->rate_supported);
++
++ acx_l_tx_data(priv, tx, p - (u8*)head);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_disassoc_from_sta
++*----------------------------------------------------------------*/
++static void
++acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req)
++{
++ const u8 *ta;
++ client_t *clt;
++
++ FN_ENTER;
++
++ ta = req->hdr->a2;
++ clt = acx_l_sta_list_get(priv, ta);
++ if (!clt)
++ goto end;
++
++ if (clt->used != CLIENT_ASSOCIATED_3
++ && clt->used != CLIENT_AUTHENTICATED_2) {
++ /* it's disassociating, but it's
++ ** not even authenticated! Let it know that */
++ acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc "
++ "req but it is not even auth'ed! sending deauth\n");
++ acx_l_transmit_deauthen(priv, ta,
++ WLAN_MGMT_REASON_CLASS2_NONAUTH);
++ clt->used = CLIENT_EXIST_1;
++ } else {
++ /* mark it as auth'ed only */
++ clt->used = CLIENT_AUTHENTICATED_2;
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_deauthen_from_sta
++*----------------------------------------------------------------*/
++static void
++acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req)
++{
++ const wlan_hdr_t *hdr;
++ client_t *client;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (acx_debug & L_ASSOC) {
++ acx_print_mac("DEAUTHEN priv->addr=", priv->dev_addr, " ");
++ acx_print_mac("a1", hdr->a1, " ");
++ acx_print_mac("a2", hdr->a2, " ");
++ acx_print_mac("a3", hdr->a3, " ");
++ acx_print_mac("priv->bssid", priv->bssid, "\n");
++ }
++
++ if (!mac_is_equal(priv->dev_addr, hdr->a1)) {
++ goto end;
++ }
++
++ acxlog_mac(L_DEBUG, "STA ", hdr->a2, " sent us deauthen packet\n");
++
++ client = acx_l_sta_list_get(priv, hdr->a2);
++ if (!client) {
++ goto end;
++ }
++ client->used = CLIENT_EXIST_1;
++end:
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_disassoc_from_ap
++*----------------------------------------------------------------*/
++static void
++acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req)
++{
++ FN_ENTER;
++
++ if (!priv->ap_client) {
++ /* Hrm, we aren't assoc'ed yet anyhow... */
++ goto end;
++ }
++ if (mac_is_equal(priv->dev_addr, req->hdr->a1)) {
++ acx_l_transmit_deauthen(priv, priv->bssid,
++ WLAN_MGMT_REASON_DEAUTH_LEAVING);
++ /* Start scan anew */
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_deauth_from_ap
++*----------------------------------------------------------------*/
++static void
++acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req)
++{
++ FN_ENTER;
++
++ if (!priv->ap_client) {
++ /* Hrm, we aren't assoc'ed yet anyhow... */
++ goto end;
++ }
++ /* Chk: is ta is verified to be from our AP? */
++ if (mac_is_equal(priv->dev_addr, req->hdr->a1)) {
++ acxlog(L_DEBUG, "AP sent us deauth packet\n");
++ /* not needed: acx_set_status(priv, ACX_STATUS_1_SCANNING) */
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/*------------------------------------------------------------------------------
++ * acx_l_rx
++ *
++ * The end of the Rx path. Pulls data from a rxhostdesc into a socket
++ * buffer and feeds it to the network stack via netif_rx().
++ *
++ * Arguments:
++ * rxdesc: the rxhostdesc to pull the data from
++ * priv: the acx100 private struct of the interface
++ *----------------------------------------------------------------------------*/
++static void
++acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ FN_ENTER;
++ if (likely(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ struct sk_buff *skb;
++ skb = acx_rxbuf_to_ether(priv, rxbuf);
++ if (likely(skb)) {
++ netif_rx(skb);
++ priv->netdev->last_rx = jiffies;
++ priv->stats.rx_packets++;
++ priv->stats.rx_bytes += skb->len;
++ }
++ }
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_data_frame_master
++*----------------------------------------------------------------*/
++static int
++acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ struct wlan_hdr *hdr;
++ struct tx *tx;
++ void *txbuf;
++ int len;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ hdr = acx_get_wlan_hdr(priv, rxbuf);
++
++ switch (WF_FC_FROMTODSi & hdr->fc) {
++ case 0:
++ case WF_FC_FROMDSi:
++ acxlog(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n");
++ goto done;
++ case WF_FC_TODSi:
++ break;
++ default: /* WF_FC_FROMTODSi */
++ acxlog(L_DEBUG, "wds data frame ignored (todo)\n");
++ goto done;
++ }
++
++ /* check if it is our BSSID, if not, leave */
++ if (!mac_is_equal(priv->bssid, hdr->a1)) {
++ goto done;
++ }
++
++ if (mac_is_equal(priv->dev_addr, hdr->a3)) {
++ /* this one is for us */
++ acx_l_rx(priv, rxbuf);
++ } else {
++ if (mac_is_bcast(hdr->a3)) {
++ /* this one is bcast, rx it too */
++ acx_l_rx(priv, rxbuf);
++ }
++ tx = acx_l_alloc_tx(priv);
++ if (!tx) {
++ goto fail;
++ }
++ /* repackage, tx, and hope it someday reaches its destination */
++ /* order is important, we do it in-place */
++ MAC_COPY(hdr->a1, hdr->a3);
++ MAC_COPY(hdr->a3, hdr->a2);
++ MAC_COPY(hdr->a2, priv->bssid);
++ /* To_DS = 0, From_DS = 1 */
++ hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi;
++
++ len = RXBUF_BYTES_RCVD(rxbuf);
++ txbuf = acx_l_get_txbuf(priv, tx);
++ if (txbuf) {
++ memcpy(txbuf, &rxbuf->hdr_a3, len);
++ acx_l_tx_data(priv, tx, len);
++ }
++ }
++done:
++ result = OK;
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_data_frame_client
++*----------------------------------------------------------------*/
++static int
++acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ const u8 *da, *bssid;
++ const wlan_hdr_t *hdr;
++ netdevice_t *dev = priv->netdev;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ if (ACX_STATUS_4_ASSOCIATED != priv->status)
++ goto drop;
++
++ hdr = acx_get_wlan_hdr(priv, rxbuf);
++
++ switch (WF_FC_FROMTODSi & hdr->fc) {
++ case 0:
++ if (priv->mode != ACX_MODE_0_ADHOC) {
++ acxlog(L_DEBUG, "adhoc->adhoc data frame ignored\n");
++ goto drop;
++ }
++ bssid = hdr->a3;
++ break;
++ case WF_FC_FROMDSi:
++ if (priv->mode != ACX_MODE_2_STA) {
++ acxlog(L_DEBUG, "ap->sta data frame ignored\n");
++ goto drop;
++ }
++ bssid = hdr->a2;
++ break;
++ case WF_FC_TODSi:
++ acxlog(L_DEBUG, "sta->ap data frame ignored\n");
++ goto drop;
++ default: /* WF_FC_FROMTODSi: wds->wds */
++ acxlog(L_DEBUG, "wds data frame ignored (todo)\n");
++ goto drop;
++ }
++
++ da = hdr->a1;
++
++ if (unlikely(acx_debug & L_DEBUG)) {
++ acx_print_mac("rx: da=", da, "");
++ acx_print_mac(" bssid=", bssid, "");
++ acx_print_mac(" priv->bssid=", priv->bssid, "");
++ acx_print_mac(" priv->addr=", priv->dev_addr, "\n");
++ }
++
++ /* promiscuous mode --> receive all packets */
++ if (unlikely(dev->flags & IFF_PROMISC))
++ goto process;
++
++ /* FIRST, check if it is our BSSID */
++ if (!mac_is_equal(priv->bssid, bssid)) {
++ /* is not our BSSID, so bail out */
++ goto drop;
++ }
++
++ /* then, check if it is our address */
++ if (mac_is_equal(priv->dev_addr, da)) {
++ goto process;
++ }
++
++ /* then, check if it is broadcast */
++ if (mac_is_bcast(da)) {
++ goto process;
++ }
++
++ if (mac_is_mcast(da)) {
++ /* unconditionally receive all multicasts */
++ if (dev->flags & IFF_ALLMULTI)
++ goto process;
++
++ /* FIXME: check against the list of
++ * multicast addresses that are configured
++ * for the interface (ifconfig) */
++ acxlog(L_XFER, "FIXME: multicast packet, need to check "
++ "against a list of multicast addresses "
++ "(to be created!); accepting packet for now\n");
++ /* for now, just accept it here */
++ goto process;
++ }
++
++ acxlog(L_DEBUG, "rx: foreign packet, dropping\n");
++ goto drop;
++process:
++ /* receive packet */
++ acx_l_rx(priv, rxbuf);
++
++ result = OK;
++drop:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_mgmt_frame
++*
++* Theory of operation: mgmt packet gets parsed (to make it easy
++* to access variable-sized IEs), results stored in 'parsed'.
++* Then we react to the packet.
++* NB: wlan_mgmt_decode_XXX are dev-independent (shoudnt have been named acx_XXX)
++*----------------------------------------------------------------*/
++typedef union parsed_mgmt_req {
++ wlan_fr_mgmt_t mgmt;
++ wlan_fr_assocreq_t assocreq;
++ wlan_fr_reassocreq_t reassocreq;
++ wlan_fr_assocresp_t assocresp;
++ wlan_fr_reassocresp_t reassocresp;
++ wlan_fr_beacon_t beacon;
++ wlan_fr_disassoc_t disassoc;
++ wlan_fr_authen_t authen;
++ wlan_fr_deauthen_t deauthen;
++ wlan_fr_proberesp_t proberesp;
++} parsed_mgmt_req_t;
++
++void BUG_excessive_stack_usage(void);
++
++static int
++acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */
++ wlan_hdr_t *hdr;
++ int adhoc, sta_scan, sta, ap;
++ int len;
++
++ if (sizeof(parsed) > 256)
++ BUG_excessive_stack_usage();
++
++ FN_ENTER;
++
++ hdr = acx_get_wlan_hdr(priv, rxbuf);
++
++ /* Management frames never have these set */
++ if (WF_FC_FROMTODSi & hdr->fc) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++
++ len = RXBUF_BYTES_RCVD(rxbuf);
++ if (WF_FC_ISWEPi & hdr->fc)
++ len -= 0x10;
++
++ adhoc = (priv->mode == ACX_MODE_0_ADHOC);
++ sta_scan = ((priv->mode == ACX_MODE_2_STA)
++ && (priv->status != ACX_STATUS_4_ASSOCIATED));
++ sta = ((priv->mode == ACX_MODE_2_STA)
++ && (priv->status == ACX_STATUS_4_ASSOCIATED));
++ ap = (priv->mode == ACX_MODE_3_AP);
++
++ switch (WF_FC_FSTYPEi & hdr->fc) {
++ /* beacons first, for speed */
++ case WF_FSTYPE_BEACONi:
++ memset(&parsed.beacon, 0, sizeof(parsed.beacon));
++ parsed.beacon.hdr = hdr;
++ parsed.beacon.len = len;
++ if (acx_debug & L_DATA) {
++ printk("beacon len:%d fc:%04X dur:%04X seq:%04X",
++ len, hdr->fc, hdr->dur, hdr->seq);
++ acx_print_mac(" a1:", hdr->a1, "");
++ acx_print_mac(" a2:", hdr->a2, "");
++ acx_print_mac(" a3:", hdr->a3, "\n");
++ }
++ wlan_mgmt_decode_beacon(&parsed.beacon);
++ /* beacon and probe response are very similar, so... */
++ acx_l_process_probe_response(priv, &parsed.beacon, rxbuf);
++ break;
++ case WF_FSTYPE_ASSOCREQi:
++ if (!ap)
++ break;
++ memset(&parsed.assocreq, 0, sizeof(parsed.assocreq));
++ parsed.assocreq.hdr = hdr;
++ parsed.assocreq.len = len;
++ wlan_mgmt_decode_assocreq(&parsed.assocreq);
++ if (mac_is_equal(hdr->a1, priv->bssid)
++ && mac_is_equal(hdr->a3, priv->bssid)) {
++ acx_l_transmit_assocresp(priv, &parsed.assocreq);
++ }
++ break;
++ case WF_FSTYPE_REASSOCREQi:
++ if (!ap)
++ break;
++ memset(&parsed.assocreq, 0, sizeof(parsed.assocreq));
++ parsed.assocreq.hdr = hdr;
++ parsed.assocreq.len = len;
++ wlan_mgmt_decode_assocreq(&parsed.assocreq);
++ /* reassocreq and assocreq are equivalent */
++ acx_l_transmit_reassocresp(priv, &parsed.reassocreq);
++ break;
++ case WF_FSTYPE_ASSOCRESPi:
++ if (!sta_scan)
++ break;
++ memset(&parsed.assocresp, 0, sizeof(parsed.assocresp));
++ parsed.assocresp.hdr = hdr;
++ parsed.assocresp.len = len;
++ wlan_mgmt_decode_assocresp(&parsed.assocresp);
++ acx_l_process_assocresp(priv, &parsed.assocresp);
++ break;
++ case WF_FSTYPE_REASSOCRESPi:
++ if (!sta_scan)
++ break;
++ memset(&parsed.assocresp, 0, sizeof(parsed.assocresp));
++ parsed.assocresp.hdr = hdr;
++ parsed.assocresp.len = len;
++ wlan_mgmt_decode_assocresp(&parsed.assocresp);
++ acx_l_process_reassocresp(priv, &parsed.reassocresp);
++ break;
++ case WF_FSTYPE_PROBEREQi:
++ if (ap || adhoc) {
++ /* FIXME: since we're supposed to be an AP,
++ ** we need to return a Probe Response packet.
++ ** Currently firmware is doing it for us,
++ ** but firmware is buggy! See comment elsewhere --vda */
++ }
++ break;
++ case WF_FSTYPE_PROBERESPi:
++ memset(&parsed.proberesp, 0, sizeof(parsed.proberesp));
++ parsed.proberesp.hdr = hdr;
++ parsed.proberesp.len = len;
++ wlan_mgmt_decode_proberesp(&parsed.proberesp);
++ acx_l_process_probe_response(priv, &parsed.proberesp, rxbuf);
++ break;
++ case 6:
++ case 7:
++ /* exit */
++ break;
++ case WF_FSTYPE_ATIMi:
++ /* exit */
++ break;
++ case WF_FSTYPE_DISASSOCi:
++ if (!sta && !ap)
++ break;
++ memset(&parsed.disassoc, 0, sizeof(parsed.disassoc));
++ parsed.disassoc.hdr = hdr;
++ parsed.disassoc.len = len;
++ wlan_mgmt_decode_disassoc(&parsed.disassoc);
++ if (sta)
++ acx_l_process_disassoc_from_ap(priv, &parsed.disassoc);
++ else
++ acx_l_process_disassoc_from_sta(priv, &parsed.disassoc);
++ break;
++ case WF_FSTYPE_AUTHENi:
++ if (!sta_scan && !ap)
++ break;
++ memset(&parsed.authen, 0, sizeof(parsed.authen));
++ parsed.authen.hdr = hdr;
++ parsed.authen.len = len;
++ wlan_mgmt_decode_authen(&parsed.authen);
++ acx_l_process_authen(priv, &parsed.authen);
++ break;
++ case WF_FSTYPE_DEAUTHENi:
++ if (!sta && !ap)
++ break;
++ memset(&parsed.deauthen, 0, sizeof(parsed.deauthen));
++ parsed.deauthen.hdr = hdr;
++ parsed.deauthen.len = len;
++ wlan_mgmt_decode_deauthen(&parsed.deauthen);
++ if (sta)
++ acx_l_process_deauth_from_ap(priv, &parsed.deauthen);
++ else
++ acx_l_process_deauth_from_sta(priv, &parsed.deauthen);
++ break;
++ }
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#ifdef UNUSED
++/*----------------------------------------------------------------
++* acx_process_class_frame
++*
++* Called from IRQ context only
++*----------------------------------------------------------------*/
++static int
++acx_process_class_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala)
++{
++ return OK;
++}
++#endif
++
++
++/*----------------------------------------------------------------
++* acx_l_process_NULL_frame
++*----------------------------------------------------------------*/
++#ifdef BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL
++static int
++acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala)
++{
++ const signed char *esi;
++ const u8 *ebx;
++ const wlan_hdr_t *hdr;
++ const client_t *client;
++ int result = NOT_OK;
++
++ hdr = acx_get_wlan_hdr(priv, rxbuf);
++
++ switch (WF_FC_FROMTODSi & hdr->fc) {
++ case 0:
++ esi = hdr->a1;
++ ebx = hdr->a2;
++ break;
++ case WF_FC_FROMDSi:
++ esi = hdr->a1;
++ ebx = hdr->a3;
++ break;
++ case WF_FC_TODSi:
++ esi = hdr->a1;
++ ebx = hdr->a2;
++ break;
++ default: /* WF_FC_FROMTODSi */
++ esi = hdr->a1; /* added by me! --vda */
++ ebx = hdr->a2;
++ }
++
++ if (esi[0x0] < 0) {
++ result = OK;
++ goto done;
++ }
++
++ client = acx_l_sta_list_get(priv, ebx);
++ if (client)
++ result = NOT_OK;
++ else {
++#ifdef IS_IT_BROKEN
++ acxlog(L_DEBUG|L_XFER, "<transmit_deauth 7>\n");
++ acx_l_transmit_deauthen(priv, ebx,
++ WLAN_MGMT_REASON_CLASS2_NONAUTH);
++#else
++ acxlog(L_DEBUG, "received NULL frame from unknown client! "
++ "We really shouldn't send deauthen here, right?\n");
++#endif
++ result = OK;
++ }
++done:
++ return result;
++}
++#endif
++
++
++/*----------------------------------------------------------------
++* acx_l_process_probe_response
++*----------------------------------------------------------------*/
++static int
++acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req,
++ const rxbuffer_t *rxbuf)
++{
++ struct client *bss;
++ wlan_hdr_t *hdr;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (mac_is_equal(hdr->a3, priv->dev_addr)) {
++ acxlog(L_ASSOC, "huh, scan found our own MAC!?\n");
++ goto ok; /* just skip this one silently */
++ }
++
++ bss = acx_l_sta_list_get_or_add(priv, hdr->a2);
++
++ /* NB: be careful modifying bss data! It may be one
++ ** of already known clients (like our AP is we are a STA)
++ ** Thus do not blindly modify e.g. current ratemask! */
++
++ if (STA_LIST_ADD_CAN_FAIL && !bss) {
++ /* uh oh, we found more sites/stations than we can handle with
++ * our current setup: pull the emergency brake and stop scanning! */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_STOP_SCAN);
++ /* TODO: a nice comment what below call achieves --vda */
++ acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH);
++ goto ok;
++ }
++ /* NB: get_or_add already filled bss->address = hdr->a2 */
++ MAC_COPY(bss->bssid, hdr->a3);
++
++ /* copy the ESSID element */
++ if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) {
++ bss->essid_len = req->ssid->len;
++ memcpy(bss->essid, req->ssid->ssid, req->ssid->len);
++ bss->essid[req->ssid->len] = '\0';
++ } else {
++ /* Either no ESSID IE or oversized one */
++ printk("%s: received packet has bogus ESSID\n",
++ priv->netdev->name);
++ }
++
++ if (req->ds_parms)
++ bss->channel = req->ds_parms->curr_ch;
++ if (req->cap_info)
++ bss->cap_info = ieee2host16(*req->cap_info);
++
++ bss->sir = acx_signal_to_winlevel(rxbuf->phy_level);
++ bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr);
++
++ bss->rate_cap = 0; /* operational mask */
++ bss->rate_bas = 0; /* basic mask */
++ if (req->supp_rates)
++ add_bits_to_ratemasks(req->supp_rates->rates,
++ req->supp_rates->len, &bss->rate_bas, &bss->rate_cap);
++ if (req->ext_rates)
++ add_bits_to_ratemasks(req->ext_rates->rates,
++ req->ext_rates->len, &bss->rate_bas, &bss->rate_cap);
++ /* Fix up any possible bogosity - code elsewhere
++ * is not expecting empty masks */
++ if (!bss->rate_cap)
++ bss->rate_cap = priv->rate_basic;
++ if (!bss->rate_bas)
++ bss->rate_bas = 1 << lowest_bit(bss->rate_cap);
++ if (!bss->rate_cur)
++ bss->rate_cur = 1 << lowest_bit(bss->rate_bas);
++
++ /* People moan about this being too noisy at L_ASSOC */
++ acxlog(L_DEBUG,
++ "found %s: ESSID='%s' ch=%d "
++ "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n",
++ (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP",
++ bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info,
++ bss->sir, bss->snr);
++ok:
++ FN_EXIT0;
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_assocresp
++*----------------------------------------------------------------*/
++static int
++acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req)
++{
++ const wlan_hdr_t *hdr;
++ int res = OK;
++
++ FN_ENTER;
++ hdr = req->hdr;
++
++ if ((ACX_MODE_2_STA == priv->mode)
++ && mac_is_equal(priv->dev_addr, hdr->a1)) {
++ u16 st = ieee2host16(*(req->status));
++ if (WLAN_MGMT_STATUS_SUCCESS == st) {
++ priv->aid = ieee2host16(*(req->aid));
++ /* tell the card we are associated when we are out of interrupt context */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_ASSOCIATE);
++ } else {
++
++ /* TODO: we shall delete peer from sta_list, and try other candidates... */
++
++ printk("%s: association FAILED: peer sent "
++ "response code %d (%s)\n",
++ priv->netdev->name, st, get_status_string(st));
++ res = NOT_OK;
++ }
++ }
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_reassocresp
++*----------------------------------------------------------------*/
++static int
++acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req)
++{
++ const wlan_hdr_t *hdr;
++ int result = NOT_OK;
++ u16 st;
++
++ FN_ENTER;
++ hdr = req->hdr;
++
++ if (!mac_is_equal(priv->dev_addr, hdr->a1)) {
++ goto end;
++ }
++ st = ieee2host16(*(req->status));
++ if (st == WLAN_MGMT_STATUS_SUCCESS) {
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
++ result = OK;
++ } else {
++ printk("%s: reassociation FAILED: peer sent "
++ "response code %d (%s)\n",
++ priv->netdev->name, st, get_status_string(st));
++ }
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_process_authen
++*
++* Called only in STA_SCAN or AP mode
++*----------------------------------------------------------------*/
++static int
++acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req)
++{
++ const wlan_hdr_t *hdr;
++ client_t *clt;
++ wlan_ie_challenge_t *chal;
++ u16 alg, seq, status;
++ int ap, result;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (acx_debug & L_ASSOC) {
++ acx_print_mac("AUTHEN priv->addr=", priv->dev_addr, " ");
++ acx_print_mac("a1=", hdr->a1, " ");
++ acx_print_mac("a2=", hdr->a2, " ");
++ acx_print_mac("a3=", hdr->a3, " ");
++ acx_print_mac("priv->bssid=", priv->bssid, "\n");
++ }
++
++ if (!mac_is_equal(priv->dev_addr, hdr->a1)
++ || !mac_is_equal(priv->bssid, hdr->a3)) {
++ result = OK;
++ goto end;
++ }
++
++ alg = ieee2host16(*(req->auth_alg));
++ seq = ieee2host16(*(req->auth_seq));
++ status = ieee2host16(*(req->status));
++
++ ap = (priv->mode == ACX_MODE_3_AP);
++
++ if (priv->auth_alg <= 1) {
++ if (priv->auth_alg != alg) {
++ acxlog(L_ASSOC, "authentication algorithm mismatch: "
++ "want: %d, req: %d\n", priv->auth_alg, alg);
++ result = NOT_OK;
++ goto end;
++ }
++ }
++ acxlog(L_ASSOC, "algorithm is ok\n");
++
++ if (ap) {
++ clt = acx_l_sta_list_get_or_add(priv, hdr->a2);
++ if (STA_LIST_ADD_CAN_FAIL && !clt) {
++ acxlog(L_ASSOC, "could not allocate room for client\n");
++ result = NOT_OK;
++ goto end;
++ }
++ } else {
++ clt = priv->ap_client;
++ if (!mac_is_equal(clt->address, hdr->a2)) {
++ printk("%s: malformed auth frame from AP?!\n",
++ priv->netdev->name);
++ result = NOT_OK;
++ goto end;
++ }
++ }
++
++ /* now check which step in the authentication sequence we are
++ * currently in, and act accordingly */
++ acxlog(L_ASSOC, "acx_process_authen auth seq step %d\n", seq);
++ switch (seq) {
++ case 1:
++ if (!ap)
++ break;
++ acx_l_transmit_authen2(priv, req, clt);
++ break;
++ case 2:
++ if (ap)
++ break;
++ if (status == WLAN_MGMT_STATUS_SUCCESS) {
++ if (alg == WLAN_AUTH_ALG_OPENSYSTEM) {
++ acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED);
++ acx_l_transmit_assoc_req(priv);
++ } else
++ if (alg == WLAN_AUTH_ALG_SHAREDKEY) {
++ acx_l_transmit_authen3(priv, req);
++ }
++ } else {
++ printk("%s: auth FAILED: peer sent "
++ "response code %d (%s), "
++ "still waiting for authentication\n",
++ priv->netdev->name,
++ status, get_status_string(status));
++ acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH);
++ }
++ break;
++ case 3:
++ if (!ap)
++ break;
++ if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY)
++ || (alg != WLAN_AUTH_ALG_SHAREDKEY)
++ || (clt->auth_step != 2))
++ break;
++ chal = req->challenge;
++ if (!chal
++ || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN)
++ || (chal->eid != WLAN_EID_CHALLENGE)
++ || (chal->len != WLAN_CHALLENGE_LEN)
++ )
++ break;
++ acx_l_transmit_authen4(priv, req);
++ MAC_COPY(clt->address, hdr->a2);
++ clt->used = CLIENT_AUTHENTICATED_2;
++ clt->auth_step = 4;
++ clt->seq = ieee2host16(hdr->seq);
++ break;
++ case 4:
++ if (ap)
++ break;
++ /* ok, we're through: we're authenticated. Woohoo!! */
++ acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED);
++ acxlog(L_ASSOC, "Authenticated!\n");
++ /* now that we're authenticated, request association */
++ acx_l_transmit_assoc_req(priv);
++ break;
++ }
++ result = NOT_OK;
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_gen_challenge
++*----------------------------------------------------------------*/
++static void
++acx_gen_challenge(wlan_ie_challenge_t* d)
++{
++ FN_ENTER;
++ d->eid = WLAN_EID_CHALLENGE;
++ d->len = WLAN_CHALLENGE_LEN;
++ get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN);
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_deauthen
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct deauthen_frame_body *body;
++
++ FN_ENTER;
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto bad;
++ body = (void*)(head + 1);
++
++ head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi);
++ head->dur = 0;
++ MAC_COPY(head->da, addr);
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, priv->bssid);
++ head->seq = 0;
++
++ acxlog(L_DEBUG|L_ASSOC|L_XFER,
++ "sending deauthen to "MACSTR" for %d\n",
++ MAC(addr), reason);
++
++ body->reason = host2ieee16(reason);
++
++ /* body is fixed size here, but beware of cutting-and-pasting this -
++ ** do not use sizeof(*body) for variable sized mgmt packets! */
++ acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body));
++
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_authen1
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_authen1(wlandevice_t *priv)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++
++ FN_ENTER;
++
++ acxlog(L_ASSOC, "Sending authentication1 request, "
++ "awaiting response!\n");
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto bad;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_AUTHENi;
++ head->dur = host2ieee16(0x8000);
++ MAC_COPY(head->da, priv->bssid);
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, priv->bssid);
++ head->seq = 0;
++
++ body->auth_alg = host2ieee16(priv->auth_alg);
++ body->auth_seq = host2ieee16(1);
++ body->status = host2ieee16(0);
++
++ acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2);
++
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_authen2
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req,
++ client_t *clt)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++ unsigned int packet_len;
++
++ FN_ENTER;
++
++ if (!clt)
++ goto ok;
++
++ MAC_COPY(clt->address, req->hdr->a2);
++#ifdef UNUSED
++ clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0);
++#endif
++ clt->auth_alg = ieee2host16(*(req->auth_alg));
++ clt->auth_step = 2;
++ clt->seq = ieee2host16(req->hdr->seq);
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto bad;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */
++ head->dur = req->hdr->dur;
++ MAC_COPY(head->da, req->hdr->a2);
++ /* MAC_COPY(head->sa, req->hdr->a1); */
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, req->hdr->a3);
++ head->seq = req->hdr->seq;
++
++ /* already in IEEE format, no endianness conversion */
++ body->auth_alg = *(req->auth_alg);
++ body->auth_seq = host2ieee16(2);
++ body->status = host2ieee16(0);
++
++ packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2;
++ if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) {
++ clt->used = CLIENT_AUTHENTICATED_2;
++ } else { /* shared key */
++ acx_gen_challenge(&body->challenge);
++ memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN);
++ packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN;
++ }
++
++ acxlog_mac(L_ASSOC|L_XFER,
++ "transmit_auth2: BSSID=", head->bssid, "\n");
++
++ acx_l_tx_data(priv, tx, packet_len);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_authen3
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++ unsigned int packet_len;
++
++ FN_ENTER;
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto ok;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto ok;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi;
++ /* FIXME: is this needed?? authen4 does it...
++ head->dur = req->hdr->dur;
++ head->seq = req->hdr->seq;
++ */
++ MAC_COPY(head->da, priv->bssid);
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, priv->bssid);
++
++ /* already in IEEE format, no endianness conversion */
++ body->auth_alg = *(req->auth_alg);
++ body->auth_seq = host2ieee16(3);
++ body->status = host2ieee16(0);
++ memcpy(&body->challenge, req->challenge, req->challenge->len + 2);
++ packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len;
++
++ acxlog(L_ASSOC|L_XFER, "transmit_authen3!\n");
++
++ acx_l_tx_data(priv, tx, packet_len);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_authen4
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++
++ FN_ENTER;
++
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto ok;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto ok;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */
++ head->dur = req->hdr->dur;
++ MAC_COPY(head->da, req->hdr->a2);
++ /* MAC_COPY(head->sa, req->hdr->a1); */
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, req->hdr->a3);
++ head->seq = req->hdr->seq;
++
++ /* already in IEEE format, no endianness conversion */
++ body->auth_alg = *(req->auth_alg);
++ body->auth_seq = host2ieee16(4);
++ body->status = host2ieee16(0);
++
++ acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_assoc_req
++*
++* priv->ap_client is a current candidate AP here
++*----------------------------------------------------------------*/
++static int
++acx_l_transmit_assoc_req(wlandevice_t *priv)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ u8 *body, *p, *prate;
++ unsigned int packet_len;
++ u16 cap;
++
++ FN_ENTER;
++
++ acxlog(L_ASSOC, "sending association request, "
++ "awaiting response. NOT ASSOCIATED YET\n");
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto bad;
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_ASSOCREQi;
++ head->dur = host2ieee16(0x8000);
++ MAC_COPY(head->da, priv->bssid);
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, priv->bssid);
++ head->seq = 0;
++
++ p = body;
++ /* now start filling the AssocReq frame body */
++
++ /* since this assoc request will most likely only get
++ * sent in the STA to AP case (and not when Ad-Hoc IBSS),
++ * the cap combination indicated here will thus be
++ * WF_MGMT_CAP_ESSi *always* (no IBSS ever)
++ * The specs are more than non-obvious on all that:
++ *
++ * 802.11 7.3.1.4 Capability Information field
++ ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within
++ ** Beacon or Probe Response management frames. STAs within an IBSS
++ ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted
++ ** Beacon or Probe Response management frames
++ **
++ ** APs set the Privacy subfield to 1 within transmitted Beacon,
++ ** Probe Response, Association Response, and Reassociation Response
++ ** if WEP is required for all data type frames within the BSS.
++ ** STAs within an IBSS set the Privacy subfield to 1 in Beacon
++ ** or Probe Response management frames if WEP is required
++ ** for all data type frames within the IBSS */
++
++ /* note that returning 0 will be refused by several APs...
++ * (so this indicates that you're probably supposed to
++ * "confirm" the ESS mode) */
++ cap = WF_MGMT_CAP_ESSi;
++
++ /* this one used to be a check on wep_restricted,
++ * but more likely it's wep_enabled instead */
++ if (priv->wep_enabled)
++ SET_BIT(cap, WF_MGMT_CAP_PRIVACYi);
++
++ /* Probably we can just set these always, because our hw is
++ ** capable of shortpre and PBCC --vda */
++ /* only ask for short preamble if the peer station supports it */
++ if (priv->ap_client->cap_info & WF_MGMT_CAP_SHORT)
++ SET_BIT(cap, WF_MGMT_CAP_SHORTi);
++ /* only ask for PBCC support if the peer station supports it */
++ if (priv->ap_client->cap_info & WF_MGMT_CAP_PBCC)
++ SET_BIT(cap, WF_MGMT_CAP_PBCCi);
++
++ /* IEs: 1. caps */
++ *(u16*)p = cap; p += 2;
++ /* 2. listen interval */
++ *(u16*)p = host2ieee16(priv->listen_interval); p += 2;
++ /* 3. ESSID */
++ p = wlan_fill_ie_ssid(p,
++ strlen(priv->essid_for_assoc), priv->essid_for_assoc);
++ /* 4. supp rates */
++ prate = p;
++ p = wlan_fill_ie_rates(p,
++ priv->rate_supported_len, priv->rate_supported);
++ /* 5. ext supp rates */
++ p = wlan_fill_ie_rates_ext(p,
++ priv->rate_supported_len, priv->rate_supported);
++
++ if (acx_debug & L_DEBUG) {
++ printk("association: rates element\n");
++ acx_dump_bytes(prate, p - prate);
++ }
++
++ /* calculate lengths */
++ packet_len = WLAN_HDR_A3_LEN + (p - body);
++
++ acxlog(L_ASSOC, "association: requesting caps 0x%04X, ESSID '%s'\n",
++ cap, priv->essid_for_assoc);
++
++ acx_l_tx_data(priv, tx, packet_len);
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_transmit_disassoc
++*
++* FIXME: looks like incomplete implementation of a helper:
++* acx_l_transmit_disassoc(priv, clt) - kick this client (we're an AP)
++* acx_l_transmit_disassoc(priv, NULL) - leave BSSID (we're a STA)
++*----------------------------------------------------------------*/
++#ifdef BROKEN
++int
++acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct disassoc_frame_body *body;
++
++ FN_ENTER;
++/* if (clt != NULL) { */
++ tx = acx_l_alloc_tx(priv);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(priv, tx);
++ if (!head)
++ goto bad;
++ body = (void*)(head + 1);
++
++/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */
++
++ head->fc = WF_FSTYPE_DISASSOCi;
++ head->dur = 0;
++ /* huh? It muchly depends on whether we're STA or AP...
++ ** sta->ap: da=bssid, sa=own, bssid=bssid
++ ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */
++ MAC_COPY(head->da, priv->bssid);
++ MAC_COPY(head->sa, priv->dev_addr);
++ MAC_COPY(head->bssid, priv->dev_addr);
++ head->seq = 0;
++
++ /* "Class 3 frame received from nonassociated station." */
++ body->reason = host2ieee16(7);
++
++ /* fixed size struct, ok to sizeof */
++ acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body));
++/* } */
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++#endif
++
++
++/*----------------------------------------------------------------
++* acx_s_complete_scan
++*
++* Called either from after_interrupt_task() if:
++* 1) there was Scan_Complete IRQ, or
++* 2) scanning expired in timer()
++* We need to decide which ESS or IBSS to join.
++* Iterates thru priv->sta_list:
++* if priv->ap is not bcast, will join only specified
++* ESS or IBSS with this bssid
++* checks peers' caps for ESS/IBSS bit
++* checks peers' SSID, allows exact match or hidden SSID
++* If station to join is chosen:
++* points priv->ap_client to the chosen struct client
++* sets priv->essid_for_assoc for future assoc attempt
++* Auth/assoc is not yet performed
++* Returns OK if there is no need to restart scan
++*----------------------------------------------------------------*/
++int
++acx_s_complete_scan(wlandevice_t *priv)
++{
++ struct client *bss;
++ unsigned long flags;
++ u16 needed_cap;
++ int i;
++ int idx_found = -1;
++ int result = OK;
++
++ FN_ENTER;
++
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */
++ break;
++ case ACX_MODE_2_STA:
++ needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */
++ break;
++ default:
++ printk("acx: driver bug: mode=%d in complete_scan()\n", priv->mode);
++ dump_stack();
++ goto end;
++ }
++
++ acx_lock(priv, flags);
++
++ /* TODO: sta_iterator hiding implementation would be nice here... */
++
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ bss = &priv->sta_list[i];
++ if (!bss->used) continue;
++
++ acxlog(L_ASSOC, "Scan Table: SSID='%s' CH=%d SIR=%d SNR=%d\n",
++ bss->essid, bss->channel, bss->sir, bss->snr);
++
++ if (!mac_is_bcast(priv->ap))
++ if (!mac_is_equal(bss->bssid, priv->ap))
++ continue; /* keep looking */
++
++ /* broken peer with no mode flags set? */
++ if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) {
++ printk("%s: strange peer "MACSTR" found with "
++ "neither ESS (AP) nor IBSS (Ad-Hoc) "
++ "capability - skipped\n",
++ priv->netdev->name, MAC(bss->address));
++ continue;
++ }
++ acxlog(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n",
++ bss->cap_info, needed_cap);
++
++ /* does peer station support what we need? */
++ if ((bss->cap_info & needed_cap) != needed_cap)
++ continue; /* keep looking */
++
++ /* strange peer with NO basic rates?! */
++ if (unlikely(!bss->rate_bas)) {
++ printk("%s: strange peer "MACSTR" with empty rate set "
++ "- skipped\n",
++ priv->netdev->name, MAC(bss->address));
++ continue;
++ }
++
++ /* do we support all basic rates of this peer? */
++ if ((bss->rate_bas & priv->rate_oper) != bss->rate_bas) {
++/* we probably need to have all rates as operational rates,
++ even in case of an 11M-only configuration */
++#ifdef THIS_IS_TROUBLESOME
++ printk("%s: peer "MACSTR": incompatible basic rates "
++ "(AP requests 0x%04X, we have 0x%04X) "
++ "- skipped\n",
++ priv->netdev->name, MAC(bss->address),
++ bss->rate_bas, priv->rate_oper);
++ continue;
++#else
++ printk("%s: peer "MACSTR": incompatible basic rates "
++ "(AP requests 0x%04X, we have 0x%04X). "
++ "Considering anyway...\n",
++ priv->netdev->name, MAC(bss->address),
++ bss->rate_bas, priv->rate_oper);
++#endif
++ }
++
++ if ( !(priv->reg_dom_chanmask & (1<<(bss->channel-1))) ) {
++ printk("%s: warning: peer "MACSTR" is on channel %d "
++ "outside of channel range of current "
++ "regulatory domain - couldn't join "
++ "even if other settings match. "
++ "You might want to adapt your config\n",
++ priv->netdev->name, MAC(bss->address),
++ bss->channel);
++ continue; /* keep looking */
++ }
++
++ if (!priv->essid_active || !strcmp(bss->essid, priv->essid)) {
++ acxlog(L_ASSOC,
++ "found station with matching ESSID! ('%s' "
++ "station, '%s' config)\n",
++ bss->essid,
++ (priv->essid_active) ? priv->essid : "[any]");
++ /* TODO: continue looking for peer with better SNR */
++ bss->used = CLIENT_JOIN_CANDIDATE;
++ idx_found = i;
++
++ /* stop searching if this station is
++ * on the current channel, otherwise
++ * keep looking for an even better match */
++ if (bss->channel == priv->channel)
++ break;
++ } else
++ if (!bss->essid[0]
++ || ((' ' == bss->essid[0]) && !bss->essid[1])
++ ) {
++ /* hmm, station with empty or single-space SSID:
++ * using hidden SSID broadcast?
++ */
++ /* This behaviour is broken: which AP from zillion
++ ** of APs with hidden SSID you'd try?
++ ** We should use Probe requests to get Probe responses
++ ** and check for real SSID (are those never hidden?) */
++ bss->used = CLIENT_JOIN_CANDIDATE;
++ if (idx_found == -1)
++ idx_found = i;
++ acxlog(L_ASSOC, "found station with empty or "
++ "single-space (hidden) SSID, considering "
++ "for assoc attempt\n");
++ /* ...and keep looking for better matches */
++ } else {
++ acxlog(L_ASSOC, "ESSID doesn't match! ('%s' "
++ "station, '%s' config)\n",
++ bss->essid,
++ (priv->essid_active) ? priv->essid : "[any]");
++ }
++ }
++
++ /* TODO: iterate thru join candidates instead */
++ /* TODO: rescan if not associated within some timeout */
++ if (idx_found != -1) {
++ char *essid_src;
++ size_t essid_len;
++
++ bss = &priv->sta_list[idx_found];
++ priv->ap_client = bss;
++
++ if (bss->essid[0] == '\0') {
++ /* if the ESSID of the station we found is empty
++ * (no broadcast), then use user configured ESSID
++ * instead */
++ essid_src = priv->essid;
++ essid_len = priv->essid_len;
++ } else {
++ essid_src = bss->essid;
++ essid_len = strlen(bss->essid);
++ }
++
++ acx_update_capabilities(priv);
++
++ memcpy(priv->essid_for_assoc, essid_src, essid_len);
++ priv->essid_for_assoc[essid_len] = '\0';
++ priv->channel = bss->channel;
++ MAC_COPY(priv->bssid, bss->bssid);
++
++ bss->rate_cfg = (bss->rate_cap & priv->rate_oper);
++ bss->rate_cur = 1 << lowest_bit(bss->rate_cfg);
++ bss->rate_100 = acx_rate111to100(bss->rate_cur);
++
++ acxlog_mac(L_ASSOC,
++ "matching station found: ", priv->bssid, ", joining\n");
++
++ /* TODO: do we need to switch to the peer's channel first? */
++
++ if (ACX_MODE_0_ADHOC == priv->mode) {
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
++ } else {
++ acx_l_transmit_authen1(priv);
++ acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH);
++ }
++ } else { /* idx_found == -1 */
++ /* uh oh, no station found in range */
++ if (ACX_MODE_0_ADHOC == priv->mode) {
++ printk("%s: no matching station found in range, "
++ "generating our own IBSS instead\n",
++ priv->netdev->name);
++ /* we do it hostap way: */
++ MAC_COPY(priv->bssid, priv->dev_addr);
++ priv->bssid[0] |= 0x02; /* 'local assigned addr' bit */
++ /* add IBSS bit to our caps... */
++ acx_update_capabilities(priv);
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
++ /* In order to cmd_join be called below */
++ idx_found = 0;
++ } else {
++ /* we shall scan again, AP can be
++ ** just temporarily powered off */
++ acxlog(L_ASSOC,
++ "no matching station found in range yet\n");
++ acx_set_status(priv, ACX_STATUS_1_SCANNING);
++ result = NOT_OK;
++ }
++ }
++
++ acx_unlock(priv, flags);
++
++ if (idx_found != -1) {
++ if (ACX_MODE_0_ADHOC == priv->mode) {
++ /* need to update channel in beacon template */
++ SET_BIT(priv->set_mask, SET_TEMPLATES);
++ if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
++ acx_s_update_card_settings(priv, 0, 0);
++ }
++ /* Inform firmware on our decision to start or join BSS */
++ acx_s_cmd_join_bssid(priv, priv->bssid);
++ }
++
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_read_fw
++**
++** Loads a firmware image
++**
++** Returns:
++** 0 unable to load file
++** pointer to firmware success
++*/
++#if USE_FW_LOADER_26
++firmware_image_t*
++acx_s_read_fw(struct device *dev, const char *file, u32 *size)
++#else
++#undef acx_s_read_fw
++firmware_image_t*
++acx_s_read_fw(const char *file, u32 *size)
++#endif
++{
++ firmware_image_t *res;
++
++#if USE_FW_LOADER_LEGACY
++ mm_segment_t orgfs;
++ unsigned long page;
++ char *buffer;
++ struct file *inf;
++ int retval;
++ int offset;
++ char *filename;
++#endif
++
++#if USE_FW_LOADER_26
++ const struct firmware *fw_entry;
++
++ res = NULL;
++ acxlog(L_DEBUG, "requesting firmware image '%s'\n", file);
++ if (!request_firmware(&fw_entry, file, dev)) {
++ *size = 8;
++ if (fw_entry->size >= 8)
++ *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4));
++ if (fw_entry->size != *size) {
++ printk("acx: firmware size does not match "
++ "firmware header: %d != %d, "
++ "aborting fw upload\n",
++ (int) fw_entry->size, (int) *size);
++ goto release_ret;
++ }
++ res = vmalloc(*size);
++ if (!res) {
++ printk("acx: no memory for firmware "
++ "(%u bytes)\n", *size);
++ goto release_ret;
++ }
++ memcpy(res, fw_entry->data, fw_entry->size);
++release_ret:
++ release_firmware(fw_entry);
++ return res;
++ }
++ printk("acx: firmware image '%s' was not provided. "
++ "Check your hotplug scripts\n", file);
++#endif
++
++#if USE_FW_LOADER_LEGACY
++ printk("acx: firmware upload via firmware_dir module parameter "
++ "is deprecated. Switch to using hotplug\n");
++
++ res = NULL;
++ orgfs = get_fs(); /* store original fs */
++ set_fs(KERNEL_DS);
++
++ /* Read in whole file then check the size */
++ page = __get_free_page(GFP_KERNEL);
++ if (unlikely(0 == page)) {
++ printk("acx: no memory for firmware upload\n");
++ goto fail;
++ }
++
++ filename = kmalloc(PATH_MAX, GFP_KERNEL);
++ if (unlikely(!filename)) {
++ printk("acx: no memory for firmware upload\n");
++ goto fail;
++ }
++ if (!firmware_dir) {
++ firmware_dir = "/usr/share/acx";
++ acxlog(L_DEBUG, "no firmware directory specified "
++ "via module parameter firmware_dir, "
++ "using default %s\n", firmware_dir);
++ }
++ snprintf(filename, PATH_MAX, "%s/%s", firmware_dir, file);
++ acxlog(L_DEBUG, "reading firmware image '%s'\n", filename);
++
++ buffer = (char*)page;
++
++ /* Note that file must be given as absolute path:
++ * a relative path works on first loading,
++ * but any subsequent firmware loading during card
++ * eject/insert will fail, most likely since the first
++ * module loading happens in user space (and thus
++ * filp_open can figure out the absolute path from a
++ * relative path) whereas the card reinsert processing
++ * probably happens in kernel space where you don't have
++ * a current directory to be able to figure out an
++ * absolute path from a relative path... */
++ inf = filp_open(filename, O_RDONLY, 0);
++ kfree(filename);
++ if (OK != IS_ERR(inf)) {
++ const char *err;
++
++ switch (-PTR_ERR(inf)) {
++ case 2: err = "file not found";
++ break;
++ default:
++ err = "unknown error";
++ break;
++ }
++ printk("acx: error %ld trying to open file '%s': %s\n",
++ -PTR_ERR(inf), file, err);
++ goto fail;
++ }
++
++ if (unlikely((NULL == inf->f_op) || (NULL == inf->f_op->read))) {
++ printk("acx: %s does not have a read method?!\n", file);
++ goto fail_close;
++ }
++
++ offset = 0;
++ do {
++ retval = inf->f_op->read(inf, buffer, PAGE_SIZE, &inf->f_pos);
++
++ if (unlikely(0 > retval)) {
++ printk("acx: error %d reading file '%s'\n",
++ -retval, file);
++ vfree(res);
++ res = NULL;
++ } else if (0 == retval) {
++ if (0 == offset) {
++ printk("acx: firmware image file "
++ "'%s' is empty?!\n", file);
++ }
++ } else if (0 < retval) {
++ /* allocate result buffer here if needed,
++ * since we don't want to waste resources/time
++ * (in case file opening/reading fails)
++ * by doing allocation in front of the loop instead. */
++ if (NULL == res) {
++ *size = 8 + le32_to_cpu(*(u32 *)(4 + buffer));
++
++ res = vmalloc(*size);
++ if (NULL == res) {
++ printk("acx: unable to "
++ "allocate %u bytes for "
++ "firmware module upload\n",
++ *size);
++ goto fail_close;
++ }
++ acxlog(L_DEBUG, "allocated %u bytes "
++ "for firmware module loading\n",
++ *size);
++ }
++ if ((unlikely(offset + retval > *size))) {
++ printk("acx: ERROR: allocation "
++ "was less than firmware image size?!\n");
++ goto fail_close;
++ }
++ memcpy((u8*)res + offset, buffer, retval);
++ offset += retval;
++ }
++ } while (0 < retval);
++
++fail_close:
++ retval = filp_close(inf, NULL);
++
++ if (unlikely(retval)) {
++ printk("acx: error %d closing file '%s'\n", -retval, file);
++ }
++
++ if (unlikely((NULL != res) && (offset != le32_to_cpu(res->size) + 8))) {
++ printk("acx: firmware is reporting a different size "
++ "(0x%08X; 0x%08X was read)\n",
++ le32_to_cpu(res->size) + 8, offset);
++ vfree(res);
++ res = NULL;
++ }
++
++fail:
++ if (page)
++ free_page(page);
++ set_fs(orgfs);
++#endif
++
++ /* checksum will be verified in write_fw, so don't bother here */
++ return res;
++}
++
++
++#ifdef POWER_SAVE_80211
++/*----------------------------------------------------------------
++* acx_s_activate_power_save_mode
++*----------------------------------------------------------------*/
++static void
++acx_s_activate_power_save_mode(wlandevice_t *priv)
++{
++ acx100_ie_powermgmt_t pm;
++
++ FN_ENTER;
++
++ acx_s_interrogate(priv, &pm, ACX1xx_IE_POWER_MGMT);
++ if (pm.wakeup_cfg != 0x81)
++ goto end;
++
++ pm.wakeup_cfg = 0;
++ pm.options = 0;
++ pm.hangover_period = 0;
++ acx_s_configure(priv, &pm, ACX1xx_IE_POWER_MGMT);
++end:
++ FN_EXIT0;
++}
++#endif
++
++
++/***********************************************************************
++** acx_s_set_wepkey
++*/
++static void
++acx100_s_set_wepkey(wlandevice_t *priv)
++{
++ ie_dot11WEPDefaultKey_t dk;
++ int i;
++
++ for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
++ if (priv->wep_keys[i].size != 0) {
++ acxlog(L_INIT, "setting WEP key: %d with "
++ "total size: %d\n", i, (int) priv->wep_keys[i].size);
++ dk.action = 1;
++ dk.keySize = priv->wep_keys[i].size;
++ dk.defaultKeyNum = i;
++ memcpy(dk.key, priv->wep_keys[i].key, dk.keySize);
++ acx_s_configure(priv, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE);
++ }
++ }
++}
++
++static void
++acx111_s_set_wepkey(wlandevice_t *priv)
++{
++ acx111WEPDefaultKey_t dk;
++ int i;
++
++ for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
++ if (priv->wep_keys[i].size != 0) {
++ acxlog(L_INIT, "setting WEP key: %d with "
++ "total size: %d\n", i, (int) priv->wep_keys[i].size);
++ memset(&dk, 0, sizeof(dk));
++ dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */
++ dk.keySize = priv->wep_keys[i].size;
++
++ /* are these two lines necessary? */
++ dk.type = 0; /* default WEP key */
++ dk.index = 0; /* ignored when setting default key */
++
++ dk.defaultKeyNum = i;
++ memcpy(dk.key, priv->wep_keys[i].key, dk.keySize);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk));
++ }
++ }
++}
++
++static void
++acx_s_set_wepkey(wlandevice_t *priv)
++{
++ if (IS_ACX111(priv))
++ acx111_s_set_wepkey(priv);
++ else
++ acx100_s_set_wepkey(priv);
++}
++
++
++/***********************************************************************
++** acx100_s_init_wep
++**
++** FIXME: this should probably be moved into the new card settings
++** management, but since we're also modifying the memory map layout here
++** due to the WEP key space we want, we should take care...
++*/
++int
++acx100_s_init_wep(wlandevice_t *priv)
++{
++/* int i;
++ acx100_cmd_wep_mgmt_t wep_mgmt; size = 37 bytes */
++ acx100_ie_wep_options_t options;
++ ie_dot11WEPDefaultKeyID_t dk;
++ acx_ie_memmap_t pt;
++ int res = NOT_OK;
++
++ FN_ENTER;
++
++ if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ acxlog(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd);
++
++ pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
++ pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
++
++ if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ /* let's choose maximum setting: 4 default keys, plus 10 other keys: */
++ options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
++ options.WEPOption = 0x00;
++
++ acxlog(L_ASSOC, "%s: writing WEP options\n", __func__);
++ acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS);
++
++ acx100_s_set_wepkey(priv);
++
++ if (priv->wep_keys[priv->wep_current_index].size != 0) {
++ acxlog(L_ASSOC, "setting active default WEP key number: %d\n",
++ priv->wep_current_index);
++ dk.KeyID = priv->wep_current_index;
++ acx_s_configure(priv, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */
++ }
++ /* FIXME!!! wep_key_struct is filled nowhere! But priv
++ * is initialized to 0, and we don't REALLY need those keys either */
++/* for (i = 0; i < 10; i++) {
++ if (priv->wep_key_struct[i].len != 0) {
++ MAC_COPY(wep_mgmt.MacAddr, priv->wep_key_struct[i].addr);
++ wep_mgmt.KeySize = cpu_to_le16(priv->wep_key_struct[i].len);
++ memcpy(&wep_mgmt.Key, priv->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize));
++ wep_mgmt.Action = cpu_to_le16(1);
++ acxlog(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize));
++ if (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) {
++ priv->wep_key_struct[i].index = i;
++ }
++ }
++ } */
++
++ /* now retrieve the updated WEPCacheEnd pointer... */
++ if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n",
++ priv->netdev->name);
++ goto fail;
++ }
++ /* ...and tell it to start allocating templates at that location */
++ /* (no endianness conversion needed) */
++ pt.PacketTemplateStart = pt.WEPCacheEnd;
++
++ if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n",
++ priv->netdev->name);
++ goto fail;
++ }
++ res = OK;
++
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_s_init_max_null_data_template(wlandevice_t *priv)
++{
++ struct acx_template_nullframe b;
++ int result;
++
++ FN_ENTER;
++ memset(&b, 0, sizeof(b));
++ b.size = cpu_to_le16(sizeof(b) - 2);
++ result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b));
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_init_max_beacon_template
++*/
++static int
++acx_s_init_max_beacon_template(wlandevice_t *priv)
++{
++ struct acx_template_beacon b;
++ int result;
++
++ FN_ENTER;
++ memset(&b, 0, sizeof(b));
++ b.size = cpu_to_le16(sizeof(b) - 2);
++ result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &b, sizeof(b));
++
++ FN_EXIT1(result);
++ return result;
++}
++
++/***********************************************************************
++** acx_s_init_max_tim_template
++*/
++static int
++acx_s_init_max_tim_template(wlandevice_t *priv)
++{
++ acx_template_tim_t t;
++
++ memset(&t, 0, sizeof(t));
++ t.size = cpu_to_le16(sizeof(t) - 2);
++ return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t));
++}
++
++
++/***********************************************************************
++** acx_s_init_max_probe_response_template
++*/
++static int
++acx_s_init_max_probe_response_template(wlandevice_t *priv)
++{
++ struct acx_template_proberesp pr;
++
++ memset(&pr, 0, sizeof(pr));
++ pr.size = cpu_to_le16(sizeof(pr) - 2);
++
++ return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, sizeof(pr));
++}
++
++
++/***********************************************************************
++** acx_s_init_max_probe_request_template
++*/
++static int
++acx_s_init_max_probe_request_template(wlandevice_t *priv)
++{
++ union {
++ acx100_template_probereq_t p100;
++ acx111_template_probereq_t p111;
++ } pr;
++ int res;
++
++ FN_ENTER;
++ memset(&pr, 0, sizeof(pr));
++ pr.p100.size = cpu_to_le16(sizeof(pr) - 2);
++ res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &pr, sizeof(pr));
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx_s_set_tim_template
++**
++** In full blown driver we will regularly update partial virtual bitmap
++** by calling this function
++** (it can be done by irq handler on each DTIM irq or by timer...)
++
++[802.11 7.3.2.6] TIM information element:
++- 1 EID
++- 1 Length
++1 1 DTIM Count
++ indicates how many beacons (including this) appear before next DTIM
++ (0=this one is a DTIM)
++2 1 DTIM Period
++ number of beacons between successive DTIMs
++ (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc)
++3 1 Bitmap Control
++ bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?)
++ set to 1 in TIM elements with a value of 0 in the DTIM Count field
++ when one or more broadcast or multicast frames are buffered at the AP.
++ bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE).
++4 n Partial Virtual Bitmap
++ Visible part of traffic-indication bitmap.
++ Full bitmap consists of 2008 bits (251 octets) such that bit number N
++ (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8)
++ in octet number N/8 where the low-order bit of each octet is bit0,
++ and the high order bit is bit7.
++ Each set bit in virtual bitmap corresponds to traffic buffered by AP
++ for a specific station (with corresponding AID?).
++ Partial Virtual Bitmap shows a part of bitmap which has non-zero.
++ Bitmap Offset is a number of skipped zero octets (see above).
++ 'Missing' octets at the tail are also assumed to be zero.
++ Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55
++ This means that traffic-indication bitmap is:
++ 00000000 00000000 01010101 01010101 01010101 00000000 00000000...
++ (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?)
++*/
++static int
++acx_s_set_tim_template(wlandevice_t *priv)
++{
++/* For now, configure smallish test bitmap, all zero ("no pending data") */
++ enum { bitmap_size = 5 };
++
++ acx_template_tim_t t;
++ int result;
++
++ FN_ENTER;
++
++ memset(&t, 0, sizeof(t));
++ t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */
++ t.tim_eid = WLAN_EID_TIM;
++ t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */
++ result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t));
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_fill_beacon_or_proberesp_template
++**
++** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!!
++**
++** NB: we use the fact that
++** struct acx_template_proberesp and struct acx_template_beacon are the same
++** (well, almost...)
++**
++** [802.11] Beacon's body consist of these IEs:
++** 1 Timestamp
++** 2 Beacon interval
++** 3 Capability information
++** 4 SSID
++** 5 Supported rates (up to 8 rates)
++** 6 FH Parameter Set (frequency-hopping PHYs only)
++** 7 DS Parameter Set (direct sequence PHYs only)
++** 8 CF Parameter Set (only if PCF is supported)
++** 9 IBSS Parameter Set (ad-hoc only)
++**
++** Beacon only:
++** 10 TIM (AP only) (see 802.11 7.3.2.6)
++** 11 Country Information (802.11d)
++** 12 FH Parameters (802.11d)
++** 13 FH Pattern Table (802.11d)
++** ... (?!! did not yet find relevant PDF file... --vda)
++** 19 ERP Information (extended rate PHYs)
++** 20 Extended Supported Rates (if more than 8 rates)
++**
++** Proberesp only:
++** 10 Country information (802.11d)
++** 11 FH Parameters (802.11d)
++** 12 FH Pattern Table (802.11d)
++** 13-n Requested information elements (802.11d)
++** ????
++** 18 ERP Information (extended rate PHYs)
++** 19 Extended Supported Rates (if more than 8 rates)
++*/
++static int
++acx_fill_beacon_or_proberesp_template(wlandevice_t *priv,
++ struct acx_template_beacon *templ,
++ u16 fc /* in host order! */)
++{
++ int len;
++ u8 *p;
++
++ FN_ENTER;
++
++ memset(templ, 0, sizeof(*templ));
++ MAC_BCAST(templ->da);
++ MAC_COPY(templ->sa, priv->dev_addr);
++ MAC_COPY(templ->bssid, priv->bssid);
++
++ templ->beacon_interval = cpu_to_le16(priv->beacon_interval);
++ acx_update_capabilities(priv);
++ templ->cap = cpu_to_le16(priv->capabilities);
++
++ p = templ->variable;
++ p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid);
++ p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported);
++ p = wlan_fill_ie_ds_parms(p, priv->channel);
++ /* NB: should go AFTER tim, but acx seem to keep tim last always */
++ p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported);
++
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ /* ATIM window */
++ p = wlan_fill_ie_ibss_parms(p, 0); break;
++ case ACX_MODE_3_AP:
++ /* TIM IE is set up as separate template */
++ break;
++ }
++
++ len = p - (u8*)templ;
++ templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc);
++ /* - 2: do not count 'u16 size' field */
++ templ->size = cpu_to_le16(len - 2);
++
++ FN_EXIT1(len);
++ return len;
++}
++
++
++/***********************************************************************
++** acx_s_set_beacon_template
++*/
++static int
++acx_s_set_beacon_template(wlandevice_t *priv)
++{
++ struct acx_template_beacon bcn;
++ int len, result;
++
++ FN_ENTER;
++
++ len = acx_fill_beacon_or_proberesp_template(priv, &bcn, WF_FSTYPE_BEACON);
++ result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &bcn, len);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_set_probe_response_template
++*/
++static int
++acx_s_set_probe_response_template(wlandevice_t *priv)
++{
++ struct acx_template_proberesp pr;
++ int len, result;
++
++ FN_ENTER;
++
++ len = acx_fill_beacon_or_proberesp_template(priv, &pr, WF_FSTYPE_PROBERESP);
++ result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx100_s_init_packet_templates()
++**
++** NOTE: order is very important here, to have a correct memory layout!
++** init templates: max Probe Request (station mode), max NULL data,
++** max Beacon, max TIM, max Probe Response.
++*/
++int
++acx100_s_init_packet_templates(wlandevice_t *priv)
++{
++ acx_ie_memmap_t mm;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ acxlog(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm));
++
++ /* acx100 still do not emit probe requests, thus this call
++ ** is sourt of not needed. But we want it to work someday */
++ if (OK != acx_s_init_max_probe_request_template(priv))
++ goto failed;
++
++#ifdef NOT_WORKING_YET
++ /* FIXME: creating the NULL data template breaks
++ * communication right now, needs further testing.
++ * Also, need to set the template once we're joining a network. */
++ if (OK != acx_s_init_max_null_data_template(priv))
++ goto failed;
++#endif
++
++ if (OK != acx_s_init_max_beacon_template(priv))
++ goto failed;
++
++ if (OK != acx_s_init_max_tim_template(priv))
++ goto failed;
++
++ if (OK != acx_s_init_max_probe_response_template(priv))
++ goto failed;
++
++ if (OK != acx_s_set_tim_template(priv))
++ goto failed;
++
++ if (OK != acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP)) {
++ goto failed;
++ }
++
++ mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4);
++ if (OK != acx_s_configure(priv, &mm, ACX1xx_IE_MEMORY_MAP)) {
++ goto failed;
++ }
++
++ result = OK;
++ goto success;
++
++failed:
++ acxlog(L_DEBUG|L_INIT,
++ /* "cb=0x%X\n" */
++ "pACXMemoryMap:\n"
++ ".CodeStart=0x%X\n"
++ ".CodeEnd=0x%X\n"
++ ".WEPCacheStart=0x%X\n"
++ ".WEPCacheEnd=0x%X\n"
++ ".PacketTemplateStart=0x%X\n"
++ ".PacketTemplateEnd=0x%X\n",
++ /* len, */
++ le32_to_cpu(mm.CodeStart),
++ le32_to_cpu(mm.CodeEnd),
++ le32_to_cpu(mm.WEPCacheStart),
++ le32_to_cpu(mm.WEPCacheEnd),
++ le32_to_cpu(mm.PacketTemplateStart),
++ le32_to_cpu(mm.PacketTemplateEnd));
++
++success:
++ FN_EXIT1(result);
++ return result;
++}
++
++int
++acx111_s_init_packet_templates(wlandevice_t *priv)
++{
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ acxlog(L_DEBUG|L_INIT, "initializing max packet templates\n");
++
++ if (OK != acx_s_init_max_probe_request_template(priv))
++ goto failed;
++
++ if (OK != acx_s_init_max_null_data_template(priv))
++ goto failed;
++
++ if (OK != acx_s_init_max_beacon_template(priv))
++ goto failed;
++
++ if (OK != acx_s_init_max_tim_template(priv))
++ goto failed;
++
++ if (OK != acx_s_init_max_probe_response_template(priv))
++ goto failed;
++
++ /* the other templates will be set later (acx_start) */
++ /*
++ if (OK != acx_s_set_tim_template(priv))
++ goto failed;*/
++
++ result = OK;
++ goto success;
++
++failed:
++ printk("%s: acx111_init_packet_templates() FAILED\n", priv->netdev->name);
++
++success:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx100_s_set_probe_request_template(wlandevice_t *priv)
++{
++ struct acx100_template_probereq probereq;
++ char *p;
++ int res;
++ int frame_len;
++
++ FN_ENTER;
++
++ memset(&probereq, 0, sizeof(probereq));
++
++ probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi;
++ MAC_BCAST(probereq.da);
++ MAC_COPY(probereq.sa, priv->dev_addr);
++ MAC_BCAST(probereq.bssid);
++
++ probereq.beacon_interval = cpu_to_le16(priv->beacon_interval);
++ acx_update_capabilities(priv);
++ probereq.cap = cpu_to_le16(priv->capabilities);
++
++ p = probereq.variable;
++ acxlog(L_ASSOC, "SSID='%s' len=%d\n", priv->essid, priv->essid_len);
++ p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid);
++ p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported);
++ /* FIXME: should these be here or AFTER ds_parms? */
++ p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported);
++ /* HUH?? who said it must be here? I've found nothing in 802.11! --vda*/
++ /* p = wlan_fill_ie_ds_parms(p, priv->channel); */
++ frame_len = p - (char*)&probereq;
++ probereq.size = frame_len - 2;
++
++ res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len);
++ FN_EXIT0;
++ return res;
++}
++
++static int
++acx111_s_set_probe_request_template(wlandevice_t *priv)
++{
++ struct acx111_template_probereq probereq;
++ char *p;
++ int res;
++ int frame_len;
++
++ FN_ENTER;
++
++ memset(&probereq, 0, sizeof(probereq));
++
++ probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi;
++ MAC_BCAST(probereq.da);
++ MAC_COPY(probereq.sa, priv->dev_addr);
++ MAC_BCAST(probereq.bssid);
++
++ p = probereq.variable;
++ p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid);
++ p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported);
++ p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported);
++ frame_len = p - (char*)&probereq;
++ probereq.size = frame_len - 2;
++
++ res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len);
++ FN_EXIT0;
++ return res;
++}
++
++static int
++acx_s_set_probe_request_template(wlandevice_t *priv)
++{
++ if (IS_ACX111(priv)) {
++ return acx111_s_set_probe_request_template(priv);
++ } else {
++ return acx100_s_set_probe_request_template(priv);
++ }
++}
++
++
++/***********************************************************************
++** acx_s_update_card_settings
++**
++** Applies accumulated changes in various priv->xxxx members
++** Called by ioctl commit handler, acx_start, acx_set_defaults,
++** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG),
++*/
++static void
++acx111_s_sens_radio_16_17(wlandevice_t *priv)
++{
++ u32 feature1, feature2;
++
++ if ((priv->sensitivity < 1) || (priv->sensitivity > 3)) {
++ printk("%s: invalid sensitivity setting (1..3), "
++ "setting to 1\n", priv->netdev->name);
++ priv->sensitivity = 1;
++ }
++ acx111_s_get_feature_config(priv, &feature1, &feature2);
++ CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX);
++ if (priv->sensitivity > 1)
++ SET_BIT(feature1, FEATURE1_LOW_RX);
++ if (priv->sensitivity > 2)
++ SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX);
++ acx111_s_feature_set(priv, feature1, feature2);
++}
++
++void
++acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all)
++{
++ unsigned long flags;
++ unsigned int start_scan = 0;
++ int i;
++
++ FN_ENTER;
++
++ if (get_all)
++ SET_BIT(priv->get_mask, GETSET_ALL);
++ if (set_all)
++ SET_BIT(priv->set_mask, GETSET_ALL);
++ /* Why not just set masks to 0xffffffff? We can get rid of GETSET_ALL */
++
++ acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n",
++ priv->get_mask, priv->set_mask);
++
++ /* Track dependencies betweed various settings */
++
++ if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) {
++ acxlog(L_INIT, "important setting has been changed. "
++ "Need to update packet templates, too\n");
++ SET_BIT(priv->set_mask, SET_TEMPLATES);
++ }
++ if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) {
++ /* This will actually tune RX/TX to the channel */
++ SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX);
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ /* Beacons contain channel# - update them */
++ SET_BIT(priv->set_mask, SET_TEMPLATES);
++ }
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ start_scan = 1;
++ }
++ }
++
++ /* Apply settings */
++
++#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */
++ /* send a disassoc request in case it's required */
++ if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP|GETSET_ALL)) {
++ if (ACX_MODE_2_STA == priv->mode) {
++ if (ACX_STATUS_4_ASSOCIATED == priv->status) {
++ acxlog(L_ASSOC, "we were ASSOCIATED - "
++ "sending disassoc request\n");
++ acx_lock(priv, flags);
++ acx_l_transmit_disassoc(priv, NULL);
++ /* FIXME: deauth? */
++ acx_unlock(priv, flags);
++ }
++ /* need to reset some other stuff as well */
++ acxlog(L_DEBUG, "resetting bssid\n");
++ MAC_ZERO(priv->bssid);
++ SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST);
++ /* FIXME: should start scanning */
++ start_scan = 1;
++ }
++ }
++#endif
++
++ if (priv->get_mask & (GETSET_STATION_ID|GETSET_ALL)) {
++ u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
++ const u8 *paddr;
++
++ acx_s_interrogate(priv, stationID, ACX1xx_IE_DOT11_STATION_ID);
++ paddr = &stationID[4];
++ for (i = 0; i < ETH_ALEN; i++) {
++ /* we copy the MAC address (reversed in
++ * the card) to the netdevice's MAC
++ * address, and on ifup it will be
++ * copied into iwpriv->dev_addr */
++ priv->netdev->dev_addr[ETH_ALEN - 1 - i] = paddr[i];
++ }
++ CLEAR_BIT(priv->get_mask, GETSET_STATION_ID);
++ }
++
++ if (priv->get_mask & (GETSET_SENSITIVITY|GETSET_ALL)) {
++ if ((RADIO_RFMD_11 == priv->radio_type)
++ || (RADIO_MAXIM_0D == priv->radio_type)
++ || (RADIO_RALINK_15 == priv->radio_type)) {
++ acx_s_read_phy_reg(priv, 0x30, &priv->sensitivity);
++ } else {
++ acxlog(L_INIT, "don't know how to get sensitivity "
++ "for radio type 0x%02X\n", priv->radio_type);
++ priv->sensitivity = 0;
++ }
++ acxlog(L_INIT, "got sensitivity value %u\n", priv->sensitivity);
++
++ CLEAR_BIT(priv->get_mask, GETSET_SENSITIVITY);
++ }
++
++ if (priv->get_mask & (GETSET_ANTENNA|GETSET_ALL)) {
++ u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
++
++ memset(antenna, 0, sizeof(antenna));
++ acx_s_interrogate(priv, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
++ priv->antenna = antenna[4];
++ acxlog(L_INIT, "got antenna value 0x%02X\n", priv->antenna);
++ CLEAR_BIT(priv->get_mask, GETSET_ANTENNA);
++ }
++
++ if (priv->get_mask & (GETSET_ED_THRESH|GETSET_ALL)) {
++ if (IS_ACX100(priv)) {
++ u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
++
++ memset(ed_threshold, 0, sizeof(ed_threshold));
++ acx_s_interrogate(priv, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
++ priv->ed_threshold = ed_threshold[4];
++ } else {
++ acxlog(L_INIT, "acx111 doesn't support ED\n");
++ priv->ed_threshold = 0;
++ }
++ acxlog(L_INIT, "got Energy Detect (ED) threshold %u\n", priv->ed_threshold);
++ CLEAR_BIT(priv->get_mask, GETSET_ED_THRESH);
++ }
++
++ if (priv->get_mask & (GETSET_CCA|GETSET_ALL)) {
++ if (IS_ACX100(priv)) {
++ u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
++
++ memset(cca, 0, sizeof(priv->cca));
++ acx_s_interrogate(priv, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
++ priv->cca = cca[4];
++ } else {
++ acxlog(L_INIT, "acx111 doesn't support CCA\n");
++ priv->cca = 0;
++ }
++ acxlog(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", priv->cca);
++ CLEAR_BIT(priv->get_mask, GETSET_CCA);
++ }
++
++ if (priv->get_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) {
++ acx_ie_generic_t dom;
++
++ acx_s_interrogate(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
++ priv->reg_dom_id = dom.m.bytes[0];
++ /* FIXME: should also set chanmask somehow */
++ acxlog(L_INIT, "got regulatory domain 0x%02X\n", priv->reg_dom_id);
++ CLEAR_BIT(priv->get_mask, GETSET_REG_DOMAIN);
++ }
++
++ if (priv->set_mask & (GETSET_STATION_ID|GETSET_ALL)) {
++ u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
++ u8 *paddr;
++
++ paddr = &stationID[4];
++ for (i = 0; i < ETH_ALEN; i++) {
++ /* copy the MAC address we obtained when we noticed
++ * that the ethernet iface's MAC changed
++ * to the card (reversed in
++ * the card!) */
++ paddr[i] = priv->dev_addr[ETH_ALEN - 1 - i];
++ }
++ acx_s_configure(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID);
++ CLEAR_BIT(priv->set_mask, GETSET_STATION_ID);
++ }
++
++ if (priv->set_mask & (SET_TEMPLATES|GETSET_ALL)) {
++ acxlog(L_INIT, "updating packet templates\n");
++ /* Doesn't work for acx100, do it only for acx111 for now */
++ if (IS_ACX111(priv)) {
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ acx_s_set_probe_request_template(priv);
++ }
++ }
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ /* FIXME: why only for AP? STA need probe req templates... */
++ acx_s_set_beacon_template(priv);
++ acx_s_set_tim_template(priv);
++ /* BTW acx111 firmware would not send probe responses
++ ** if probe request does not have all basic rates flagged
++ ** by 0x80! Thus firmware does not conform to 802.11,
++ ** it should ignore 0x80 bit in ratevector from STA.
++ ** We can 'fix' it by not using this template and
++ ** sending probe responses by hand. TODO --vda */
++ acx_s_set_probe_response_template(priv);
++ }
++ /* Needed if generated frames are to be emitted at different tx rate now */
++ acxlog(L_IRQ, "redoing cmd_join_bssid() after template cfg\n");
++ acx_s_cmd_join_bssid(priv, priv->bssid);
++ CLEAR_BIT(priv->set_mask, SET_TEMPLATES);
++ }
++ if (priv->set_mask & (SET_STA_LIST|GETSET_ALL)) {
++ acx_lock(priv, flags);
++ acx_l_sta_list_init(priv);
++ CLEAR_BIT(priv->set_mask, SET_STA_LIST);
++ acx_unlock(priv, flags);
++ }
++ if (priv->set_mask & (SET_RATE_FALLBACK|GETSET_ALL)) {
++ u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN];
++
++ /* configure to not do fallbacks when not in auto rate mode */
++ rate[4] = (priv->rate_auto) ? /* priv->txrate_fallback_retries */ 1 : 0;
++ acxlog(L_INIT, "updating Tx fallback to %u retries\n", rate[4]);
++ acx_s_configure(priv, &rate, ACX1xx_IE_RATE_FALLBACK);
++ CLEAR_BIT(priv->set_mask, SET_RATE_FALLBACK);
++ }
++ if (priv->set_mask & (GETSET_TXPOWER|GETSET_ALL)) {
++ acxlog(L_INIT, "updating transmit power: %u dBm\n",
++ priv->tx_level_dbm);
++ acx_s_set_tx_level(priv, priv->tx_level_dbm);
++ CLEAR_BIT(priv->set_mask, GETSET_TXPOWER);
++ }
++
++ if (priv->set_mask & (GETSET_SENSITIVITY|GETSET_ALL)) {
++ acxlog(L_INIT, "updating sensitivity value: %u\n",
++ priv->sensitivity);
++ switch (priv->radio_type) {
++ case RADIO_RFMD_11:
++ case RADIO_MAXIM_0D:
++ case RADIO_RALINK_15:
++ acx_s_write_phy_reg(priv, 0x30, priv->sensitivity);
++ break;
++ case RADIO_RADIA_16:
++ case RADIO_UNKNOWN_17:
++ acx111_s_sens_radio_16_17(priv);
++ break;
++ default:
++ acxlog(L_INIT, "don't know how to modify sensitivity "
++ "for radio type 0x%02X\n", priv->radio_type);
++ }
++ CLEAR_BIT(priv->set_mask, GETSET_SENSITIVITY);
++ }
++
++ if (priv->set_mask & (GETSET_ANTENNA|GETSET_ALL)) {
++ /* antenna */
++ u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
++
++ memset(antenna, 0, sizeof(antenna));
++ antenna[4] = priv->antenna;
++ acxlog(L_INIT, "updating antenna value: 0x%02X\n",
++ priv->antenna);
++ acx_s_configure(priv, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
++ CLEAR_BIT(priv->set_mask, GETSET_ANTENNA);
++ }
++
++ if (priv->set_mask & (GETSET_ED_THRESH|GETSET_ALL)) {
++ /* ed_threshold */
++ acxlog(L_INIT, "updating Energy Detect (ED) threshold: %u\n",
++ priv->ed_threshold);
++ if (IS_ACX100(priv)) {
++ u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
++
++ memset(ed_threshold, 0, sizeof(ed_threshold));
++ ed_threshold[4] = priv->ed_threshold;
++ acx_s_configure(priv, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
++ }
++ else
++ acxlog(L_INIT, "acx111 doesn't support ED!\n");
++ CLEAR_BIT(priv->set_mask, GETSET_ED_THRESH);
++ }
++
++ if (priv->set_mask & (GETSET_CCA|GETSET_ALL)) {
++ /* CCA value */
++ acxlog(L_INIT, "updating Channel Clear Assessment "
++ "(CCA) value: 0x%02X\n", priv->cca);
++ if (IS_ACX100(priv)) {
++ u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
++
++ memset(cca, 0, sizeof(cca));
++ cca[4] = priv->cca;
++ acx_s_configure(priv, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
++ }
++ else
++ acxlog(L_INIT, "acx111 doesn't support CCA!\n");
++ CLEAR_BIT(priv->set_mask, GETSET_CCA);
++ }
++
++ if (priv->set_mask & (GETSET_LED_POWER|GETSET_ALL)) {
++ /* Enable Tx */
++ acxlog(L_INIT, "updating power LED status: %u\n", priv->led_power);
++
++ acx_lock(priv, flags);
++ if (IS_PCI(priv))
++ acx_l_power_led(priv, priv->led_power);
++ CLEAR_BIT(priv->set_mask, GETSET_LED_POWER);
++ acx_unlock(priv, flags);
++ }
++
++/* this seems to cause Tx lockup after some random time (Tx error 0x20),
++ * so let's disable it for now until further investigation */
++/* Maybe fixed now after locking is fixed. Need to retest */
++#ifdef POWER_SAVE_80211
++ if (priv->set_mask & (GETSET_POWER_80211|GETSET_ALL)) {
++ acx100_ie_powermgmt_t pm;
++
++ /* change 802.11 power save mode settings */
++ acxlog(L_INIT, "updating 802.11 power save mode settings: "
++ "wakeup_cfg 0x%02X, listen interval %u, "
++ "options 0x%02X, hangover period %u, "
++ "enhanced_ps_transition_time %d\n",
++ priv->ps_wakeup_cfg, priv->ps_listen_interval,
++ priv->ps_options, priv->ps_hangover_period,
++ priv->ps_enhanced_transition_time);
++ acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT);
++ acxlog(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, "
++ "listen interval %u, options 0x%02X, "
++ "hangover period %u, "
++ "enhanced_ps_transition_time %d\n",
++ pm.wakeup_cfg, pm.listen_interval, pm.options,
++ pm.hangover_period, pm.enhanced_ps_transition_time);
++ pm.wakeup_cfg = priv->ps_wakeup_cfg;
++ pm.listen_interval = priv->ps_listen_interval;
++ pm.options = priv->ps_options;
++ pm.hangover_period = priv->ps_hangover_period;
++ pm.enhanced_ps_transition_time = cpu_to_le16(priv->ps_enhanced_transition_time);
++ acx_s_configure(priv, &pm, ACX100_IE_POWER_MGMT);
++ acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT);
++ acxlog(L_INIT, "wakeup_cfg: 0x%02X\n", pm.wakeup_cfg);
++ acx_s_msleep(40);
++ acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT);
++ acxlog(L_INIT, "power save mode change %s\n",
++ (pm.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful");
++ /* FIXME: maybe verify via PS_CFG_PENDING bit here
++ * that power save mode change was successful. */
++ /* FIXME: we shouldn't trigger a scan immediately after
++ * fiddling with power save mode (since the firmware is sending
++ * a NULL frame then). Does this need locking?? */
++ CLEAR_BIT(priv->set_mask, GETSET_POWER_80211);
++ }
++#endif
++
++ if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) {
++ /* channel */
++ acxlog(L_INIT, "updating channel to: %u\n", priv->channel);
++ CLEAR_BIT(priv->set_mask, GETSET_CHANNEL);
++ }
++
++ if (priv->set_mask & (GETSET_TX|GETSET_ALL)) {
++ /* set Tx */
++ acxlog(L_INIT, "updating: %s Tx\n",
++ priv->tx_disabled ? "disable" : "enable");
++ if (priv->tx_disabled)
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ /* ^ */
++ /* FIXME: this used to be 1, but since we don't transfer a parameter... */
++ else
++ acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1);
++ CLEAR_BIT(priv->set_mask, GETSET_TX);
++ }
++
++ if (priv->set_mask & (GETSET_RX|GETSET_ALL)) {
++ /* Enable Rx */
++ acxlog(L_INIT, "updating: enable Rx on channel: %u\n",
++ priv->channel);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1);
++ CLEAR_BIT(priv->set_mask, GETSET_RX);
++ }
++
++ if (priv->set_mask & (GETSET_RETRY|GETSET_ALL)) {
++ u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN];
++ u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN];
++
++ acxlog(L_INIT, "updating short retry limit: %u, long retry limit: %u\n",
++ priv->short_retry, priv->long_retry);
++ short_retry[0x4] = priv->short_retry;
++ long_retry[0x4] = priv->long_retry;
++ acx_s_configure(priv, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT);
++ acx_s_configure(priv, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT);
++ CLEAR_BIT(priv->set_mask, GETSET_RETRY);
++ }
++
++ if (priv->set_mask & (SET_MSDU_LIFETIME|GETSET_ALL)) {
++ u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN];
++
++ acxlog(L_INIT, "updating tx MSDU lifetime: %u\n",
++ priv->msdu_lifetime);
++ *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)priv->msdu_lifetime);
++ acx_s_configure(priv, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME);
++ CLEAR_BIT(priv->set_mask, SET_MSDU_LIFETIME);
++ }
++
++ if (priv->set_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) {
++ /* reg_domain */
++ acx_ie_generic_t dom;
++ unsigned mask;
++
++ acxlog(L_INIT, "updating regulatory domain: 0x%02X\n",
++ priv->reg_dom_id);
++ for (i = 0; i < sizeof(reg_domain_ids); i++)
++ if (reg_domain_ids[i] == priv->reg_dom_id)
++ break;
++
++ if (sizeof(reg_domain_ids) == i) {
++ acxlog(L_INIT, "Invalid or unsupported regulatory "
++ "domain 0x%02X specified, falling back to "
++ "FCC (USA)! Please report if this sounds "
++ "fishy!\n", priv->reg_dom_id);
++ i = 0;
++ priv->reg_dom_id = reg_domain_ids[i];
++ }
++
++ priv->reg_dom_chanmask = reg_domain_channel_masks[i];
++ dom.m.bytes[0] = priv->reg_dom_id;
++ acx_s_configure(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
++
++ mask = (1 << (priv->channel - 1));
++ if (!(priv->reg_dom_chanmask & mask)) {
++ /* hmm, need to adjust our channel to reside within domain */
++ mask = 1;
++ for (i = 1; i <= 14; i++) {
++ if (priv->reg_dom_chanmask & mask) {
++ printk("%s: adjusting "
++ "selected channel from %d "
++ "to %d due to new regulatory "
++ "domain\n", priv->netdev->name,
++ priv->channel, i);
++ priv->channel = i;
++ break;
++ }
++ mask <<= 1;
++ }
++ }
++ CLEAR_BIT(priv->set_mask, GETSET_REG_DOMAIN);
++ }
++
++ if (priv->set_mask & (GETSET_MODE|GETSET_ALL)) {
++ priv->netdev->type = ARPHRD_ETHER;
++
++ switch (priv->mode) {
++ case ACX_MODE_3_AP:
++
++ acx_lock(priv, flags);
++ acx_l_sta_list_init(priv);
++ priv->aid = 0;
++ priv->ap_client = NULL;
++ MAC_COPY(priv->bssid, priv->dev_addr);
++ /* this basically says "we're connected" */
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
++ acx_unlock(priv, flags);
++
++ acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
++ /* start sending beacons */
++ acx_s_cmd_join_bssid(priv, priv->bssid);
++ break;
++ case ACX_MODE_MONITOR:
++ /* priv->netdev->type = ARPHRD_ETHER; */
++ /* priv->netdev->type = ARPHRD_IEEE80211; */
++ priv->netdev->type = ARPHRD_IEEE80211_PRISM;
++ acx111_s_feature_on(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
++ /* this stops beacons */
++ acx_s_cmd_join_bssid(priv, priv->bssid);
++ /* this basically says "we're connected" */
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
++ SET_BIT(priv->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS);
++ break;
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
++ priv->aid = 0;
++ priv->ap_client = NULL;
++ /* we want to start looking for peer or AP */
++ start_scan = 1;
++ break;
++ case ACX_MODE_OFF:
++ /* TODO: disable RX/TX, stop any scanning activity etc: */
++ /* priv->tx_disabled = 1; */
++ /* SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); */
++
++ /* This stops beacons (invalid macmode...) */
++ acx_s_cmd_join_bssid(priv, priv->bssid);
++ acx_set_status(priv, ACX_STATUS_0_STOPPED);
++ break;
++ }
++ CLEAR_BIT(priv->set_mask, GETSET_MODE);
++ }
++
++ if (priv->set_mask & (SET_RXCONFIG|GETSET_ALL)) {
++ acx_s_initialize_rx_config(priv);
++ CLEAR_BIT(priv->set_mask, SET_RXCONFIG);
++ }
++
++ if (priv->set_mask & (GETSET_RESCAN|GETSET_ALL)) {
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ start_scan = 1;
++ break;
++ }
++ CLEAR_BIT(priv->set_mask, GETSET_RESCAN);
++ }
++
++ if (priv->set_mask & (GETSET_WEP|GETSET_ALL)) {
++ /* encode */
++
++ ie_dot11WEPDefaultKeyID_t dkey;
++#ifdef DEBUG_WEP
++ struct {
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u8 val ACX_PACKED;
++ } keyindic;
++#endif
++ acxlog(L_INIT, "updating WEP key settings\n");
++
++ acx_s_set_wepkey(priv);
++
++ dkey.KeyID = priv->wep_current_index;
++ acxlog(L_INIT, "setting WEP key %u as default\n", dkey.KeyID);
++ acx_s_configure(priv, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET);
++#ifdef DEBUG_WEP
++ keyindic.val = 3;
++ acx_s_configure(priv, &keyindic, ACX111_IE_KEY_CHOOSE);
++#endif
++ start_scan = 1;
++ CLEAR_BIT(priv->set_mask, GETSET_WEP);
++ }
++
++ if (priv->set_mask & (SET_WEP_OPTIONS|GETSET_ALL)) {
++ acx100_ie_wep_options_t options;
++
++ if (IS_ACX111(priv)) {
++ acxlog(L_DEBUG, "setting WEP Options for acx111 is not supported\n");
++ } else {
++ acxlog(L_INIT, "setting WEP Options\n");
++
++ /* let's choose maximum setting: 4 default keys,
++ * plus 10 other keys: */
++ options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
++ /* don't decrypt default key only,
++ * don't override decryption: */
++ options.WEPOption = 0;
++ if (priv->mode == ACX_MODE_MONITOR) {
++ /* don't decrypt default key only,
++ * override decryption mechanism: */
++ options.WEPOption = 2;
++ }
++
++ acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS);
++ }
++ CLEAR_BIT(priv->set_mask, SET_WEP_OPTIONS);
++ }
++
++ /* Rescan was requested */
++ if (start_scan) {
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* We can avoid clearing list if join code
++ ** will be a bit more clever about not picking
++ ** 'bad' AP over and over again */
++ acx_lock(priv, flags);
++ priv->ap_client = NULL;
++ acx_l_sta_list_init(priv);
++ acx_set_status(priv, ACX_STATUS_1_SCANNING);
++ acx_unlock(priv, flags);
++
++ acx_s_cmd_start_scan(priv);
++ }
++ }
++
++ /* debug, rate, and nick don't need any handling */
++ /* what about sniffing mode?? */
++
++ acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n",
++ priv->get_mask, priv->set_mask);
++
++/* end: */
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++void
++acx_s_initialize_rx_config(wlandevice_t *priv)
++{
++ struct {
++ u16 id ACX_PACKED;
++ u16 len ACX_PACKED;
++ u16 rx_cfg1 ACX_PACKED;
++ u16 rx_cfg2 ACX_PACKED;
++ } cfg;
++
++ switch (priv->mode) {
++ case ACX_MODE_OFF:
++ priv->rx_config_1 = (u16) (0
++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */
++ /* | RX_CFG1_FILTER_SSID */
++ /* | RX_CFG1_FILTER_BCAST */
++ /* | RX_CFG1_RCV_MC_ADDR1 */
++ /* | RX_CFG1_RCV_MC_ADDR0 */
++ /* | RX_CFG1_FILTER_ALL_MULTI */
++ /* | RX_CFG1_FILTER_BSSID */
++ /* | RX_CFG1_FILTER_MAC */
++ /* | RX_CFG1_RCV_PROMISCUOUS */
++ /* | RX_CFG1_INCLUDE_FCS */
++ /* | RX_CFG1_INCLUDE_PHY_HDR */
++ );
++ priv->rx_config_2 = (u16) (0
++ /*| RX_CFG2_RCV_ASSOC_REQ */
++ /*| RX_CFG2_RCV_AUTH_FRAMES */
++ /*| RX_CFG2_RCV_BEACON_FRAMES */
++ /*| RX_CFG2_RCV_CONTENTION_FREE */
++ /*| RX_CFG2_RCV_CTRL_FRAMES */
++ /*| RX_CFG2_RCV_DATA_FRAMES */
++ /*| RX_CFG2_RCV_BROKEN_FRAMES */
++ /*| RX_CFG2_RCV_MGMT_FRAMES */
++ /*| RX_CFG2_RCV_PROBE_REQ */
++ /*| RX_CFG2_RCV_PROBE_RESP */
++ /*| RX_CFG2_RCV_ACK_FRAMES */
++ /*| RX_CFG2_RCV_OTHER */
++ );
++ break;
++ case ACX_MODE_MONITOR:
++ priv->rx_config_1 = (u16) (0
++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */
++ /* | RX_CFG1_FILTER_SSID */
++ /* | RX_CFG1_FILTER_BCAST */
++ /* | RX_CFG1_RCV_MC_ADDR1 */
++ /* | RX_CFG1_RCV_MC_ADDR0 */
++ /* | RX_CFG1_FILTER_ALL_MULTI */
++ /* | RX_CFG1_FILTER_BSSID */
++ /* | RX_CFG1_FILTER_MAC */
++ | RX_CFG1_RCV_PROMISCUOUS
++ /* | RX_CFG1_INCLUDE_FCS */
++ /* | RX_CFG1_INCLUDE_PHY_HDR */
++ );
++ priv->rx_config_2 = (u16) (0
++ | RX_CFG2_RCV_ASSOC_REQ
++ | RX_CFG2_RCV_AUTH_FRAMES
++ | RX_CFG2_RCV_BEACON_FRAMES
++ | RX_CFG2_RCV_CONTENTION_FREE
++ | RX_CFG2_RCV_CTRL_FRAMES
++ | RX_CFG2_RCV_DATA_FRAMES
++ | RX_CFG2_RCV_BROKEN_FRAMES
++ | RX_CFG2_RCV_MGMT_FRAMES
++ | RX_CFG2_RCV_PROBE_REQ
++ | RX_CFG2_RCV_PROBE_RESP
++ | RX_CFG2_RCV_ACK_FRAMES
++ | RX_CFG2_RCV_OTHER
++ );
++ break;
++ default:
++ priv->rx_config_1 = (u16) (0
++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */
++ /* | RX_CFG1_FILTER_SSID */
++ /* | RX_CFG1_FILTER_BCAST */
++ /* | RX_CFG1_RCV_MC_ADDR1 */
++ /* | RX_CFG1_RCV_MC_ADDR0 */
++ /* | RX_CFG1_FILTER_ALL_MULTI */
++ /* | RX_CFG1_FILTER_BSSID */
++ | RX_CFG1_FILTER_MAC
++ /* | RX_CFG1_RCV_PROMISCUOUS */
++ /* | RX_CFG1_INCLUDE_FCS */
++ /* | RX_CFG1_INCLUDE_PHY_HDR */
++ );
++ priv->rx_config_2 = (u16) (0
++ | RX_CFG2_RCV_ASSOC_REQ
++ | RX_CFG2_RCV_AUTH_FRAMES
++ | RX_CFG2_RCV_BEACON_FRAMES
++ | RX_CFG2_RCV_CONTENTION_FREE
++ | RX_CFG2_RCV_CTRL_FRAMES
++ | RX_CFG2_RCV_DATA_FRAMES
++ /*| RX_CFG2_RCV_BROKEN_FRAMES */
++ | RX_CFG2_RCV_MGMT_FRAMES
++ | RX_CFG2_RCV_PROBE_REQ
++ | RX_CFG2_RCV_PROBE_RESP
++ /*| RX_CFG2_RCV_ACK_FRAMES */
++ | RX_CFG2_RCV_OTHER
++ );
++ break;
++ }
++#ifdef DEBUG_WEP
++ if (IS_ACX100(priv))
++ /* only ACX100 supports that */
++#endif
++ priv->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR;
++
++ acxlog(L_INIT, "setting RXconfig to %04X:%04X\n",
++ priv->rx_config_1, priv->rx_config_2);
++ cfg.rx_cfg1 = cpu_to_le16(priv->rx_config_1);
++ cfg.rx_cfg2 = cpu_to_le16(priv->rx_config_2);
++ acx_s_configure(priv, &cfg, ACX1xx_IE_RXCONFIG);
++}
++
++
++/***********************************************************************
++** acx_e_after_interrupt_task
++*/
++static int
++acx_s_recalib_radio(wlandevice_t *priv)
++{
++ if (IS_ACX111(priv)) {
++ acx111_cmd_radiocalib_t cal;
++
++ printk("%s: recalibrating radio\n", priv->netdev->name);
++ /* automatic recalibration, choose all methods: */
++ cal.methods = cpu_to_le32(0x8000000f);
++ /* automatic recalibration every 60 seconds (value in TUs)
++ * FIXME: what is the firmware default here?? */
++ cal.interval = cpu_to_le32(58594);
++ return acx_s_issue_cmd_timeo(priv, ACX111_CMD_RADIOCALIB,
++ &cal, sizeof(cal), CMD_TIMEOUT_MS(100));
++ } else {
++ if (/* (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0)) &&
++ (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */
++ (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1)) &&
++ (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1)) )
++ return OK;
++ return NOT_OK;
++ }
++}
++
++static void
++acx_s_after_interrupt_recalib(wlandevice_t *priv)
++{
++ int res;
++
++ /* this helps with ACX100 at least;
++ * hopefully ACX111 also does a
++ * recalibration here */
++
++ /* clear flag beforehand, since we want to make sure
++ * it's cleared; then only set it again on specific circumstances */
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* better wait a bit between recalibrations to
++ * prevent overheating due to torturing the card
++ * into working too long despite high temperature
++ * (just a safety measure) */
++ if (priv->recalib_time_last_success
++ && time_before(jiffies, priv->recalib_time_last_success
++ + RECALIB_PAUSE * 60 * HZ)) {
++ priv->recalib_msg_ratelimit++;
++ if (priv->recalib_msg_ratelimit <= 5)
++ printk("%s: less than " STRING(RECALIB_PAUSE)
++ " minutes since last radio recalibration, "
++ "not recalibrating (maybe card is too hot?)\n",
++ priv->netdev->name);
++ if (priv->recalib_msg_ratelimit == 5)
++ printk("disabling above message\n");
++ return;
++ }
++
++ priv->recalib_msg_ratelimit = 0;
++
++ /* note that commands sometimes fail (card busy),
++ * so only clear flag if we were fully successful */
++ res = acx_s_recalib_radio(priv);
++ if (res == OK) {
++ printk("%s: successfully recalibrated radio\n",
++ priv->netdev->name);
++ priv->recalib_time_last_success = jiffies;
++ priv->recalib_failure_count = 0;
++ } else {
++ /* failed: resubmit, but only limited
++ * amount of times within some time range
++ * to prevent endless loop */
++
++ priv->recalib_time_last_success = 0; /* we failed */
++
++ /* if some time passed between last
++ * attempts, then reset failure retry counter
++ * to be able to do next recalib attempt */
++ if (time_after(jiffies, priv->recalib_time_last_attempt + HZ))
++ priv->recalib_failure_count = 0;
++
++ if (++priv->recalib_failure_count <= 5) {
++ priv->recalib_time_last_attempt = jiffies;
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ }
++}
++
++static void
++acx_e_after_interrupt_task(void *data)
++{
++ netdevice_t *dev = (netdevice_t *) data;
++ wlandevice_t *priv = netdev_priv(dev);
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ if (!priv->after_interrupt_jobs)
++ goto end; /* no jobs to do */
++
++#if TX_CLEANUP_IN_SOFTIRQ
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) {
++ acx_lock(priv, flags);
++ acx_l_clean_tx_desc(priv);
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP);
++ acx_unlock(priv, flags);
++ }
++#endif
++ /* we see lotsa tx errors */
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) {
++ acx_s_after_interrupt_recalib(priv);
++ }
++
++ /* a poor interrupt code wanted to do update_card_settings() */
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) {
++ if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
++ acx_s_update_card_settings(priv, 0, 0);
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++ }
++
++ /* 1) we detected that no Scan_Complete IRQ came from fw, or
++ ** 2) we found too many STAs */
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) {
++ acxlog(L_IRQ, "sending a stop scan cmd...\n");
++ acx_s_issue_cmd(priv, ACX1xx_CMD_STOP_SCAN, NULL, 0);
++ /* HACK: set the IRQ bit, since we won't get a
++ * scan complete IRQ any more on ACX111 (works on ACX100!),
++ * since _we_, not a fw, have stopped the scan */
++ SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN);
++ }
++
++ /* either fw sent Scan_Complete or we detected that
++ ** no Scan_Complete IRQ came from fw. Finish scanning,
++ ** pick join partner if any */
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) {
++ if (priv->status == ACX_STATUS_1_SCANNING) {
++ if (OK != acx_s_complete_scan(priv)) {
++ SET_BIT(priv->after_interrupt_jobs,
++ ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++ } else {
++ /* + scan kills current join status - restore it
++ ** (do we need it for STA?) */
++ /* + does it happen only with active scans?
++ ** active and passive scans? ALL scans including
++ ** background one? */
++ /* + was not verified that everything is restored
++ ** (but at least we start to emit beacons again) */
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ acxlog(L_IRQ, "redoing cmd_join_bssid() after scan\n");
++ acx_s_cmd_join_bssid(priv, priv->bssid);
++ }
++ }
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ }
++
++ /* STA auth or assoc timed out, start over again */
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) {
++ acxlog(L_IRQ, "sending a start_scan cmd...\n");
++ acx_s_cmd_start_scan(priv);
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++
++ /* whee, we got positive assoc response! 8) */
++ if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) {
++ acx_ie_generic_t pdr;
++ /* tiny race window exists, checking that we still a STA */
++ switch (priv->mode) {
++ case ACX_MODE_2_STA:
++ pdr.m.aid = cpu_to_le16(priv->aid);
++ acx_s_configure(priv, &pdr, ACX1xx_IE_ASSOC_ID);
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED);
++ acxlog(L_ASSOC|L_DEBUG, "ASSOCIATED!\n");
++ CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE);
++ }
++ }
++end:
++ acx_sem_unlock(priv);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_schedule_after_interrupt_task
++**
++** Schedule the call of the after_interrupt method after leaving
++** the interrupt context.
++*/
++void
++acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag)
++{
++ SET_BIT(priv->after_interrupt_jobs, set_flag);
++ SCHEDULE_WORK(&priv->after_interrupt_task);
++}
++
++
++/***********************************************************************
++*/
++void
++acx_init_task_scheduler(wlandevice_t *priv)
++{
++ /* configure task scheduler */
++ INIT_WORK(&priv->after_interrupt_task, acx_e_after_interrupt_task,
++ priv->netdev);
++}
++
++
++/***********************************************************************
++** acx_s_start
++*/
++void
++acx_s_start(wlandevice_t *priv)
++{
++ FN_ENTER;
++
++ /*
++ * Ok, now we do everything that can possibly be done with ioctl
++ * calls to make sure that when it was called before the card
++ * was up we get the changes asked for
++ */
++
++ SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP
++ |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA
++ |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL
++ |GETSET_TX|GETSET_RX);
++
++ acxlog(L_INIT, "updating initial settings on iface activation...\n");
++ acx_s_update_card_settings(priv, 0, 0);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_update_capabilities
++*/
++void
++acx_update_capabilities(wlandevice_t *priv)
++{
++ u16 cap = 0;
++
++ switch (priv->mode) {
++ case ACX_MODE_3_AP:
++ SET_BIT(cap, WF_MGMT_CAP_ESS); break;
++ case ACX_MODE_0_ADHOC:
++ SET_BIT(cap, WF_MGMT_CAP_IBSS); break;
++ /* other types of stations do not emit beacons */
++ }
++
++ if (priv->wep_restricted) {
++ SET_BIT(cap, WF_MGMT_CAP_PRIVACY);
++ }
++ if (priv->capab_short) {
++ SET_BIT(cap, WF_MGMT_CAP_SHORT);
++ }
++ if (priv->capab_pbcc) {
++ SET_BIT(cap, WF_MGMT_CAP_PBCC);
++ }
++ if (priv->capab_agility) {
++ SET_BIT(cap, WF_MGMT_CAP_AGILITY);
++ }
++ acxlog(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n",
++ priv->capabilities, cap);
++ priv->capabilities = cap;
++}
++
++#ifdef UNUSED
++/***********************************************************************
++** FIXME: check whether this function is indeed acx111 only,
++** rename ALL relevant definitions to indicate actual card scope!
++*/
++void
++acx111_s_read_configoption(wlandevice_t *priv)
++{
++ acx111_ie_configoption_t co, co2;
++ int i;
++ const u8 *pEle;
++
++ if (OK != acx_s_interrogate(priv, &co, ACX111_IE_CONFIG_OPTIONS) ) {
++ return;
++ };
++ if (!(acx_debug & L_DEBUG))
++ return;
++
++ memcpy(&co2.configoption_fixed, &co.configoption_fixed,
++ sizeof(co.configoption_fixed));
++
++ pEle = (u8 *)&co.configoption_fixed + sizeof(co.configoption_fixed) - 4;
++
++ co2.antennas.type = pEle[0];
++ co2.antennas.len = pEle[1];
++ printk("AntennaID:%02X Len:%02X Data:",
++ co2.antennas.type, co2.antennas.len);
++ for (i = 0; i < pEle[1]; i++) {
++ co2.antennas.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += pEle[1] + 2;
++ co2.power_levels.type = pEle[0];
++ co2.power_levels.len = pEle[1];
++ printk("PowerLevelID:%02X Len:%02X Data:",
++ co2.power_levels.type, co2.power_levels.len);
++ for (i = 0; i < pEle[1]*2; i++) {
++ co2.power_levels.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += pEle[1]*2 + 2;
++ co2.data_rates.type = pEle[0];
++ co2.data_rates.len = pEle[1];
++ printk("DataRatesID:%02X Len:%02X Data:",
++ co2.data_rates.type, co2.data_rates.len);
++ for (i = 0; i < pEle[1]; i++) {
++ co2.data_rates.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += pEle[1] + 2;
++ co2.domains.type = pEle[0];
++ co2.domains.len = pEle[1];
++ printk("DomainID:%02X Len:%02X Data:",
++ co2.domains.type, co2.domains.len);
++ for (i = 0; i < pEle[1]; i++) {
++ co2.domains.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += pEle[1] + 2;
++ co2.product_id.type = pEle[0];
++ co2.product_id.len = pEle[1];
++ for (i = 0; i < pEle[1]; i++) {
++ co2.product_id.list[i] = pEle[i+2];
++ }
++ printk("ProductID:%02X Len:%02X Data:%.*s\n",
++ co2.product_id.type, co2.product_id.len,
++ co2.product_id.len, (char *)co2.product_id.list);
++
++ pEle += pEle[1] + 2;
++ co2.manufacturer.type = pEle[0];
++ co2.manufacturer.len = pEle[1];
++ for (i = 0; i < pEle[1]; i++) {
++ co2.manufacturer.list[i] = pEle[i+2];
++ }
++ printk("ManufacturerID:%02X Len:%02X Data:%.*s\n",
++ co2.manufacturer.type, co2.manufacturer.len,
++ co2.manufacturer.len, (char *)co2.manufacturer.list);
++/*
++ printk("EEPROM part:\n");
++ for (i=0; i<58; i++) {
++ printk("%02X =======> 0x%02X\n",
++ i, (u8 *)co.configoption_fixed.NVSv[i-2]);
++ }
++*/
++}
++#endif
++
++
++/***********************************************************************
++*/
++static int __init
++acx_e_init_module(void)
++{
++ int r1;
++
++ acx_struct_size_check();
++
++ printk("acx: this driver is still EXPERIMENTAL\n"
++ "acx: reading README file and/or Craig's HOWTO is "
++ "recommended, visit http://acx100.sf.net in case "
++ "of further questions/discussion\n");
++
++#if defined(CONFIG_ACX_CFI)
++ r1 = acxcfi_e_init_module();
++#elif defined(CONFIG_ACX_PCI)
++ r1 = acxpci_e_init_module();
++#elif defined(CONFIG_ACX_USB)
++ r1 = acxusb_e_init_module();
++#else
++ r1 = -EINVAL;
++#endif
++ if (r1) /* both failed! */
++ return r1;
++ /* return success if at least one succeeded */
++ return 0;
++}
++
++static void __exit
++acx_e_cleanup_module(void)
++{
++#if defined(CONFIG_ACX_PCI)
++ acxcfi_e_cleanup_module();
++#elif defined(CONFIG_ACX_PCI)
++ acxpci_e_cleanup_module();
++#elif defined(CONFIG_ACX_USB)
++ acxusb_e_cleanup_module();
++#endif
++}
++
++module_init(acx_e_init_module)
++module_exit(acx_e_cleanup_module)
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/conv.c bt_kernel/drivers/net/wireless/tiacx/conv.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/conv.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/conv.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,508 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/skbuff.h>
++#include <linux/if_arp.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif
++
++#include "acx.h"
++
++
++/*----------------------------------------------------------------
++* proto_is_stt
++*
++* Searches the 802.1h Selective Translation Table for a given
++* protocol.
++*
++* Arguments:
++* prottype protocol number (in host order) to search for.
++*
++* Returns:
++* 1 - if the table is empty or a match is found.
++* 0 - if the table is non-empty and a match is not found.
++*
++* Comment:
++* Based largely on p80211conv.c of the linux-wlan-ng project
++*----------------------------------------------------------------*/
++static inline int
++proto_is_stt(unsigned int proto)
++{
++ /* Always return found for now. This is the behavior used by the */
++ /* Zoom Win95 driver when 802.1h mode is selected */
++ /* TODO: If necessary, add an actual search we'll probably
++ need this to match the CMAC's way of doing things.
++ Need to do some testing to confirm.
++ */
++
++ if (proto == 0x80f3) /* APPLETALK */
++ return 1;
++
++ return 0;
++/* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */
++}
++
++/* Helpers */
++
++static inline void
++store_llc_snap(struct wlan_llc *llc)
++{
++ llc->dsap = 0xaa; /* SNAP, see IEEE 802 */
++ llc->ssap = 0xaa;
++ llc->ctl = 0x03;
++}
++static inline int
++llc_is_snap(const struct wlan_llc *llc)
++{
++ return (llc->dsap == 0xaa)
++ && (llc->ssap == 0xaa)
++ && (llc->ctl == 0x03);
++}
++static inline void
++store_oui_rfc1042(struct wlan_snap *snap)
++{
++ snap->oui[0] = 0;
++ snap->oui[1] = 0;
++ snap->oui[2] = 0;
++}
++static inline int
++oui_is_rfc1042(const struct wlan_snap *snap)
++{
++ return (snap->oui[0] == 0)
++ && (snap->oui[1] == 0)
++ && (snap->oui[2] == 0);
++}
++static inline void
++store_oui_8021h(struct wlan_snap *snap)
++{
++ snap->oui[0] = 0;
++ snap->oui[1] = 0;
++ snap->oui[2] = 0xf8;
++}
++static inline int
++oui_is_8021h(const struct wlan_snap *snap)
++{
++ return (snap->oui[0] == 0)
++ && (snap->oui[1] == 0)
++ && (snap->oui[2] == 0xf8);
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_ether_to_txbuf
++*
++* Uses the contents of the ether frame to build the elements of
++* the 802.11 frame.
++*
++* We don't actually set up the frame header here. That's the
++* MAC's job. We're only handling conversion of DIXII or 802.3+LLC
++* frames to something that works with 802.11.
++*
++* Comment:
++* Based largely on p80211conv.c of the linux-wlan-ng project
++*----------------------------------------------------------------*/
++int
++acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb)
++{
++ struct wlan_hdr_a3 *w_hdr;
++ struct wlan_ethhdr *e_hdr;
++ struct wlan_llc *e_llc;
++ struct wlan_snap *e_snap;
++ const u8 *a1, *a3;
++ int header_len, payload_len;
++ int result = -1;
++ /* protocol type or data length, depending on whether
++ * DIX or 802.3 ethernet format */
++ u16 proto;
++ u16 fc;
++
++ FN_ENTER;
++
++ if (unlikely(!skb->len)) {
++ acxlog(L_DEBUG, "zero-length skb!\n");
++ goto end;
++ }
++
++ w_hdr = (struct wlan_hdr_a3*)txbuf;
++
++ switch (priv->mode) {
++ case ACX_MODE_MONITOR:
++ /* NB: one day we might want to play with DESC_CTL2_FCS
++ ** Will need to stop doing "- WLAN_FCS_LEN" here then */
++ if (skb->len >= WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_FCS_LEN) {
++ printk("%s: can't tx oversized frame (%d bytes)\n",
++ priv->netdev->name, skb->len);
++ goto end;
++ }
++ memcpy(w_hdr, skb->data, skb->len);
++ result = skb->len;
++ goto end;
++ }
++
++ /* step 1: classify ether frame, DIX or 802.3? */
++ e_hdr = (wlan_ethhdr_t *)skb->data;
++ proto = ntohs(e_hdr->type);
++ if (proto <= 1500) {
++ acxlog(L_DEBUG, "tx: 802.3 len: %d\n", skb->len);
++ /* codes <= 1500 reserved for 802.3 lengths */
++ /* it's 802.3, pass ether payload unchanged, */
++ /* trim off ethernet header and copy payload to txdesc */
++ header_len = WLAN_HDR_A3_LEN;
++ } else {
++ /* it's DIXII, time for some conversion */
++ /* Create 802.11 packet. Header also contains llc and snap. */
++
++ acxlog(L_DEBUG, "tx: DIXII len: %d\n", skb->len);
++
++ /* size of header is 802.11 header + llc + snap */
++ header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t);
++ /* llc is located behind the 802.11 header */
++ e_llc = (wlan_llc_t*)(w_hdr + 1);
++ /* snap is located behind the llc */
++ e_snap = (wlan_snap_t*)(e_llc + 1);
++
++ /* setup the LLC header */
++ store_llc_snap(e_llc);
++
++ /* setup the SNAP header */
++ e_snap->type = htons(proto);
++ if (proto_is_stt(proto)) {
++ store_oui_8021h(e_snap);
++ } else {
++ store_oui_rfc1042(e_snap);
++ }
++ }
++ /* trim off ethernet header and copy payload to txbuf */
++ payload_len = skb->len - sizeof(wlan_ethhdr_t);
++ /* TODO: can we just let acx DMA payload from skb instead? */
++ memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len);
++ payload_len += header_len;
++ result = payload_len;
++
++ /* Set up the 802.11 header */
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi);
++ a1 = e_hdr->daddr;
++ a3 = priv->bssid;
++ break;
++ case ACX_MODE_2_STA:
++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi);
++ a1 = priv->bssid;
++ a3 = e_hdr->daddr;
++ break;
++ case ACX_MODE_3_AP:
++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi);
++ a1 = e_hdr->daddr;
++ a3 = e_hdr->saddr;
++ break;
++ default:
++ printk("%s: error - converting eth to wlan in unknown mode\n",
++ priv->netdev->name);
++ result = -1;
++ goto end;
++ }
++ if (priv->wep_enabled)
++ SET_BIT(fc, WF_FC_ISWEPi);
++
++ w_hdr->fc = fc;
++ w_hdr->dur = 0;
++ MAC_COPY(w_hdr->a1, a1);
++ MAC_COPY(w_hdr->a2, priv->dev_addr);
++ MAC_COPY(w_hdr->a3, a3);
++ w_hdr->seq = 0;
++
++#ifdef DEBUG_CONVERT
++ if (acx_debug & L_DATA) {
++ printk("original eth frame [%d]: ", skb->len);
++ acx_dump_bytes(skb->data, skb->len);
++ printk("802.11 frame [%d]: ", payload_len);
++ acx_dump_bytes(w_hdr, payload_len);
++ }
++#endif
++
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_rxbuf_to_ether
++*
++* Uses the contents of a received 802.11 frame to build an ether
++* frame.
++*
++* This function extracts the src and dest address from the 802.11
++* frame to use in the construction of the eth frame.
++*
++* Based largely on p80211conv.c of the linux-wlan-ng project
++*----------------------------------------------------------------*/
++struct sk_buff*
++acx_rxbuf_to_ether(wlandevice_t *priv, rxbuffer_t *rxbuf)
++{
++ struct wlan_hdr *w_hdr;
++ struct wlan_ethhdr *e_hdr;
++ struct wlan_llc *e_llc;
++ struct wlan_snap *e_snap;
++ struct sk_buff *skb;
++ const u8 *daddr;
++ const u8 *saddr;
++ const u8 *e_payload;
++ int buflen, payload_length;
++ unsigned int payload_offset, mtu;
++ u16 fc;
++
++ FN_ENTER;
++
++ /* This looks complex because it must handle possible
++ ** phy header in rxbuff */
++ w_hdr = acx_get_wlan_hdr(priv, rxbuf);
++ payload_offset = WLAN_HDR_A3_LEN; /* it is relative to w_hdr */
++ payload_length = RXBUF_BYTES_USED(rxbuf) /* entire rxbuff... */
++ - ((u8*)w_hdr - (u8*)rxbuf) /* minus space before 802.11 frame */
++ - WLAN_HDR_A3_LEN; /* minus 802.11 header */
++
++ /* setup some vars for convenience */
++ fc = w_hdr->fc;
++ switch (WF_FC_FROMTODSi & fc) {
++ case 0:
++ daddr = w_hdr->a1;
++ saddr = w_hdr->a2;
++ break;
++ case WF_FC_FROMDSi:
++ daddr = w_hdr->a1;
++ saddr = w_hdr->a3;
++ break;
++ case WF_FC_TODSi:
++ daddr = w_hdr->a3;
++ saddr = w_hdr->a2;
++ break;
++ default: /* WF_FC_FROMTODSi */
++ payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
++ payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
++ daddr = w_hdr->a3;
++ saddr = w_hdr->a4;
++ }
++
++ if ((WF_FC_ISWEPi & fc) && IS_ACX100(priv)) {
++ /* chop off the IV+ICV WEP header and footer */
++ acxlog(L_DATA|L_DEBUG, "rx: WEP packet, "
++ "chopping off IV and ICV\n");
++ payload_offset += WLAN_WEP_IV_LEN;
++ payload_length -= WLAN_WEP_IV_LEN + WLAN_WEP_ICV_LEN;
++ }
++
++ if (unlikely(payload_length < 0)) {
++ printk("%s: rx frame too short, ignored\n", priv->netdev->name);
++ goto ret_null;
++ }
++
++ e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset);
++ e_llc = (wlan_llc_t*) e_hdr;
++ e_snap = (wlan_snap_t*) (e_llc + 1);
++ e_payload = (u8*) (e_snap + 1);
++ mtu = priv->netdev->mtu;
++
++ acxlog(L_DATA, "rx: payload_offset %d, payload_length %d\n",
++ payload_offset, payload_length);
++ acxlog(L_XFER|L_DATA,
++ "rx: frame info: llc=%02X%02X%02X "
++ "snap.oui=%02X%02X%02X snap.type=%04X\n",
++ e_llc->dsap, e_llc->ssap, e_llc->ctl,
++ e_snap->oui[0], e_snap->oui[1], e_snap->oui[2],
++ ntohs(e_snap->type));
++
++ /* Test for the various encodings */
++ if ((payload_length >= sizeof(wlan_ethhdr_t))
++ && ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa))
++ && ( (mac_is_equal(daddr, e_hdr->daddr))
++ || (mac_is_equal(saddr, e_hdr->saddr))
++ )
++ ) {
++ /* 802.3 Encapsulated: */
++ /* wlan frame body contains complete eth frame (header+body) */
++ acxlog(L_DEBUG|L_DATA, "rx: 802.3 ENCAP len=%d\n", payload_length);
++
++ if (unlikely(payload_length > (mtu + ETH_HLEN))) {
++ printk("%s: rx: ENCAP frame too large (%d > %d)\n",
++ priv->netdev->name,
++ payload_length, mtu + ETH_HLEN);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length;
++ /* Attempt to align IP header (14 bytes eth header + 2 = 16) */
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* now copy the data from the 80211 frame */
++ memcpy(skb->data, e_hdr, payload_length);
++
++ } else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t))
++ && llc_is_snap(e_llc) ) {
++ /* wlan frame body contains: AA AA 03 ... (it's a SNAP) */
++
++ if ( !oui_is_rfc1042(e_snap)
++ || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) {
++ acxlog(L_DEBUG|L_DATA, "rx: SNAP+RFC1042 len=%d\n", payload_length);
++ /* wlan frame body contains: AA AA 03 !(00 00 00) ... -or- */
++ /* wlan frame body contains: AA AA 03 00 00 00 0x80f3 ... */
++ /* build eth hdr, type = len, copy AA AA 03... as eth body */
++ /* it's a SNAP + RFC1042 frame && protocol is in STT */
++
++ if (unlikely(payload_length > mtu)) {
++ printk("%s: rx: SNAP frame too large (%d > %d)\n",
++ priv->netdev->name,
++ payload_length, mtu);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length + ETH_HLEN;
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* create 802.3 header */
++ e_hdr = (wlan_ethhdr_t*) skb->data;
++ MAC_COPY(e_hdr->daddr, daddr);
++ MAC_COPY(e_hdr->saddr, saddr);
++ e_hdr->type = htons(payload_length);
++
++ /* Now copy the data from the 80211 frame.
++ Make room in front for the eth header, and keep the
++ llc and snap from the 802.11 payload */
++ memcpy(skb->data + ETH_HLEN,
++ e_llc, payload_length);
++
++ } else {
++ /* wlan frame body contains: AA AA 03 00 00 00 [type] [tail] */
++ /* build eth hdr, type=[type], copy [tail] as eth body */
++ acxlog(L_DEBUG|L_DATA, "rx: 802.1h/RFC1042 len=%d\n",
++ payload_length);
++ /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */
++ /* build a DIXII + RFC894 */
++
++ payload_length -= sizeof(wlan_llc_t) + sizeof(wlan_snap_t);
++ if (unlikely(payload_length > mtu)) {
++ printk("%s: rx: DIXII frame too large (%d > %d)\n",
++ priv->netdev->name,
++ payload_length, mtu);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length + ETH_HLEN;
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* create 802.3 header */
++ e_hdr = (wlan_ethhdr_t *) skb->data;
++ MAC_COPY(e_hdr->daddr, daddr);
++ MAC_COPY(e_hdr->saddr, saddr);
++ e_hdr->type = e_snap->type;
++
++ /* Now copy the data from the 80211 frame.
++ Make room in front for the eth header, and cut off the
++ llc and snap from the 802.11 payload */
++ memcpy(skb->data + ETH_HLEN,
++ e_payload, payload_length);
++ }
++
++ } else {
++ acxlog(L_DEBUG|L_DATA, "rx: NON-ENCAP len=%d\n", payload_length);
++ /* build eth hdr, type=len, copy wlan body as eth body */
++ /* any NON-ENCAP */
++ /* it's a generic 80211+LLC or IPX 'Raw 802.3' */
++ /* build an 802.3 frame */
++
++ if (unlikely(payload_length > mtu)) {
++ printk("%s: rx: OTHER frame too large (%d > %d)\n",
++ priv->netdev->name, payload_length, mtu);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length + ETH_HLEN;
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* set up the 802.3 header */
++ e_hdr = (wlan_ethhdr_t *) skb->data;
++ MAC_COPY(e_hdr->daddr, daddr);
++ MAC_COPY(e_hdr->saddr, saddr);
++ e_hdr->type = htons(payload_length);
++
++ /* now copy the data from the 80211 frame */
++ memcpy(skb->data + ETH_HLEN, e_llc, payload_length);
++ }
++
++ skb->dev = priv->netdev;
++ skb->protocol = eth_type_trans(skb, priv->netdev);
++
++#ifdef DEBUG_CONVERT
++ if (acx_debug & L_DATA) {
++ printk("p802.11 frame [%d]: ", RXBUF_BYTES_RCVD(rxbuf));
++ acx_dump_bytes(w_hdr, RXBUF_BYTES_RCVD(rxbuf));
++ printk("eth frame [%d]: ", skb->len);
++ acx_dump_bytes(skb->data, skb->len);
++ }
++#endif
++
++ FN_EXIT0;
++ return skb;
++
++no_skb:
++ printk("%s: rx: no memory for skb (%d bytes)\n",
++ priv->netdev->name, buflen + 2);
++ret_null:
++ FN_EXIT1((int)NULL);
++ return NULL;
++}
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/ioctl.c bt_kernel/drivers/net/wireless/tiacx/ioctl.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/ioctl.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/ioctl.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,3060 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <asm/io.h>
++#include <asm/uaccess.h> /* required for 2.4.x kernels; verify_write() */
++
++#include <linux/if_arp.h>
++#include <linux/wireless.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif /* WE >= 13 */
++
++#include "acx.h"
++
++
++/*================================================================*/
++
++/* if you plan to reorder something, make sure to reorder all other places
++ * accordingly! */
++/* someone broke SET/GET convention: SETs must have even position, GETs odd */
++#define ACX100_IOCTL SIOCIWFIRSTPRIV
++enum {
++ ACX100_IOCTL_DEBUG = ACX100_IOCTL,
++ ACX100_IOCTL_GET__________UNUSED1,
++ ACX100_IOCTL_SET_PLED,
++ ACX100_IOCTL_GET_PLED,
++ ACX100_IOCTL_SET_RATES,
++ ACX100_IOCTL_LIST_DOM,
++ ACX100_IOCTL_SET_DOM,
++ ACX100_IOCTL_GET_DOM,
++ ACX100_IOCTL_SET_SCAN_PARAMS,
++ ACX100_IOCTL_GET_SCAN_PARAMS,
++ ACX100_IOCTL_SET_PREAMB,
++ ACX100_IOCTL_GET_PREAMB,
++ ACX100_IOCTL_SET_ANT,
++ ACX100_IOCTL_GET_ANT,
++ ACX100_IOCTL_RX_ANT,
++ ACX100_IOCTL_TX_ANT,
++ ACX100_IOCTL_SET_PHY_AMP_BIAS,
++ ACX100_IOCTL_GET_PHY_CHAN_BUSY,
++ ACX100_IOCTL_SET_ED,
++ ACX100_IOCTL_GET__________UNUSED3,
++ ACX100_IOCTL_SET_CCA,
++ ACX100_IOCTL_GET__________UNUSED4,
++ ACX100_IOCTL_MONITOR,
++ ACX100_IOCTL_TEST,
++ ACX100_IOCTL_DBG_SET_MASKS,
++ ACX111_IOCTL_INFO,
++ ACX100_IOCTL_DBG_SET_IO,
++ ACX100_IOCTL_DBG_GET_IO
++};
++
++/* channel frequencies
++ * TODO: Currently, every other 802.11 driver keeps its own copy of this. In
++ * the long run this should be integrated into ieee802_11.h or wireless.h or
++ * whatever IEEE802.11x framework evolves */
++static const u16 acx_channel_freq[] = {
++ 2412, 2417, 2422, 2427, 2432, 2437, 2442,
++ 2447, 2452, 2457, 2462, 2467, 2472, 2484,
++};
++
++static const struct iw_priv_args acx_ioctl_private_args[] = {
++#if ACX_DEBUG
++{ cmd : ACX100_IOCTL_DEBUG,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetDebug" },
++#endif
++{ cmd : ACX100_IOCTL_SET_PLED,
++ set_args : IW_PRIV_TYPE_BYTE | 2,
++ get_args : 0,
++ name : "SetLEDPower" },
++{ cmd : ACX100_IOCTL_GET_PLED,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2,
++ name : "GetLEDPower" },
++{ cmd : ACX100_IOCTL_SET_RATES,
++ set_args : IW_PRIV_TYPE_CHAR | 256,
++ get_args : 0,
++ name : "SetRates" },
++{ cmd : ACX100_IOCTL_LIST_DOM,
++ set_args : 0,
++ get_args : 0,
++ name : "ListRegDomain" },
++{ cmd : ACX100_IOCTL_SET_DOM,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetRegDomain" },
++{ cmd : ACX100_IOCTL_GET_DOM,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ name : "GetRegDomain" },
++{ cmd : ACX100_IOCTL_SET_SCAN_PARAMS,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
++ get_args : 0,
++ name : "SetScanParams" },
++{ cmd : ACX100_IOCTL_GET_SCAN_PARAMS,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
++ name : "GetScanParams" },
++{ cmd : ACX100_IOCTL_SET_PREAMB,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetSPreamble" },
++{ cmd : ACX100_IOCTL_GET_PREAMB,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ name : "GetSPreamble" },
++{ cmd : ACX100_IOCTL_SET_ANT,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetAntenna" },
++{ cmd : ACX100_IOCTL_GET_ANT,
++ set_args : 0,
++ get_args : 0,
++ name : "GetAntenna" },
++{ cmd : ACX100_IOCTL_RX_ANT,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetRxAnt" },
++{ cmd : ACX100_IOCTL_TX_ANT,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetTxAnt" },
++{ cmd : ACX100_IOCTL_SET_PHY_AMP_BIAS,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetPhyAmpBias"},
++{ cmd : ACX100_IOCTL_GET_PHY_CHAN_BUSY,
++ set_args : 0,
++ get_args : 0,
++ name : "GetPhyChanBusy" },
++{ cmd : ACX100_IOCTL_SET_ED,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetED" },
++{ cmd : ACX100_IOCTL_SET_CCA,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetCCA" },
++{ cmd : ACX100_IOCTL_MONITOR,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
++ get_args : 0,
++ name : "monitor" },
++{ cmd : ACX100_IOCTL_TEST,
++ set_args : 0,
++ get_args : 0,
++ name : "Test" },
++{ cmd : ACX100_IOCTL_DBG_SET_MASKS,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
++ get_args : 0,
++ name : "DbgSetMasks" },
++{ cmd : ACX111_IOCTL_INFO,
++ set_args : 0,
++ get_args : 0,
++ name : "GetAcx111Info" },
++{ cmd : ACX100_IOCTL_DBG_SET_IO,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
++ get_args : 0,
++ name : "DbgSetIO" },
++{ cmd : ACX100_IOCTL_DBG_GET_IO,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3,
++ get_args : 0,
++ name : "DbgGetIO" },
++};
++
++
++/*------------------------------------------------------------------------------
++ * acx_ioctl_commit
++ *----------------------------------------------------------------------------*/
++static int
++acx_ioctl_commit(struct net_device *dev,
++ struct iw_request_info *info,
++ void *zwrq, char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++ if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
++ acx_s_update_card_settings(priv, 0, 0);
++ acx_sem_unlock(priv);
++
++ FN_EXIT0;
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_name(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ char *cwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" };
++
++ strcpy(cwrq, names[IS_ACX111(priv) ? 0 : 1]);
++
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_freq
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_freq(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_freq *fwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int channel = -1;
++ unsigned int mult = 1;
++ int result;
++
++ FN_ENTER;
++
++ if (fwrq->e == 0 && fwrq->m <= 1000) {
++ /* Setting by channel number */
++ channel = fwrq->m;
++ } else {
++ /* If setting by frequency, convert to a channel */
++ int i;
++
++ for (i = 0; i < (6 - fwrq->e); i++)
++ mult *= 10;
++
++ for (i = 1; i <= 14; i++)
++ if (fwrq->m == acx_channel_freq[i - 1] * mult)
++ channel = i;
++ }
++
++ if (channel > 14) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(priv);
++
++ priv->channel = channel;
++ /* hmm, the following code part is strange, but this is how
++ * it was being done before... */
++ acxlog(L_IOCTL, "Changing to channel %d\n", channel);
++ SET_BIT(priv->set_mask, GETSET_CHANNEL);
++
++ result = -EINPROGRESS; /* need to call commit handler */
++
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static inline int
++acx_ioctl_get_freq(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_freq *fwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ fwrq->e = 0;
++ fwrq->m = priv->channel;
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_mode
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_mode(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ u32 *uwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ switch (*uwrq) {
++ case IW_MODE_AUTO:
++ priv->mode = ACX_MODE_OFF;
++ break;
++#if WIRELESS_EXT > 14
++ case IW_MODE_MONITOR:
++ priv->mode = ACX_MODE_MONITOR;
++ break;
++#endif /* WIRELESS_EXT > 14 */
++ case IW_MODE_ADHOC:
++ priv->mode = ACX_MODE_0_ADHOC;
++ break;
++ case IW_MODE_INFRA:
++ priv->mode = ACX_MODE_2_STA;
++ break;
++ case IW_MODE_MASTER:
++ printk("acx: master mode (HostAP) is very, very "
++ "experimental! It might work partially, but "
++ "better get prepared for nasty surprises "
++ "at any time\n");
++ priv->mode = ACX_MODE_3_AP;
++ break;
++ case IW_MODE_REPEAT:
++ case IW_MODE_SECOND:
++ default:
++ result = -EOPNOTSUPP;
++ goto end_unlock;
++ }
++
++ acxlog(L_ASSOC, "new priv->mode=%d\n", priv->mode);
++ SET_BIT(priv->set_mask, GETSET_MODE);
++ result = -EINPROGRESS;
++
++end_unlock:
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_mode(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ u32 *uwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result = 0;
++
++ switch (priv->mode) {
++ case ACX_MODE_OFF:
++ *uwrq = IW_MODE_AUTO; break;
++#if WIRELESS_EXT > 14
++ case ACX_MODE_MONITOR:
++ *uwrq = IW_MODE_MONITOR; break;
++#endif /* WIRELESS_EXT > 14 */
++ case ACX_MODE_0_ADHOC:
++ *uwrq = IW_MODE_ADHOC; break;
++ case ACX_MODE_2_STA:
++ *uwrq = IW_MODE_INFRA; break;
++ case ACX_MODE_3_AP:
++ *uwrq = IW_MODE_MASTER; break;
++ default:
++ result = -EOPNOTSUPP;
++ }
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_set_sens(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ priv->sensitivity = (1 == vwrq->disabled) ? 0 : vwrq->value;
++ SET_BIT(priv->set_mask, GETSET_SENSITIVITY);
++
++ acx_sem_unlock(priv);
++
++ return -EINPROGRESS;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_sens(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ /* acx_sem_lock(priv); */
++
++ vwrq->value = priv->sensitivity;
++ vwrq->disabled = (vwrq->value == 0);
++ vwrq->fixed = 1;
++
++ /* acx_sem_unlock(priv); */
++
++ return OK;
++}
++
++
++/*------------------------------------------------------------------------------
++ * acx_ioctl_set_ap
++ *
++ * Sets the MAC address of the AP to associate with
++ *----------------------------------------------------------------------------*/
++static int
++acx_ioctl_set_ap(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct sockaddr *awrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result = 0;
++ const u8 *ap;
++
++ FN_ENTER;
++ if (NULL == awrq) {
++ result = -EFAULT;
++ goto end;
++ }
++ if (ARPHRD_ETHER != awrq->sa_family) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ ap = awrq->sa_data;
++ acxlog_mac(L_IOCTL, "Set AP=", ap, "\n");
++
++ MAC_COPY(priv->ap, ap);
++
++ /* We want to start rescan in managed or ad-hoc mode,
++ ** otherwise just set priv->ap.
++ ** "iwconfig <if> ap <mac> mode managed": we must be able
++ ** to set ap _first_ and _then_ set mode */
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* FIXME: if there is a convention on what zero AP means,
++ ** please add a comment about that. I don't know of any --vda */
++ if (mac_is_zero(ap)) {
++ /* "off" == 00:00:00:00:00:00 */
++ MAC_BCAST(priv->ap);
++ acxlog(L_IOCTL, "Not reassociating\n");
++ } else {
++ acxlog(L_IOCTL, "Forcing reassociation\n");
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++ }
++ break;
++ }
++ result = -EINPROGRESS;
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_ap(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct sockaddr *awrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ if (ACX_STATUS_4_ASSOCIATED == priv->status) {
++ /* as seen in Aironet driver, airo.c */
++ MAC_COPY(awrq->sa_data, priv->bssid);
++ } else {
++ MAC_ZERO(awrq->sa_data);
++ }
++ awrq->sa_family = ARPHRD_ETHER;
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_aplist
++*
++* Comment: deprecated in favour of iwscan.
++* We simply return the list of currently available stations in range,
++* don't do a new scan.
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_aplist(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ struct sockaddr *address = (struct sockaddr *) extra;
++ struct iw_quality qual[IW_MAX_AP];
++ int i, cur;
++ int result = OK;
++
++ FN_ENTER;
++
++ /* we have AP list only in STA mode */
++ if (ACX_MODE_2_STA != priv->mode) {
++ result = -EOPNOTSUPP;
++ goto end;
++ }
++
++ cur = 0;
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ struct client *bss = &priv->sta_list[i];
++ if (!bss->used) continue;
++ MAC_COPY(address[cur].sa_data, bss->bssid);
++ address[cur].sa_family = ARPHRD_ETHER;
++ qual[cur].level = bss->sir;
++ qual[cur].noise = bss->snr;
++#ifndef OLD_QUALITY
++ qual[cur].qual = acx_signal_determine_quality(qual[cur].level,
++ qual[cur].noise);
++#else
++ qual[cur].qual = (qual[cur].noise <= 100) ?
++ 100 - qual[cur].noise : 0;
++#endif
++ /* no scan: level/noise/qual not updated: */
++ qual[cur].updated = 0;
++ cur++;
++ }
++ if (cur) {
++ dwrq->flags = 1;
++ memcpy(extra + sizeof(struct sockaddr)*cur, &qual,
++ sizeof(struct iw_quality)*cur);
++ }
++ dwrq->length = cur;
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_set_scan(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ /* don't start scan if device is not up yet */
++ if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ result = -EAGAIN;
++ goto end_unlock;
++ }
++
++ /* This is NOT a rescan for new AP!
++ ** Do not use SET_BIT(GETSET_RESCAN); */
++ acx_s_cmd_start_scan(priv);
++ result = OK;
++
++end_unlock:
++ acx_sem_unlock(priv);
++/* end: */
++ FN_EXIT1(result);
++ return result;
++}
++
++
++#if WIRELESS_EXT > 13
++/***********************************************************************
++** acx_s_scan_add_station
++*/
++/* helper. not sure wheter it's really a _s_leeping fn */
++static char*
++acx_s_scan_add_station(
++ wlandevice_t *priv,
++ char *ptr,
++ char *end_buf,
++ struct client *bss)
++{
++ struct iw_event iwe;
++ char *ptr_rate;
++
++ FN_ENTER;
++
++ /* MAC address has to be added first */
++ iwe.cmd = SIOCGIWAP;
++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
++ MAC_COPY(iwe.u.ap_addr.sa_data, bss->bssid);
++ acxlog_mac(L_IOCTL, "scan, station address: ", bss->bssid, "\n");
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_ADDR_LEN);
++
++ /* Add ESSID */
++ iwe.cmd = SIOCGIWESSID;
++ iwe.u.data.length = bss->essid_len;
++ iwe.u.data.flags = 1;
++ acxlog(L_IOCTL, "scan, essid: %s\n", bss->essid);
++ ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid);
++
++ /* Add mode */
++ iwe.cmd = SIOCGIWMODE;
++ if (bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)) {
++ if (bss->cap_info & WF_MGMT_CAP_ESS)
++ iwe.u.mode = IW_MODE_MASTER;
++ else
++ iwe.u.mode = IW_MODE_ADHOC;
++ acxlog(L_IOCTL, "scan, mode: %d\n", iwe.u.mode);
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_UINT_LEN);
++ }
++
++ /* Add frequency */
++ iwe.cmd = SIOCGIWFREQ;
++ iwe.u.freq.m = acx_channel_freq[bss->channel - 1] * 100000;
++ iwe.u.freq.e = 1;
++ acxlog(L_IOCTL, "scan, frequency: %d\n", iwe.u.freq.m);
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_FREQ_LEN);
++
++ /* Add link quality */
++ iwe.cmd = IWEVQUAL;
++ /* FIXME: these values should be expressed in dBm, but we don't know
++ * how to calibrate it yet */
++ iwe.u.qual.level = bss->sir;
++ iwe.u.qual.noise = bss->snr;
++#ifndef OLD_QUALITY
++ iwe.u.qual.qual = acx_signal_determine_quality(iwe.u.qual.level,
++ iwe.u.qual.noise);
++#else
++ iwe.u.qual.qual = (iwe.u.qual.noise <= 100) ?
++ 100 - iwe.u.qual.noise : 0;
++#endif
++ iwe.u.qual.updated = 7;
++ acxlog(L_IOCTL, "scan, link quality: %d/%d/%d\n",
++ iwe.u.qual.level, iwe.u.qual.noise, iwe.u.qual.qual);
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_QUAL_LEN);
++
++ /* Add encryption */
++ iwe.cmd = SIOCGIWENCODE;
++ if (bss->cap_info & WF_MGMT_CAP_PRIVACY)
++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
++ else
++ iwe.u.data.flags = IW_ENCODE_DISABLED;
++ iwe.u.data.length = 0;
++ acxlog(L_IOCTL, "scan, encryption flags: %X\n", iwe.u.data.flags);
++ ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid);
++
++ /* add rates */
++ iwe.cmd = SIOCGIWRATE;
++ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
++ ptr_rate = ptr + IW_EV_LCP_LEN;
++
++ {
++ u16 rate = bss->rate_cap;
++ const u8* p = bitpos2ratebyte;
++ while (rate) {
++ if (rate & 1) {
++ iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */
++ acxlog(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value);
++ ptr = iwe_stream_add_value(ptr, ptr_rate, end_buf, &iwe, IW_EV_PARAM_LEN);
++ }
++ rate >>= 1;
++ p++;
++ }}
++
++ if ((ptr_rate - ptr) > (ptrdiff_t)IW_EV_LCP_LEN)
++ ptr = ptr_rate;
++
++ /* drop remaining station data items for now */
++
++ FN_EXIT0;
++ return ptr;
++}
++
++
++/***********************************************************************
++ * acx_ioctl_get_scan
++ */
++static int
++acx_ioctl_get_scan(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ char *ptr = extra;
++ int i;
++ int result = OK;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ /* no scan available if device is not up yet */
++ if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ acxlog(L_IOCTL, "iface not up yet\n");
++ result = -EAGAIN;
++ goto end_unlock;
++ }
++#if 0 /* Why is this needed? If needed, add a comment */
++ if (priv->scan_start && time_before(jiffies, priv->scan_start + 3*HZ)) {
++ acxlog(L_IOCTL, "scan in progress, no results yet\n");
++ result = -EAGAIN;
++ goto end_unlock;
++ }
++#endif
++
++#ifdef ENODATA_TO_BE_USED_AFTER_SCAN_ERROR_ONLY
++ if (priv->bss_table_count == 0) {
++ /* no stations found */
++ result = -ENODATA;
++ goto end_unlock;
++ }
++#endif
++
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ struct client *bss = &priv->sta_list[i];
++ if (!bss->used) continue;
++ ptr = acx_s_scan_add_station(priv, ptr,
++ extra + IW_SCAN_MAX_DATA, bss);
++ }
++ dwrq->length = ptr - extra;
++ dwrq->flags = 0;
++
++end_unlock:
++ acx_sem_unlock(priv);
++/* end: */
++ FN_EXIT1(result);
++ return result;
++}
++#endif /* WIRELESS_EXT > 13 */
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_essid
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_essid(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int len = dwrq->length;
++ int result;
++
++ FN_ENTER;
++
++ acxlog(L_IOCTL, "Set ESSID '%*s', length %d, flags 0x%04X\n",
++ len, extra, len, dwrq->flags);
++
++ if (len < 0) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(priv);
++
++ /* ESSID disabled? */
++ if (0 == dwrq->flags) {
++ priv->essid_active = 0;
++
++ } else {
++ if (dwrq->length > IW_ESSID_MAX_SIZE+1) {
++ result = -E2BIG;
++ goto end_unlock;
++ }
++
++ if (len > sizeof(priv->essid))
++ len = sizeof(priv->essid);
++ memcpy(priv->essid, extra, len-1);
++ priv->essid[len-1] = '\0';
++ /* Paranoia: just in case there is a '\0'... */
++ priv->essid_len = strlen(priv->essid);
++ priv->essid_active = 1;
++ }
++
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++
++ result = -EINPROGRESS;
++
++end_unlock:
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_essid(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ dwrq->flags = priv->essid_active;
++ if (priv->essid_active) {
++ memcpy(extra, priv->essid, priv->essid_len);
++ extra[priv->essid_len] = '\0';
++ dwrq->length = priv->essid_len + 1;
++ dwrq->flags = 1;
++ }
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_update_client_rates
++*----------------------------------------------------------------*/
++static void
++acx_l_update_client_rates(wlandevice_t *priv, u16 rate)
++{
++ int i;
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ client_t *clt = &priv->sta_list[i];
++ if (!clt->used) continue;
++ clt->rate_cfg = (clt->rate_cap & rate);
++ if (!clt->rate_cfg) {
++ /* no compatible rates left: kick client */
++ acxlog_mac(L_ASSOC, "client ",clt->address," kicked: "
++ "rates are not compatible anymore\n");
++ acx_l_sta_list_del(priv, clt);
++ continue;
++ }
++ clt->rate_cur &= clt->rate_cfg;
++ if (!clt->rate_cur) {
++ /* current rate become invalid, choose a valid one */
++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
++ }
++ clt->fallback_count = clt->stepup_count = 0;
++ clt->ignore_count = 16;
++ }
++ switch (priv->mode) {
++ case ACX_MODE_2_STA:
++ if (priv->ap_client && !priv->ap_client->used) {
++ /* Owwww... we kicked our AP!! :) */
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++ }
++ }
++}
++
++
++/***********************************************************************
++*/
++/* maps bits from acx111 rate to rate in Mbits */
++static const unsigned int
++acx111_rate_tbl[] = {
++ 1000000, /* 0 */
++ 2000000, /* 1 */
++ 5500000, /* 2 */
++ 6000000, /* 3 */
++ 9000000, /* 4 */
++ 11000000, /* 5 */
++ 12000000, /* 6 */
++ 18000000, /* 7 */
++ 22000000, /* 8 */
++ 24000000, /* 9 */
++ 36000000, /* 10 */
++ 48000000, /* 11 */
++ 54000000, /* 12 */
++ 500000, /* 13, should not happen */
++ 500000, /* 14, should not happen */
++ 500000, /* 15, should not happen */
++};
++
++/***********************************************************************
++ * acx_ioctl_set_rate
++ */
++static int
++acx_ioctl_set_rate(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ u16 txrate_cfg = 1;
++ unsigned long flags;
++ int autorate;
++ int result = -EINVAL;
++
++ FN_ENTER;
++ acxlog(L_IOCTL,
++ "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n",
++ vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags);
++
++ if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) {
++ int i = VEC_SIZE(acx111_rate_tbl)-1;
++ if (vwrq->value == -1)
++ /* "iwconfig rate auto" --> choose highest */
++ vwrq->value = IS_ACX100(priv) ? 22000000 : 54000000;
++ while (i >= 0) {
++ if (vwrq->value == acx111_rate_tbl[i]) {
++ txrate_cfg <<= i;
++ i = 0;
++ break;
++ }
++ i--;
++ }
++ if (i == -1) { /* no matching rate */
++ result = -EINVAL;
++ goto end;
++ }
++ } else { /* rate N, N<1000 (driver specific): we don't use this */
++ result = -EOPNOTSUPP;
++ goto end;
++ }
++ /* now: only one bit is set in txrate_cfg, corresponding to
++ ** indicated rate */
++
++ autorate = (vwrq->fixed == 0) && (RATE111_1 != txrate_cfg);
++ if (autorate) {
++ /* convert 00100000 -> 00111111 */
++ txrate_cfg = (txrate_cfg<<1)-1;
++ }
++
++ if (IS_ACX100(priv)) {
++ txrate_cfg &= RATE111_ACX100_COMPAT;
++ if (!txrate_cfg) {
++ result = -ENOTSUPP; /* rate is not supported by acx100 */
++ goto end;
++ }
++ }
++
++ acx_sem_lock(priv);
++ acx_lock(priv, flags);
++
++ priv->rate_auto = autorate;
++ priv->rate_oper = txrate_cfg;
++ priv->rate_basic = txrate_cfg;
++ /* only do that in auto mode, non-auto will be able to use
++ * one specific Tx rate only anyway */
++ if (autorate) {
++ /* only use 802.11b base rates, for standard 802.11b H/W
++ * compatibility */
++ priv->rate_basic &= RATE111_80211B_COMPAT;
++ }
++ priv->rate_bcast = 1 << lowest_bit(txrate_cfg);
++ if (IS_ACX100(priv))
++ priv->rate_bcast100 = acx_rate111to100(priv->rate_bcast);
++ acx_l_update_ratevector(priv);
++ acx_l_update_client_rates(priv, txrate_cfg);
++
++ /* Do/don't do tx rate fallback; beacon contents and rate */
++ SET_BIT(priv->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES);
++ result = -EINPROGRESS;
++
++ acx_unlock(priv, flags);
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_rate
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_rate(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ /* TODO: remember rate of last tx, show it. think about multiple peers... */
++ wlandevice_t *priv = netdev_priv(dev);
++ vwrq->value = acx111_rate_tbl[highest_bit(priv->rate_oper)];
++ vwrq->fixed = !priv->rate_auto;
++ vwrq->disabled = 0;
++ return OK;
++}
++
++static int
++acx_ioctl_set_rts(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int val = vwrq->value;
++
++ if (vwrq->disabled)
++ val = 2312;
++ if ((val < 0) || (val > 2312))
++ return -EINVAL;
++
++ priv->rts_threshold = val;
++ return OK;
++}
++
++static inline int
++acx_ioctl_get_rts(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ vwrq->value = priv->rts_threshold;
++ vwrq->disabled = (vwrq->value >= 2312);
++ vwrq->fixed = 1;
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_encode
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_encode(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int index;
++ int result;
++
++ FN_ENTER;
++ acxlog(L_IOCTL,
++ "Set Encoding flags=0x%04X, size=%d, key: %s\n",
++ dwrq->flags, dwrq->length, extra ? "set" : "No key");
++
++ acx_sem_lock(priv);
++
++ index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
++
++ if (dwrq->length > 0) {
++ /* if index is 0 or invalid, use default key */
++ if ((index < 0) || (index > 3))
++ index = (int)priv->wep_current_index;
++
++ if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) {
++ if (dwrq->length > 29)
++ dwrq->length = 29; /* restrict it */
++
++ if (dwrq->length > 13)
++ priv->wep_keys[index].size = 29; /* 29*8 == 232, WEP256 */
++ else
++ if (dwrq->length > 5)
++ priv->wep_keys[index].size = 13; /* 13*8 == 104bit, WEP128 */
++ else
++ if (dwrq->length > 0)
++ priv->wep_keys[index].size = 5; /* 5*8 == 40bit, WEP64 */
++ else
++ /* disable key */
++ priv->wep_keys[index].size = 0;
++
++ memset(priv->wep_keys[index].key, 0, sizeof(priv->wep_keys[index].key));
++ memcpy(priv->wep_keys[index].key, extra, dwrq->length);
++ }
++
++ } else {
++ /* set transmit key */
++ if ((index >= 0) && (index <= 3))
++ priv->wep_current_index = index;
++ else
++ if (0 == (dwrq->flags & IW_ENCODE_MODE)) {
++ /* complain if we were not just setting
++ * the key mode */
++ result = -EINVAL;
++ goto end_unlock;
++ }
++ }
++
++ priv->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED);
++
++ if (dwrq->flags & IW_ENCODE_OPEN) {
++ priv->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
++ priv->wep_restricted = 0;
++
++ } else if (dwrq->flags & IW_ENCODE_RESTRICTED) {
++ priv->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
++ priv->wep_restricted = 1;
++ }
++
++ /* set flag to make sure the card WEP settings get updated */
++ SET_BIT(priv->set_mask, GETSET_WEP);
++
++ acxlog(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n",
++ dwrq->length, extra,
++ dwrq->flags);
++
++ for (index = 0; index <= 3; index++) {
++ if (priv->wep_keys[index].size) {
++ acxlog(L_IOCTL,
++ "index=%d, size=%d, key at 0x%p\n",
++ priv->wep_keys[index].index,
++ (int) priv->wep_keys[index].size,
++ priv->wep_keys[index].key);
++ }
++ }
++ result = -EINPROGRESS;
++
++end_unlock:
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_encode
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_encode(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
++
++ if (priv->wep_enabled == 0) {
++ dwrq->flags = IW_ENCODE_DISABLED;
++
++ } else {
++ if ((index < 0) || (index > 3))
++ index = (int)priv->wep_current_index;
++
++ dwrq->flags =
++ (priv->wep_restricted == 1) ? IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
++ dwrq->length = priv->wep_keys[index].size;
++
++ memcpy(extra,
++ priv->wep_keys[index].key,
++ priv->wep_keys[index].size);
++ }
++
++ /* set the current index */
++ SET_BIT(dwrq->flags, index + 1);
++
++ acxlog(L_IOCTL, "len=%d, key=%p, flags=0x%X\n",
++ dwrq->length, dwrq->pointer,
++ dwrq->flags);
++
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_set_power(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acxlog(L_IOCTL, "Set 802.11 Power Save flags=0x%04X\n", vwrq->flags);
++ if (vwrq->disabled) {
++ CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE);
++ SET_BIT(priv->set_mask, GETSET_POWER_80211);
++ return -EINPROGRESS;
++ }
++ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
++ u16 ps_timeout = (vwrq->value * 1024) / 1000;
++
++ if (ps_timeout > 255)
++ ps_timeout = 255;
++ acxlog(L_IOCTL, "setting PS timeout value to %d time units "
++ "due to %dus\n", ps_timeout, vwrq->value);
++ priv->ps_hangover_period = ps_timeout;
++ } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
++ u16 ps_periods = vwrq->value / 1000000;
++
++ if (ps_periods > 255)
++ ps_periods = 255;
++ acxlog(L_IOCTL, "setting PS period value to %d periods "
++ "due to %dus\n", ps_periods, vwrq->value);
++ priv->ps_listen_interval = ps_periods;
++ CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK);
++ SET_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL);
++ }
++ switch (vwrq->flags & IW_POWER_MODE) {
++ /* FIXME: are we doing the right thing here? */
++ case IW_POWER_UNICAST_R:
++ CLEAR_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS);
++ break;
++ case IW_POWER_MULTICAST_R:
++ SET_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS);
++ break;
++ case IW_POWER_ALL_R:
++ SET_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS);
++ break;
++ case IW_POWER_ON:
++ break;
++ default:
++ acxlog(L_IOCTL, "unknown PS mode\n");
++ return -EINVAL;
++ }
++
++ SET_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE);
++ SET_BIT(priv->set_mask, GETSET_POWER_80211);
++
++ return -EINPROGRESS;
++
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_power(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acxlog(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags);
++ vwrq->disabled = ((priv->ps_wakeup_cfg & PS_CFG_ENABLE) == 0);
++ if (vwrq->disabled)
++ return OK;
++ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
++ vwrq->value = priv->ps_hangover_period * 1000 / 1024;
++ vwrq->flags = IW_POWER_TIMEOUT;
++ } else {
++ vwrq->value = priv->ps_listen_interval * 1000000;
++ vwrq->flags = IW_POWER_PERIOD|IW_POWER_RELATIVE;
++ }
++ if (priv->ps_options & PS_OPT_STILL_RCV_BCASTS)
++ SET_BIT(vwrq->flags, IW_POWER_ALL_R);
++ else
++ SET_BIT(vwrq->flags, IW_POWER_UNICAST_R);
++
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_txpow
++*----------------------------------------------------------------*/
++static inline int
++acx_ioctl_get_txpow(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ vwrq->flags = IW_TXPOW_DBM;
++ vwrq->disabled = 0;
++ vwrq->fixed = 1;
++ vwrq->value = priv->tx_level_dbm;
++
++ acxlog(L_IOCTL, "get txpower:%d dBm\n", priv->tx_level_dbm);
++
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_txpow
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_txpow(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++ acxlog(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n",
++ vwrq->value, vwrq->disabled, vwrq->flags);
++
++ acx_sem_lock(priv);
++
++ if (vwrq->disabled != priv->tx_disabled) {
++ SET_BIT(priv->set_mask, GETSET_TX); /* Tx status needs update later */
++ }
++
++ priv->tx_disabled = vwrq->disabled;
++ if (vwrq->value == -1) {
++ if (vwrq->disabled) {
++ priv->tx_level_dbm = 0;
++ acxlog(L_IOCTL, "disable radio tx\n");
++ } else {
++ /* priv->tx_level_auto = 1; */
++ acxlog(L_IOCTL, "set tx power auto (NIY)\n");
++ }
++ } else {
++ priv->tx_level_dbm = vwrq->value <= 20 ? vwrq->value : 20;
++ /* priv->tx_level_auto = 0; */
++ acxlog(L_IOCTL, "set txpower=%d dBm\n", priv->tx_level_dbm);
++ }
++ SET_BIT(priv->set_mask, GETSET_TXPOWER);
++
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_range
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_range(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ if (dwrq->pointer != NULL) {
++ struct iw_range *range = (struct iw_range *)extra;
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned int i;
++
++ dwrq->length = sizeof(struct iw_range);
++ memset(range, 0, sizeof(struct iw_range));
++ range->num_channels = 0;
++ for (i = 1; i <= 14; i++) {
++ if (priv->reg_dom_chanmask & (1 << (i - 1))) {
++ range->freq[range->num_channels].i = i;
++ range->freq[range->num_channels].m = acx_channel_freq[i - 1] * 100000;
++ range->freq[range->num_channels++].e = 1; /* MHz values */
++ }
++ }
++ range->num_frequency = range->num_channels;
++
++ range->min_rts = 0;
++ range->max_rts = 2312;
++ /* range->min_frag = 256;
++ * range->max_frag = 2312;
++ */
++
++ range->encoding_size[0] = 5;
++ range->encoding_size[1] = 13;
++ range->encoding_size[2] = 29;
++ range->num_encoding_sizes = 3;
++ range->max_encoding_tokens = 4;
++
++ range->min_pmp = 0;
++ range->max_pmp = 5000000;
++ range->min_pmt = 0;
++ range->max_pmt = 65535 * 1000;
++ range->pmp_flags = IW_POWER_PERIOD;
++ range->pmt_flags = IW_POWER_TIMEOUT;
++ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
++
++ for (i = 0; i <= IW_MAX_TXPOWER - 1; i++)
++ range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1);
++ range->num_txpower = IW_MAX_TXPOWER;
++ range->txpower_capa = IW_TXPOW_DBM;
++
++ range->we_version_compiled = WIRELESS_EXT;
++ range->we_version_source = 0x9;
++
++ range->retry_capa = IW_RETRY_LIMIT;
++ range->retry_flags = IW_RETRY_LIMIT;
++ range->min_retry = 1;
++ range->max_retry = 255;
++
++ range->r_time_flags = IW_RETRY_LIFETIME;
++ range->min_r_time = 0;
++ /* FIXME: lifetime ranges and orders of magnitude are strange?? */
++ range->max_r_time = 65535;
++
++ if (IS_USB(priv))
++ range->sensitivity = 0;
++ else if (IS_ACX111(priv))
++ range->sensitivity = 3;
++ else
++ range->sensitivity = 255;
++
++ for (i=0; i < priv->rate_supported_len; i++) {
++ range->bitrate[i] = (priv->rate_supported[i] & ~0x80) * 500000;
++ /* never happens, but keep it, to be safe: */
++ if (range->bitrate[i] == 0)
++ break;
++ }
++ range->num_bitrates = i;
++
++ range->max_qual.qual = 100;
++ range->max_qual.level = 100;
++ range->max_qual.noise = 100;
++ /* TODO: better values */
++ range->avg_qual.qual = 90;
++ range->avg_qual.level = 80;
++ range->avg_qual.noise = 2;
++ }
++
++ return OK;
++}
++
++
++/*================================================================*/
++/* Private functions */
++/*================================================================*/
++
++#if WIRELESS_EXT < 13
++/*----------------------------------------------------------------
++* acx_ioctl_get_iw_priv
++*
++* Comment: I added the monitor mode and changed the stuff below
++* to look more like the orinoco driver
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_iw_priv(struct iwreq *iwr)
++{
++ int result = -EINVAL;
++
++ if (!iwr->u.data.pointer)
++ return -EINVAL;
++ result = verify_area(VERIFY_WRITE, iwr->u.data.pointer,
++ sizeof(acx_ioctl_private_args));
++ if (result)
++ return result;
++
++ iwr->u.data.length = VEC_SIZE(acx_ioctl_private_args);
++ if (copy_to_user(iwr->u.data.pointer, acx_ioctl_private_args, sizeof(acx_ioctl_private_args)) != 0)
++ result = -EFAULT;
++
++ return result;
++}
++#endif
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_nick
++*----------------------------------------------------------------*/
++static inline int
++acx_ioctl_get_nick(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ /* FIXME : consider spinlock here */
++ strcpy(extra, priv->nick);
++ /* FIXME : consider spinlock here */
++
++ dwrq->length = strlen(extra) + 1;
++
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_nick
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_nick(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *dwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ if (dwrq->length > IW_ESSID_MAX_SIZE + 1) {
++ result = -E2BIG;
++ goto end_unlock;
++ }
++
++ /* extra includes trailing \0, so it's ok */
++ strcpy(priv->nick, extra);
++ result = OK;
++
++end_unlock:
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*------------------------------------------------------------------------------
++ * acx_ioctl_get_retry
++ *----------------------------------------------------------------------------*/
++static int
++acx_ioctl_get_retry(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned int type = vwrq->flags & IW_RETRY_TYPE;
++ unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER;
++ int result;
++
++ acx_sem_lock(priv);
++
++ /* return the short retry number by default */
++ if (type == IW_RETRY_LIFETIME) {
++ vwrq->flags = IW_RETRY_LIFETIME;
++ vwrq->value = priv->msdu_lifetime;
++ } else if (modifier == IW_RETRY_MAX) {
++ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
++ vwrq->value = priv->long_retry;
++ } else {
++ vwrq->flags = IW_RETRY_LIMIT;
++ if (priv->long_retry != priv->short_retry)
++ SET_BIT(vwrq->flags, IW_RETRY_MIN);
++ vwrq->value = priv->short_retry;
++ }
++
++ /* can't be disabled */
++ vwrq->disabled = (u8)0;
++ result = OK;
++
++ acx_sem_unlock(priv);
++
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_retry
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_retry(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ if (!vwrq) {
++ result = -EFAULT;
++ goto end;
++ }
++ if (vwrq->disabled) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(priv);
++
++ result = -EINVAL;
++ if (IW_RETRY_LIMIT == (vwrq->flags & IW_RETRY_TYPE)) {
++ printk("old retry limits: short %d long %d\n",
++ priv->short_retry, priv->long_retry);
++ if (vwrq->flags & IW_RETRY_MAX) {
++ priv->long_retry = vwrq->value;
++ } else if (vwrq->flags & IW_RETRY_MIN) {
++ priv->short_retry = vwrq->value;
++ } else {
++ /* no modifier: set both */
++ priv->long_retry = vwrq->value;
++ priv->short_retry = vwrq->value;
++ }
++ printk("new retry limits: short %d long %d\n",
++ priv->short_retry, priv->long_retry);
++ SET_BIT(priv->set_mask, GETSET_RETRY);
++ result = -EINPROGRESS;
++ }
++ else if (vwrq->flags & IW_RETRY_LIFETIME) {
++ priv->msdu_lifetime = vwrq->value;
++ printk("new MSDU lifetime: %d\n", priv->msdu_lifetime);
++ SET_BIT(priv->set_mask, SET_MSDU_LIFETIME);
++ result = -EINPROGRESS;
++ }
++
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/******************************* private ioctls ******************************/
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_debug
++*----------------------------------------------------------------*/
++#if ACX_DEBUG
++static int
++acx_ioctl_set_debug(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ unsigned int debug_new = *((unsigned int *)extra);
++ int result = -EINVAL;
++
++ acxlog(L_ANY, "setting debug from %04X to %04X\n", acx_debug, debug_new);
++ acx_debug = debug_new;
++
++ result = OK;
++ return result;
++
++}
++#endif
++
++/*----------------------------------------------------------------
++* acx_ioctl_list_reg_domain
++*----------------------------------------------------------------*/
++static const char * const
++reg_domain_strings[] = {
++ " 1-11 FCC (USA)",
++ " 1-11 DOC/IC (Canada)",
++ /* BTW: WLAN use in ETSI is regulated by
++ * ETSI standard EN 300 328-2 V1.1.2 */
++ " 1-13 ETSI (Europe)",
++ "10-11 Spain",
++ "10-13 France",
++ " 14 MKK (Japan)",
++ " 1-14 MKK1",
++ " 3-9 Israel (not all firmware versions)",
++ NULL /* needs to remain as last entry */
++};
++
++static int
++acx_ioctl_list_reg_domain(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++
++ int i = 1;
++ const char * const *entry = reg_domain_strings;
++
++ printk("dom# chan# domain/country\n");
++ while (*entry)
++ printk("%4d %s\n", i++, *entry++);
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_reg_domain
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_reg_domain(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ if ((*extra < 1) || ((size_t)*extra > reg_domain_ids_len)) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(priv);
++
++ priv->reg_dom_id = reg_domain_ids[*extra - 1];
++ SET_BIT(priv->set_mask, GETSET_REG_DOMAIN);
++
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_reg_domain
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_reg_domain(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int dom,i;
++
++ /* no locking */
++ dom = priv->reg_dom_id;
++
++ for (i=1; i <= 7; i++) {
++ if (reg_domain_ids[i-1] == dom) {
++ acxlog(L_IOCTL, "regulatory domain is currently set "
++ "to %d (0x%X): %s\n", i, dom,
++ reg_domain_strings[i-1]);
++ *extra = i;
++ break;
++ }
++ }
++
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_short_preamble
++*----------------------------------------------------------------*/
++static const char * const
++preamble_modes[] = {
++ "off",
++ "on",
++ "auto (peer capability dependent)",
++ "unknown mode, error"
++};
++
++static int
++acx_ioctl_set_short_preamble(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int i;
++ int result;
++
++ FN_ENTER;
++
++ if ((unsigned char)*extra > 2) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(priv);
++
++ priv->preamble_mode = (u8)*extra;
++ switch (priv->preamble_mode) {
++ case 0: /* long */
++ priv->preamble_cur = 0;
++ break;
++ case 1:
++ /* short, kick incapable peers */
++ priv->preamble_cur = 1;
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ client_t *clt = &priv->sta_list[i];
++ if (!clt->used) continue;
++ if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) {
++ clt->used = CLIENT_EMPTY_SLOT_0;
++ }
++ }
++ switch (priv->mode) {
++ case ACX_MODE_2_STA:
++ if (priv->ap_client && !priv->ap_client->used) {
++ /* We kicked our AP :) */
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++ }
++ }
++ break;
++ case 2: /* auto. short only if all peers are short-capable */
++ priv->preamble_cur = 1;
++ for (i = 0; i < VEC_SIZE(priv->sta_list); i++) {
++ client_t *clt = &priv->sta_list[i];
++ if (!clt->used) continue;
++ if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) {
++ priv->preamble_cur = 0;
++ break;
++ }
++ }
++ break;
++ }
++ printk("new short preamble setting: configured %s, active %s\n",
++ preamble_modes[priv->preamble_mode],
++ preamble_modes[priv->preamble_cur]);
++ result = OK;
++
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_short_preamble
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_short_preamble(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ printk("current short preamble setting: configured %s, active %s\n",
++ preamble_modes[priv->preamble_mode],
++ preamble_modes[priv->preamble_cur]);
++
++ *extra = (char)priv->preamble_mode;
++
++ acx_sem_unlock(priv);
++
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_antenna
++*
++* Comment: TX and RX antenna can be set separately but this function good
++* for testing 0-4 bits
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_antenna(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ printk("old antenna value: 0x%02X (COMBINED bit mask)\n"
++ "Rx antenna selection:\n"
++ "0x00 ant. 1\n"
++ "0x40 ant. 2\n"
++ "0x80 full diversity\n"
++ "0xc0 partial diversity\n"
++ "0x0f dwell time mask (in units of us)\n"
++ "Tx antenna selection:\n"
++ "0x00 ant. 2\n" /* yep, those ARE reversed! */
++ "0x20 ant. 1\n"
++ "new antenna value: 0x%02X\n",
++ priv->antenna, (u8)*extra);
++
++ priv->antenna = (u8)*extra;
++ SET_BIT(priv->set_mask, GETSET_ANTENNA);
++
++ acx_sem_unlock(priv);
++
++ return -EINPROGRESS;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_antenna
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_antenna(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ /* no locking. it's pointless to lock a single load */
++ printk("current antenna value: 0x%02X (COMBINED bit mask)\n"
++ "Rx antenna selection:\n"
++ "0x00 ant. 1\n"
++ "0x40 ant. 2\n"
++ "0x80 full diversity\n"
++ "0xc0 partial diversity\n"
++ "Tx antenna selection:\n"
++ "0x00 ant. 2\n" /* yep, those ARE reversed! */
++ "0x20 ant. 1\n", priv->antenna);
++
++ return 0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_rx_antenna
++*
++*
++* Arguments:
++* 0 = antenna1; 1 = antenna2; 2 = full diversity; 3 = partial diversity
++* Comment: Could anybody test which antenna is the external one
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_rx_antenna(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ if (*extra > 3) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ printk("old antenna value: 0x%02X\n", priv->antenna);
++
++ acx_sem_lock(priv);
++
++ priv->antenna &= 0x3f;
++ SET_BIT(priv->antenna, (*extra << 6));
++ SET_BIT(priv->set_mask, GETSET_ANTENNA);
++ printk("new antenna value: 0x%02X\n", priv->antenna);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_tx_antenna
++*
++*
++* Arguments: 0 == antenna2; 1 == antenna1;
++* Comment: Could anybody test which antenna is the external one
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_set_tx_antenna(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ FN_ENTER;
++
++ if (*extra > 1) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ printk("old antenna value: 0x%02X\n", priv->antenna);
++
++ acx_sem_lock(priv);
++
++ priv->antenna &= ~0x30;
++ SET_BIT(priv->antenna, ((*extra & 0x01) << 5));
++ SET_BIT(priv->set_mask, GETSET_ANTENNA);
++ printk("new antenna value: 0x%02X\n", priv->antenna);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_wlansniff
++*
++* can we just remove this in favor of monitor mode? --vda
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_wlansniff(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned int *params = (unsigned int*)extra;
++ unsigned int enable = (unsigned int)(params[0] > 0);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ /* not using printk() here, since it distorts kismet display
++ * when printk messages activated */
++ acxlog(L_IOCTL, "setting monitor to: 0x%02X\n", params[0]);
++
++ switch (params[0]) {
++ case 0:
++ priv->netdev->type = ARPHRD_ETHER;
++ break;
++ case 1:
++ priv->netdev->type = ARPHRD_IEEE80211_PRISM;
++ break;
++ case 2:
++ priv->netdev->type = ARPHRD_IEEE80211;
++ break;
++ }
++
++ if (params[0]) {
++ priv->mode = ACX_MODE_MONITOR;
++ SET_BIT(priv->set_mask, GETSET_MODE);
++ }
++
++ if (enable) {
++ priv->channel = params[1];
++ SET_BIT(priv->set_mask, GETSET_RX);
++ }
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_unknown11
++* FIXME: looks like some sort of "iwpriv kick_sta MAC" but it's broken
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_unknown11(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++#ifdef BROKEN
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ client_t client;
++ int result;
++
++ acx_sem_lock(priv);
++ acx_lock(priv, flags);
++
++ acx_l_transmit_disassoc(priv, &client);
++ result = OK;
++
++ acx_unlock(priv, flags);
++ acx_sem_unlock(priv);
++
++ return result;
++#endif
++ return -EINVAL;
++}
++
++
++/***********************************************************************
++** debug helper function to be able to debug various issues relatively easily
++*/
++static int
++acx_ioctl_dbg_set_masks(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ const unsigned int *params = (unsigned int*)extra;
++ int result;
++
++ acx_sem_lock(priv);
++
++ acxlog(L_IOCTL, "setting flags in settings mask: "
++ "get_mask %08X set_mask %08X\n"
++ "before: get_mask %08X set_mask %08X\n",
++ params[0], params[1],
++ priv->get_mask, priv->set_mask);
++ SET_BIT(priv->get_mask, params[0]);
++ SET_BIT(priv->set_mask, params[1]);
++ acxlog(L_IOCTL, "after: get_mask %08X set_mask %08X\n",
++ priv->get_mask, priv->set_mask);
++ result = -EINPROGRESS; /* immediately call commit handler */
++
++ acx_sem_unlock(priv);
++
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_rates
++*
++* This ioctl takes string parameter. Examples:
++* iwpriv wlan0 SetRates "1,2"
++* use 1 and 2 Mbit rates, both are in basic rate set
++* iwpriv wlan0 SetRates "1,2 5,11"
++* use 1,2,5.5,11 Mbit rates. 1 and 2 are basic
++* iwpriv wlan0 SetRates "1,2 5c,11c"
++* same ('c' means 'CCK modulation' and it is a default for 5 and 11)
++* iwpriv wlan0 SetRates "1,2 5p,11p"
++* use 1,2,5.5,11 Mbit, 1,2 are basic. 5 and 11 are using PBCC
++* iwpriv wlan0 SetRates "1,2,5,11 22p"
++* use 1,2,5.5,11,22 Mbit. 1,2,5.5 and 11 are basic. 22 is using PBCC
++* (this is the maximum acx100 can do (modulo x4 mode))
++* iwpriv wlan0 SetRates "1,2,5,11 22"
++* same. 802.11 defines only PBCC modulation
++* for 22 and 33 Mbit rates, so there is no ambiguity
++* iwpriv wlan0 SetRates "1,2,5,11 6o,9o,12o,18o,24o,36o,48o,54o"
++* 1,2,5.5 and 11 are basic. 11g OFDM rates are enabled but
++* they are not in basic rate set. 22 Mbit is disabled.
++* iwpriv wlan0 SetRates "1,2,5,11 6,9,12,18,24,36,48,54"
++* same. OFDM is default for 11g rates except 22 and 33 Mbit,
++* thus 'o' is optional
++* iwpriv wlan0 SetRates "1,2,5,11 6d,9d,12d,18d,24d,36d,48d,54d"
++* 1,2,5.5 and 11 are basic. 11g CCK-OFDM rates are enabled
++* (acx111 does not support CCK-OFDM, driver will reject this cmd)
++* iwpriv wlan0 SetRates "6,9,12 18,24,36,48,54"
++* 6,9,12 are basic, rest of 11g rates is enabled. Using OFDM
++*----------------------------------------------------------------*/
++#include "setrate.c"
++
++/* disallow: 33Mbit (unsupported by hw) */
++/* disallow: CCKOFDM (unsupported by hw) */
++static int
++acx111_supported(int mbit, int modulation, void *opaque)
++{
++ if (mbit==33) return -ENOTSUPP;
++ if (modulation==DOT11_MOD_CCKOFDM) return -ENOTSUPP;
++ return OK;
++}
++
++static const u16
++acx111mask[] = {
++ [DOT11_RATE_1 ] = RATE111_1 ,
++ [DOT11_RATE_2 ] = RATE111_2 ,
++ [DOT11_RATE_5 ] = RATE111_5 ,
++ [DOT11_RATE_11] = RATE111_11,
++ [DOT11_RATE_22] = RATE111_22,
++ /* [DOT11_RATE_33] = */
++ [DOT11_RATE_6 ] = RATE111_6 ,
++ [DOT11_RATE_9 ] = RATE111_9 ,
++ [DOT11_RATE_12] = RATE111_12,
++ [DOT11_RATE_18] = RATE111_18,
++ [DOT11_RATE_24] = RATE111_24,
++ [DOT11_RATE_36] = RATE111_36,
++ [DOT11_RATE_48] = RATE111_48,
++ [DOT11_RATE_54] = RATE111_54,
++};
++
++static u32
++acx111_gen_mask(int mbit, int modulation, void *opaque)
++{
++ /* lower 16 bits show selected 1, 2, CCK and OFDM rates */
++ /* upper 16 bits show selected PBCC rates */
++ u32 m = acx111mask[rate_mbit2enum(mbit)];
++ if (modulation==DOT11_MOD_PBCC)
++ return m<<16;
++ return m;
++}
++
++static int
++verify_rate(u32 rate, int chip_type)
++{
++ /* never happens. be paranoid */
++ if (!rate) return -EINVAL;
++
++ /* disallow: mixing PBCC and CCK at 5 and 11Mbit
++ ** (can be supported, but needs complicated handling in tx code) */
++ if (( rate & ((RATE111_11+RATE111_5)<<16) )
++ && ( rate & (RATE111_11+RATE111_5) )
++ ) {
++ return -ENOTSUPP;
++ }
++ if (CHIPTYPE_ACX100 == chip_type) {
++ if ( rate & ~(RATE111_ACX100_COMPAT+(RATE111_ACX100_COMPAT<<16)) )
++ return -ENOTSUPP;
++ }
++ return 0;
++}
++
++static int
++acx_ioctl_set_rates(struct net_device *dev, struct iw_request_info *info,
++ struct iw_param *vwrq, char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ int result;
++ u32 brate = 0, orate = 0; /* basic, operational rate set */
++
++ FN_ENTER;
++
++ acxlog(L_IOCTL, "set_rates %s\n", extra);
++ result = fill_ratemasks(extra, &brate, &orate,
++ acx111_supported, acx111_gen_mask, 0);
++ if (result) goto end;
++ SET_BIT(orate, brate);
++ acxlog(L_IOCTL, "brate %08X orate %08X\n", brate, orate);
++
++ result = verify_rate(brate, priv->chip_type);
++ if (result) goto end;
++ result = verify_rate(orate, priv->chip_type);
++ if (result) goto end;
++
++ acx_sem_lock(priv);
++ acx_lock(priv, flags);
++
++ priv->rate_basic = brate;
++ priv->rate_oper = orate;
++ /* TODO: ideally, we shall monitor highest basic rate
++ ** which was successfully sent to every peer
++ ** (say, last we checked, everybody could hear 5.5 Mbits)
++ ** and use that for bcasts when we want to reach all peers.
++ ** For beacons, we probably shall use lowest basic rate
++ ** because we want to reach all *potential* new peers too */
++ priv->rate_bcast = 1 << lowest_bit(brate);
++ if (IS_ACX100(priv))
++ priv->rate_bcast100 = acx_rate111to100(priv->rate_bcast);
++ priv->rate_auto = !has_only_one_bit(orate);
++ acx_l_update_client_rates(priv, orate);
++ /* TODO: get rid of ratevector, build it only when needed */
++ acx_l_update_ratevector(priv);
++
++ /* Do/don't do tx rate fallback; beacon contents and rate */
++ SET_BIT(priv->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES);
++ result = -EINPROGRESS;
++
++ acx_unlock(priv, flags);
++ acx_sem_unlock(priv);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_get_phy_chan_busy_percentage
++*----------------------------------------------------------------*/
++static int
++acx_ioctl_get_phy_chan_busy_percentage(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ struct { /* added ACX_PACKED, not tested --vda */
++ u16 type ACX_PACKED;
++ u16 len ACX_PACKED;
++ u32 busytime ACX_PACKED;
++ u32 totaltime ACX_PACKED;
++ } usage;
++
++ acx_sem_lock(priv);
++
++ if (OK != acx_s_interrogate(priv, &usage, ACX1xx_IE_MEDIUM_USAGE))
++ goto bad_unlock;
++
++ printk("%s: average busy percentage since last invocation: %d%% "
++ "(microseconds: %u of %u)\n",
++ dev->name,
++ 100 * (le32_to_cpu(usage.busytime) / 100) / (le32_to_cpu(usage.totaltime) / 100),
++ le32_to_cpu(usage.busytime), le32_to_cpu(usage.totaltime));
++ /* prevent calculation overflow */
++
++ acx_sem_unlock(priv);
++
++ return OK;
++
++bad_unlock:
++ acx_sem_unlock(priv);
++
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_ed_threshold
++*----------------------------------------------------------------*/
++static inline int
++acx_ioctl_set_ed_threshold(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ printk("old ED threshold value: %d\n", priv->ed_threshold);
++ priv->ed_threshold = (unsigned char)*extra;
++ printk("new ED threshold value: %d\n", (unsigned char)*extra);
++ SET_BIT(priv->set_mask, GETSET_ED_THRESH);
++
++ acx_sem_unlock(priv);
++
++ return -EINPROGRESS;
++}
++
++
++/*----------------------------------------------------------------
++* acx_ioctl_set_cca
++*----------------------------------------------------------------*/
++static inline int
++acx_ioctl_set_cca(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ acx_sem_lock(priv);
++
++ printk("old CCA value: 0x%02X\n", priv->cca);
++ priv->cca = (unsigned char)*extra;
++ printk("new CCA value: 0x%02X\n", (unsigned char)*extra);
++ SET_BIT(priv->set_mask, GETSET_CCA);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static const char * const
++scan_modes[] = { "active", "passive", "background" };
++
++static void
++acx_print_scan_params(wlandevice_t *priv, const char* head)
++{
++ printk("%s: %smode %d (%s), min chan time %dTU, "
++ "max chan time %dTU, max scan rate byte: %d\n",
++ priv->netdev->name, head,
++ priv->scan_mode, scan_modes[priv->scan_mode],
++ priv->scan_probe_delay, priv->scan_duration, priv->scan_rate);
++}
++
++static int
++acx_ioctl_set_scan_params(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++ const int *params = (int *)extra;
++
++ acx_sem_lock(priv);
++
++ acx_print_scan_params(priv, "old scan parameters: ");
++ if ((params[0] != -1) && (params[0] >= 0) && (params[0] <= 2))
++ priv->scan_mode = params[0];
++ if (params[1] != -1)
++ priv->scan_probe_delay = params[1];
++ if (params[2] != -1)
++ priv->scan_duration = params[2];
++ if ((params[3] != -1) && (params[3] <= 255))
++ priv->scan_rate = params[3];
++ acx_print_scan_params(priv, "new scan parameters: ");
++ SET_BIT(priv->set_mask, GETSET_RESCAN);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++
++ return result;
++}
++
++static int
++acx_ioctl_get_scan_params(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++ int *params = (int *)extra;
++
++ acx_sem_lock(priv);
++
++ acx_print_scan_params(priv, "current scan parameters: ");
++ params[0] = priv->scan_mode;
++ params[1] = priv->scan_probe_delay;
++ params[2] = priv->scan_duration;
++ params[3] = priv->scan_rate;
++ result = OK;
++
++ acx_sem_unlock(priv);
++
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx100_ioctl_set_led_power(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ static const char * const led_modes[] = { "off", "on", "LinkQuality" };
++
++ wlandevice_t *priv = netdev_priv(dev);
++ int result;
++
++ acx_sem_lock(priv);
++
++ printk("%s: power LED status: old %d (%s), ",
++ dev->name,
++ priv->led_power,
++ led_modes[priv->led_power]);
++ priv->led_power = extra[0];
++ if (priv->led_power > 2) priv->led_power = 2;
++ printk("new %d (%s)\n",
++ priv->led_power,
++ led_modes[priv->led_power]);
++
++ if (priv->led_power == 2) {
++ printk("%s: max link quality setting: old %d, ",
++ dev->name, priv->brange_max_quality);
++ if (extra[1])
++ priv->brange_max_quality = extra[1];
++ printk("new %d\n", priv->brange_max_quality);
++ }
++
++ SET_BIT(priv->set_mask, GETSET_LED_POWER);
++
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(priv);
++
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static inline int
++acx100_ioctl_get_led_power(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ extra[0] = priv->led_power;
++ if (priv->led_power == 2)
++ extra[1] = priv->brange_max_quality;
++ else
++ extra[1] = -1;
++
++ acx_sem_unlock(priv);
++
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx111_ioctl_info(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ if (!IS_PCI((wlandevice_t*)netdev_priv(dev)))
++ return OK;
++ return acx111pci_ioctl_info(dev, info, vwrq, extra);
++}
++
++
++/***********************************************************************
++*/
++static int
++acx100_ioctl_set_phy_amp_bias(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ if (!IS_PCI((wlandevice_t*)netdev_priv(dev))) {
++ printk("acx: set_phy_amp_bias() is not supported on USB\n");
++ return OK;
++ }
++ return acx100pci_ioctl_set_phy_amp_bias(dev, info, vwrq, extra);
++}
++
++
++/***********************************************************************
++*/
++#if WIRELESS_EXT >= 13
++static const iw_handler acx_ioctl_handler[] =
++{
++ (iw_handler) acx_ioctl_commit, /* SIOCSIWCOMMIT */
++ (iw_handler) acx_ioctl_get_name, /* SIOCGIWNAME */
++ (iw_handler) NULL, /* SIOCSIWNWID */
++ (iw_handler) NULL, /* SIOCGIWNWID */
++ (iw_handler) acx_ioctl_set_freq, /* SIOCSIWFREQ */
++ (iw_handler) acx_ioctl_get_freq, /* SIOCGIWFREQ */
++ (iw_handler) acx_ioctl_set_mode, /* SIOCSIWMODE */
++ (iw_handler) acx_ioctl_get_mode, /* SIOCGIWMODE */
++ (iw_handler) acx_ioctl_set_sens, /* SIOCSIWSENS */
++ (iw_handler) acx_ioctl_get_sens, /* SIOCGIWSENS */
++ (iw_handler) NULL, /* SIOCSIWRANGE */
++ (iw_handler) acx_ioctl_get_range, /* SIOCGIWRANGE */
++ (iw_handler) NULL, /* SIOCSIWPRIV */
++ (iw_handler) NULL, /* SIOCGIWPRIV */
++ (iw_handler) NULL, /* SIOCSIWSTATS */
++ (iw_handler) NULL, /* SIOCGIWSTATS */
++#if IW_HANDLER_VERSION > 4
++ iw_handler_set_spy, /* SIOCSIWSPY */
++ iw_handler_get_spy, /* SIOCGIWSPY */
++ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
++ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
++#else /* IW_HANDLER_VERSION > 4 */
++#ifdef WIRELESS_SPY
++ (iw_handler) NULL /* acx_ioctl_set_spy FIXME */, /* SIOCSIWSPY */
++ (iw_handler) NULL /* acx_ioctl_get_spy FIXME */, /* SIOCGIWSPY */
++#else /* WSPY */
++ (iw_handler) NULL, /* SIOCSIWSPY */
++ (iw_handler) NULL, /* SIOCGIWSPY */
++#endif /* WSPY */
++ (iw_handler) NULL, /* [nothing] */
++ (iw_handler) NULL, /* [nothing] */
++#endif /* IW_HANDLER_VERSION > 4 */
++ (iw_handler) acx_ioctl_set_ap, /* SIOCSIWAP */
++ (iw_handler) acx_ioctl_get_ap, /* SIOCGIWAP */
++ (iw_handler) NULL, /* [nothing] */
++ (iw_handler) acx_ioctl_get_aplist, /* SIOCGIWAPLIST */
++#if WIRELESS_EXT > 13
++ (iw_handler) acx_ioctl_set_scan, /* SIOCSIWSCAN */
++ (iw_handler) acx_ioctl_get_scan, /* SIOCGIWSCAN */
++#else /* WE > 13 */
++ (iw_handler) NULL, /* SIOCSIWSCAN */
++ (iw_handler) NULL, /* SIOCGIWSCAN */
++#endif /* WE > 13 */
++ (iw_handler) acx_ioctl_set_essid, /* SIOCSIWESSID */
++ (iw_handler) acx_ioctl_get_essid, /* SIOCGIWESSID */
++ (iw_handler) acx_ioctl_set_nick, /* SIOCSIWNICKN */
++ (iw_handler) acx_ioctl_get_nick, /* SIOCGIWNICKN */
++ (iw_handler) NULL, /* [nothing] */
++ (iw_handler) NULL, /* [nothing] */
++ (iw_handler) acx_ioctl_set_rate, /* SIOCSIWRATE */
++ (iw_handler) acx_ioctl_get_rate, /* SIOCGIWRATE */
++ (iw_handler) acx_ioctl_set_rts, /* SIOCSIWRTS */
++ (iw_handler) acx_ioctl_get_rts, /* SIOCGIWRTS */
++ (iw_handler) NULL /* acx_ioctl_set_frag FIXME */, /* SIOCSIWFRAG */
++ (iw_handler) NULL /* acx_ioctl_get_frag FIXME */, /* SIOCGIWFRAG */
++ (iw_handler) acx_ioctl_set_txpow, /* SIOCSIWTXPOW */
++ (iw_handler) acx_ioctl_get_txpow, /* SIOCGIWTXPOW */
++ (iw_handler) acx_ioctl_set_retry, /* SIOCSIWRETRY */
++ (iw_handler) acx_ioctl_get_retry, /* SIOCGIWRETRY */
++ (iw_handler) acx_ioctl_set_encode, /* SIOCSIWENCODE */
++ (iw_handler) acx_ioctl_get_encode, /* SIOCGIWENCODE */
++ (iw_handler) acx_ioctl_set_power, /* SIOCSIWPOWER */
++ (iw_handler) acx_ioctl_get_power, /* SIOCGIWPOWER */
++};
++
++static const iw_handler acx_ioctl_private_handler[] =
++{
++#if ACX_DEBUG
++[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_debug,
++#else
++[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = (iw_handler) NULL,
++#endif
++[ACX100_IOCTL_SET_PLED - ACX100_IOCTL] = (iw_handler) acx100_ioctl_set_led_power,
++[ACX100_IOCTL_GET_PLED - ACX100_IOCTL] = (iw_handler) acx100_ioctl_get_led_power,
++[ACX100_IOCTL_SET_RATES - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_rates,
++[ACX100_IOCTL_LIST_DOM - ACX100_IOCTL] = (iw_handler) acx_ioctl_list_reg_domain,
++[ACX100_IOCTL_SET_DOM - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_reg_domain,
++[ACX100_IOCTL_GET_DOM - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_reg_domain,
++[ACX100_IOCTL_SET_SCAN_PARAMS - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_scan_params,
++[ACX100_IOCTL_GET_SCAN_PARAMS - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_scan_params,
++[ACX100_IOCTL_SET_PREAMB - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_short_preamble,
++[ACX100_IOCTL_GET_PREAMB - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_short_preamble,
++[ACX100_IOCTL_SET_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_antenna,
++[ACX100_IOCTL_GET_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_antenna,
++[ACX100_IOCTL_RX_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_rx_antenna,
++[ACX100_IOCTL_TX_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_tx_antenna,
++[ACX100_IOCTL_SET_PHY_AMP_BIAS - ACX100_IOCTL] = (iw_handler) acx100_ioctl_set_phy_amp_bias,
++[ACX100_IOCTL_GET_PHY_CHAN_BUSY - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_phy_chan_busy_percentage,
++[ACX100_IOCTL_SET_ED - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_ed_threshold,
++[ACX100_IOCTL_SET_CCA - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_cca,
++[ACX100_IOCTL_MONITOR - ACX100_IOCTL] = (iw_handler) acx_ioctl_wlansniff,
++[ACX100_IOCTL_TEST - ACX100_IOCTL] = (iw_handler) acx_ioctl_unknown11,
++[ACX100_IOCTL_DBG_SET_MASKS - ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_set_masks,
++[ACX111_IOCTL_INFO - ACX100_IOCTL] = (iw_handler) acx111_ioctl_info,
++};
++
++const struct iw_handler_def acx_ioctl_handler_def =
++{
++ .num_standard = VEC_SIZE(acx_ioctl_handler),
++ .num_private = VEC_SIZE(acx_ioctl_private_handler),
++ .num_private_args = VEC_SIZE(acx_ioctl_private_args),
++ .standard = (iw_handler *) acx_ioctl_handler,
++ .private = (iw_handler *) acx_ioctl_private_handler,
++ .private_args = (struct iw_priv_args *) acx_ioctl_private_args,
++};
++
++#endif /* WE >= 13 */
++
++
++#if WIRELESS_EXT < 13
++/*================================================================*/
++/* Main function */
++/*================================================================*/
++/*----------------------------------------------------------------
++* acx_e_ioctl_old
++*
++* Comment:
++* This is the *OLD* ioctl handler.
++* Make sure to not only place your additions here, but instead mainly
++* in the new one (acx_ioctl_handler[])!
++*----------------------------------------------------------------*/
++int
++acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result = 0;
++ struct iwreq *iwr = (struct iwreq *)ifr;
++
++ acxlog(L_IOCTL, "%s cmd = 0x%04X\n", __func__, cmd);
++
++ /* This is the way it is done in the orinoco driver.
++ * Check to see if device is present.
++ */
++ if (0 == netif_device_present(dev)) {
++ return -ENODEV;
++ }
++
++ switch (cmd) {
++/* WE 13 and higher will use acx_ioctl_handler_def */
++ case SIOCGIWNAME:
++ /* get name == wireless protocol */
++ result = acx_ioctl_get_name(dev, NULL,
++ (char *)&(iwr->u.name), NULL);
++ break;
++
++ case SIOCSIWNWID: /* pre-802.11, */
++ case SIOCGIWNWID: /* not supported. */
++ result = -EOPNOTSUPP;
++ break;
++
++ case SIOCSIWFREQ:
++ /* set channel/frequency (Hz)
++ data can be frequency or channel :
++ 0-1000 = channel
++ > 1000 = frequency in Hz */
++ result = acx_ioctl_set_freq(dev, NULL, &(iwr->u.freq), NULL);
++ break;
++
++ case SIOCGIWFREQ:
++ /* get channel/frequency (Hz) */
++ result = acx_ioctl_get_freq(dev, NULL, &(iwr->u.freq), NULL);
++ break;
++
++ case SIOCSIWMODE:
++ /* set operation mode */
++ result = acx_ioctl_set_mode(dev, NULL, &(iwr->u.mode), NULL);
++ break;
++
++ case SIOCGIWMODE:
++ /* get operation mode */
++ result = acx_ioctl_get_mode(dev, NULL, &(iwr->u.mode), NULL);
++ break;
++
++ case SIOCSIWSENS:
++ /* Set sensitivity */
++ result = acx_ioctl_set_sens(dev, NULL, &(iwr->u.sens), NULL);
++ break;
++
++ case SIOCGIWSENS:
++ /* Get sensitivity */
++ result = acx_ioctl_get_sens(dev, NULL, &(iwr->u.sens), NULL);
++ break;
++
++#if WIRELESS_EXT > 10
++ case SIOCGIWRANGE:
++ /* Get range of parameters */
++ {
++ struct iw_range range;
++ result = acx_ioctl_get_range(dev, NULL,
++ &(iwr->u.data), (char *)&range);
++ if (copy_to_user(iwr->u.data.pointer, &range,
++ sizeof(struct iw_range)))
++ result = -EFAULT;
++ }
++ break;
++#endif
++
++ case SIOCGIWPRIV:
++ result = acx_ioctl_get_iw_priv(iwr);
++ break;
++
++ /* FIXME: */
++ /* case SIOCSIWSPY: */
++ /* case SIOCGIWSPY: */
++ /* case SIOCSIWTHRSPY: */
++ /* case SIOCGIWTHRSPY: */
++
++ case SIOCSIWAP:
++ /* set access point by MAC address */
++ result = acx_ioctl_set_ap(dev, NULL, &(iwr->u.ap_addr),
++ NULL);
++ break;
++
++ case SIOCGIWAP:
++ /* get access point MAC address */
++ result = acx_ioctl_get_ap(dev, NULL, &(iwr->u.ap_addr),
++ NULL);
++ break;
++
++ case SIOCGIWAPLIST:
++ /* get list of access points in range */
++ result = acx_ioctl_get_aplist(dev, NULL, &(iwr->u.data),
++ NULL);
++ break;
++
++#if NOT_FINISHED_YET
++ /* FIXME: do proper interfacing to activate that! */
++ case SIOCSIWSCAN:
++ /* start a station scan */
++ result = acx_ioctl_set_scan(iwr, priv);
++ break;
++
++ case SIOCGIWSCAN:
++ /* get list of stations found during scan */
++ result = acx_ioctl_get_scan(iwr, priv);
++ break;
++#endif
++
++ case SIOCSIWESSID:
++ /* set ESSID (network name) */
++ {
++ char essid[IW_ESSID_MAX_SIZE+1];
++
++ if (iwr->u.essid.length > IW_ESSID_MAX_SIZE)
++ {
++ result = -E2BIG;
++ break;
++ }
++ if (copy_from_user(essid, iwr->u.essid.pointer,
++ iwr->u.essid.length))
++ {
++ result = -EFAULT;
++ break;
++ }
++ result = acx_ioctl_set_essid(dev, NULL,
++ &(iwr->u.essid), essid);
++ }
++ break;
++
++ case SIOCGIWESSID:
++ /* get ESSID */
++ {
++ char essid[IW_ESSID_MAX_SIZE+1];
++ if (iwr->u.essid.pointer)
++ result = acx_ioctl_get_essid(dev, NULL,
++ &(iwr->u.essid), essid);
++ if (copy_to_user(iwr->u.essid.pointer, essid,
++ iwr->u.essid.length))
++ result = -EFAULT;
++ }
++ break;
++
++ case SIOCSIWNICKN:
++ /* set nick */
++ {
++ char nick[IW_ESSID_MAX_SIZE+1];
++
++ if (iwr->u.data.length > IW_ESSID_MAX_SIZE)
++ {
++ result = -E2BIG;
++ break;
++ }
++ if (copy_from_user(nick, iwr->u.data.pointer,
++ iwr->u.data.length))
++ {
++ result = -EFAULT;
++ break;
++ }
++ result = acx_ioctl_set_nick(dev, NULL,
++ &(iwr->u.data), nick);
++ }
++ break;
++
++ case SIOCGIWNICKN:
++ /* get nick */
++ {
++ char nick[IW_ESSID_MAX_SIZE+1];
++ if (iwr->u.data.pointer)
++ result = acx_ioctl_get_nick(dev, NULL,
++ &(iwr->u.data), nick);
++ if (copy_to_user(iwr->u.data.pointer, nick,
++ iwr->u.data.length))
++ result = -EFAULT;
++ }
++ break;
++
++ case SIOCSIWRATE:
++ /* set default bit rate (bps) */
++ result = acx_ioctl_set_rate(dev, NULL, &(iwr->u.bitrate),
++ NULL);
++ break;
++
++ case SIOCGIWRATE:
++ /* get default bit rate (bps) */
++ result = acx_ioctl_get_rate(dev, NULL, &(iwr->u.bitrate),
++ NULL);
++ break;
++
++ case SIOCSIWRTS:
++ /* set RTS threshold value */
++ result = acx_ioctl_set_rts(dev, NULL, &(iwr->u.rts), NULL);
++ break;
++ case SIOCGIWRTS:
++ /* get RTS threshold value */
++ result = acx_ioctl_get_rts(dev, NULL, &(iwr->u.rts), NULL);
++ break;
++
++ /* FIXME: */
++ /* case SIOCSIWFRAG: */
++ /* case SIOCGIWFRAG: */
++
++#if WIRELESS_EXT > 9
++ case SIOCGIWTXPOW:
++ /* get tx power */
++ result = acx_ioctl_get_txpow(dev, NULL, &(iwr->u.txpower),
++ NULL);
++ break;
++
++ case SIOCSIWTXPOW:
++ /* set tx power */
++ result = acx_ioctl_set_txpow(dev, NULL, &(iwr->u.txpower),
++ NULL);
++ break;
++#endif
++
++ case SIOCSIWRETRY:
++ result = acx_ioctl_set_retry(dev, NULL, &(iwr->u.retry), NULL);
++ break;
++
++ case SIOCGIWRETRY:
++ result = acx_ioctl_get_retry(dev, NULL, &(iwr->u.retry), NULL);
++ break;
++
++ case SIOCSIWENCODE:
++ {
++ /* set encoding token & mode */
++ u8 key[29];
++ if (iwr->u.encoding.pointer) {
++ if (iwr->u.encoding.length > 29) {
++ result = -E2BIG;
++ break;
++ }
++ if (copy_from_user(key, iwr->u.encoding.pointer,
++ iwr->u.encoding.length)) {
++ result = -EFAULT;
++ break;
++ }
++ }
++ else
++ if (iwr->u.encoding.length) {
++ result = -EINVAL;
++ break;
++ }
++ result = acx_ioctl_set_encode(dev, NULL,
++ &(iwr->u.encoding), key);
++ }
++ break;
++
++ case SIOCGIWENCODE:
++ {
++ /* get encoding token & mode */
++ u8 key[29];
++
++ result = acx_ioctl_get_encode(dev, NULL,
++ &(iwr->u.encoding), key);
++ if (iwr->u.encoding.pointer) {
++ if (copy_to_user(iwr->u.encoding.pointer,
++ key, iwr->u.encoding.length))
++ result = -EFAULT;
++ }
++ }
++ break;
++
++ /******************** iwpriv ioctls below ********************/
++#if ACX_DEBUG
++ case ACX100_IOCTL_DEBUG:
++ acx_ioctl_set_debug(dev, NULL, NULL, iwr->u.name);
++ break;
++#endif
++
++ case ACX100_IOCTL_SET_PLED:
++ acx100_ioctl_set_led_power(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_GET_PLED:
++ acx100_ioctl_get_led_power(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_LIST_DOM:
++ acx_ioctl_list_reg_domain(dev, NULL, NULL, NULL);
++ break;
++
++ case ACX100_IOCTL_SET_DOM:
++ acx_ioctl_set_reg_domain(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_GET_DOM:
++ acx_ioctl_get_reg_domain(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_SET_SCAN_PARAMS:
++ acx_ioctl_set_scan_params(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_GET_SCAN_PARAMS:
++ acx_ioctl_get_scan_params(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_SET_PREAMB:
++ acx_ioctl_set_short_preamble(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_GET_PREAMB:
++ acx_ioctl_get_short_preamble(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_SET_ANT:
++ acx_ioctl_set_antenna(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_GET_ANT:
++ acx_ioctl_get_antenna(dev, NULL, NULL, NULL);
++ break;
++
++ case ACX100_IOCTL_RX_ANT:
++ acx_ioctl_set_rx_antenna(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_TX_ANT:
++ acx_ioctl_set_tx_antenna(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_SET_ED:
++ acx_ioctl_set_ed_threshold(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_SET_CCA:
++ acx_ioctl_set_cca(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_MONITOR: /* set sniff (monitor) mode */
++ acxlog(L_IOCTL, "%s: IWPRIV monitor\n", dev->name);
++
++ /* can only be done by admin */
++ if (!capable(CAP_NET_ADMIN)) {
++ result = -EPERM;
++ break;
++ }
++ result = acx_ioctl_wlansniff(dev, NULL, NULL, iwr->u.name);
++ break;
++
++ case ACX100_IOCTL_TEST:
++ acx_ioctl_unknown11(dev, NULL, NULL, NULL);
++ break;
++
++ case ACX111_IOCTL_INFO:
++ acx111_ioctl_info(dev, NULL, NULL, NULL);
++ break;
++
++ default:
++ acxlog(L_IOCTL, "wireless ioctl 0x%04X queried "
++ "but not implemented yet\n", cmd);
++ result = -EOPNOTSUPP;
++ break;
++ }
++
++ if ((priv->dev_state_mask & ACX_STATE_IFACE_UP) && priv->set_mask) {
++ acx_sem_lock(priv);
++ acx_s_update_card_settings(priv, 0, 0);
++ acx_sem_unlock(priv);
++ }
++
++ /* older WEs don't have a commit handler,
++ * so we need to fix return code in this case */
++ if (-EINPROGRESS == result)
++ result = 0;
++
++ return result;
++}
++#endif /* WE < 13 */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/Kconfig bt_kernel/drivers/net/wireless/tiacx/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/Kconfig 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/Kconfig 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,59 @@
++config ACX
++ tristate "TI acx100/acx111 802.11b/g wireless chipsets"
++ depends on NET_RADIO && EXPERIMENTAL && FW_LOADER
++ ---help---
++ A driver for 802.11b/g wireless cards based on
++ Texas Instruments acx100 and acx111 chipsets.
++
++ This driver supports Host AP mode that allows
++ your computer to act as an IEEE 802.11 access point.
++ This driver is quite new and experimental.
++
++ These chipsets need their firmware loaded at startup.
++ You will need to provide a firmware image via hotplug.
++
++ Firmware may be in a form of single image 40-100kb in size
++ (a 'combined' firmware) or two images - main image
++ (again 40-100kb) and radio image (~10kb or less).
++
++ Firmware images are requested from hotplug using following names:
++
++ tiacx100 - main firmware image for acx100 chipset
++ tiacx100rNN - radio acx100 firmware for radio type NN
++ tiacx100cNN - combined acx100 firmware for radio type NN
++ tiacx111 - main acx111 firmware
++ tiacx111rNN - radio acx111 firmware for radio type NN
++ tiacx111cNN - combined acx111 firmware for radio type NN
++
++ Driver will attempt to load combined image first.
++ If no such image is found, it will try to load main image
++ and radio image instead.
++
++ Firmware files are not covered by GPL and are not distributed
++ with this driver for legal reasons.
++
++ Texas Instruments did not take part in development of this driver
++ in any way, shape or form.
++
++ The driver can be compiled as a module and will be named "acx".
++
++config ACX_PCI
++ bool "TI acx100/acx111 802.11b/g PCI"
++ depends on PCI && ACX
++ ---help---
++ Include PCI and CardBus support in acx.
++
++config ACX_USB
++ bool "TI acx100/acx111 802.11b/g USB"
++ depends on USB && ACX && BROKEN
++ ---help---
++ Include USB support in acx.
++
++ There is only one currently known device in this category,
++ D-Link DWL-120+, but newer devices seem to be on the horizon.
++
++config ACX_CFI
++ bool "TI acx100 802.11b/g Compact Flash"
++ depends on ACX
++ ---help---
++ Include Compact Flash support.
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/macros.h bt_kernel/drivers/net/wireless/tiacx/macros.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/macros.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/macros.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,33 @@
++#ifndef _MACROS_H
++#define _MACROS_H
++
++
++
++#define HW_SLAVE_REG_ADDR_REG 0x00000004
++#define HW_SLAVE_REG_DATA_REG 0x00000008
++#define HW_SLAVE_REG_CTRL_REG 0x0000000c
++#define HW_SLAVE_MEM_ADDR_REG 0x00000014
++#define HW_SLAVE_MEM_DATA_REG 0x00000018
++
++#define REG_LO(b,r) ((u8 *) b + r)
++#define REG_HI(b,r) ((u8 *) b + r + 2)
++
++static inline u32 acx_readl(unsigned char *iobase, unsigned int reg)
++{
++ u16 hi,lo;
++
++ writew( 0, REG_LO(iobase, HW_SLAVE_REG_CTRL_REG));
++ writew( 1, REG_HI(iobase,HW_SLAVE_REG_CTRL_REG));
++
++ writew( reg, REG_LO(iobase, HW_SLAVE_REG_ADDR_REG));
++ writew( 0, REG_HI(iobase,HW_SLAVE_REG_ADDR_REG));
++
++ lo = readw(REG_LO(iobase,HW_SLAVE_REG_DATA_REG));
++ hi = readw(REG_HI(iobase,HW_SLAVE_REG_DATA_REG));
++
++ printk("hi=%04x,lo=%04x\n",hi,lo);
++
++ return ((u32)hi<<16) | lo;
++}
++
++#endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/Makefile bt_kernel/drivers/net/wireless/tiacx/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/Makefile 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/Makefile 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,9 @@
++#Use this if you have proper Kconfig integration:
++
++obj-$(CONFIG_ACX) += acx.o
++
++acx-obj-$(CONFIG_ACX_PCI) += pci.o
++acx-obj-$(CONFIG_ACX_USB) += usb.o
++acx-obj-$(CONFIG_ACX_CFI) += cfi.o
++
++acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y)
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/pci.c bt_kernel/drivers/net/wireless/tiacx/pci.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/pci.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/pci.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,4840 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++#define ACX_PCI 1
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
++#include <linux/moduleparam.h>
++#endif
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/if_arp.h>
++#include <linux/rtnetlink.h>
++#include <linux/wireless.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif
++#include <linux/netdevice.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/pm.h>
++
++#include "acx.h"
++
++
++/*================================================================*/
++/* Local Constants */
++#define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE)
++#define PCI_ACX100_REGION1 0x01
++#define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */
++#define PCI_ACX100_REGION2 0x02
++#define PCI_ACX100_REGION2_SIZE 0x10000 /* Memory size - 64K bytes */
++
++#define PCI_ACX111_REGION1 0x00
++#define PCI_ACX111_REGION1_SIZE 0x2000 /* Memory size - 8K bytes */
++#define PCI_ACX111_REGION2 0x01
++#define PCI_ACX111_REGION2_SIZE 0x20000 /* Memory size - 128K bytes */
++
++/* Texas Instruments Vendor ID */
++#define PCI_VENDOR_ID_TI 0x104c
++
++/* ACX100 22Mb/s WLAN controller */
++#define PCI_DEVICE_ID_TI_TNETW1100A 0x8400
++#define PCI_DEVICE_ID_TI_TNETW1100B 0x8401
++
++/* ACX111 54Mb/s WLAN controller */
++#define PCI_DEVICE_ID_TI_TNETW1130 0x9066
++
++/* PCI Class & Sub-Class code, Network-'Other controller' */
++#define PCI_CLASS_NETWORK_OTHERS 0x280
++
++#define CARD_EEPROM_ID_SIZE 6
++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */
++
++
++/***********************************************************************
++*/
++static void acx_l_disable_irq(wlandevice_t *priv);
++static void acx_l_enable_irq(wlandevice_t *priv);
++static int acx_e_probe_pci(struct pci_dev *pdev,
++ const struct pci_device_id *id);
++static void acx_e_remove_pci(struct pci_dev *pdev);
++
++#ifdef CONFIG_PM
++static int acx_e_suspend(struct pci_dev *pdev, pm_message_t state);
++static int acx_e_resume(struct pci_dev *pdev);
++#endif
++
++static void acx_i_tx_timeout(netdevice_t *dev);
++static struct net_device_stats *acx_e_get_stats(netdevice_t *dev);
++static struct iw_statistics *acx_e_get_wireless_stats(netdevice_t *dev);
++
++static irqreturn_t acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
++static void acx_i_set_multicast_list(netdevice_t *dev);
++
++static int acx_e_open(netdevice_t *dev);
++static int acx_e_close(netdevice_t *dev);
++static void acx_s_up(netdevice_t *dev);
++static void acx_s_down(netdevice_t *dev);
++
++
++/***********************************************************************
++** Register access
++*/
++
++/* Pick one */
++/* #define INLINE_IO static */
++#define INLINE_IO static inline
++
++INLINE_IO u32
++acx_read_reg32(wlandevice_t *priv, unsigned int offset)
++{
++#if ACX_IO_WIDTH == 32
++ return readl((u8 *)priv->iobase + priv->io[offset]);
++#else
++ return readw((u8 *)priv->iobase + priv->io[offset])
++ + (readw((u8 *)priv->iobase + priv->io[offset] + 2) << 16);
++#endif
++}
++
++INLINE_IO u16
++acx_read_reg16(wlandevice_t *priv, unsigned int offset)
++{
++ return readw((u8 *)priv->iobase + priv->io[offset]);
++}
++
++INLINE_IO u8
++acx_read_reg8(wlandevice_t *priv, unsigned int offset)
++{
++ return readb((u8 *)priv->iobase + priv->io[offset]);
++}
++
++INLINE_IO void
++acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val)
++{
++#if ACX_IO_WIDTH == 32
++ writel(val, (u8 *)priv->iobase + priv->io[offset]);
++#else
++ writew(val & 0xffff, (u8 *)priv->iobase + priv->io[offset]);
++ writew(val >> 16, (u8 *)priv->iobase + priv->io[offset] + 2);
++#endif
++}
++
++INLINE_IO void
++acx_write_reg16(wlandevice_t *priv, unsigned int offset, u16 val)
++{
++ writew(val, (u8 *)priv->iobase + priv->io[offset]);
++}
++
++INLINE_IO void
++acx_write_reg8(wlandevice_t *priv, unsigned int offset, u8 val)
++{
++ writeb(val, (u8 *)priv->iobase + priv->io[offset]);
++}
++
++/* Handle PCI posting properly:
++ * Make sure that writes reach the adapter in case they require to be executed
++ * *before* the next write, by reading a random (and safely accessible) register.
++ * This call has to be made if there is no read following (which would flush the data
++ * to the adapter), yet the written data has to reach the adapter immediately. */
++INLINE_IO void
++acx_write_flush(wlandevice_t *priv)
++{
++ /* readb(priv->iobase + priv->io[IO_ACX_INFO_MAILBOX_OFFS]); */
++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should also be safe): */
++ readb(priv->iobase);
++}
++
++
++/***********************************************************************
++*/
++static const char name_acx100[] = "ACX100";
++static const char name_tnetw1100a[] = "TNETW1100A";
++static const char name_tnetw1100b[] = "TNETW1100B";
++
++static const char name_acx111[] = "ACX111";
++static const char name_tnetw1130[] = "TNETW1130";
++
++static const struct pci_device_id
++acx_pci_id_tbl[] __devinitdata = {
++ {
++ .vendor = PCI_VENDOR_ID_TI,
++ .device = PCI_DEVICE_ID_TI_TNETW1100A,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = CHIPTYPE_ACX100,
++ },
++ {
++ .vendor = PCI_VENDOR_ID_TI,
++ .device = PCI_DEVICE_ID_TI_TNETW1100B,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = CHIPTYPE_ACX100,
++ },
++ {
++ .vendor = PCI_VENDOR_ID_TI,
++ .device = PCI_DEVICE_ID_TI_TNETW1130,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = CHIPTYPE_ACX111,
++ },
++ {
++ .vendor = 0,
++ .device = 0,
++ .subvendor = 0,
++ .subdevice = 0,
++ .driver_data = 0,
++ }
++};
++
++MODULE_DEVICE_TABLE(pci, acx_pci_id_tbl);
++
++/* FIXME: checks should be removed once driver is included in the kernel */
++#ifndef __devexit_p
++#warning *** your kernel is EXTREMELY old since it does not even know about
++#warning __devexit_p - this driver could easily FAIL to work, so better
++#warning upgrade your kernel! ***
++#define __devexit_p(x) x
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
++/* pci_name() got introduced at start of 2.6.x,
++ * got mandatory (slot_name member removed) in 2.6.11-bk1 */
++#define pci_name(x) x->slot_name
++#endif
++
++static struct pci_driver acx_pci_drv_id = {
++ .name = "acx_pci",
++ .id_table = acx_pci_id_tbl,
++ .probe = acx_e_probe_pci,
++ .remove = __devexit_p(acx_e_remove_pci),
++#ifdef CONFIG_PM
++ .suspend = acx_e_suspend,
++ .resume = acx_e_resume
++#endif /* CONFIG_PM */
++};
++
++typedef struct acx_device {
++ netdevice_t *newest;
++} acx_device_t;
++
++/* if this driver was only about PCI devices, then we probably wouldn't
++ * need this linked list.
++ * But if we want to register ALL kinds of devices in one global list,
++ * then we need it and need to maintain it properly. */
++static struct acx_device root_acx_dev = {
++ .newest = NULL,
++};
++DECLARE_MUTEX(root_acx_dev_sem);
++
++
++/***********************************************************************
++*/
++static inline txdesc_t*
++get_txdesc(wlandevice_t* priv, int index)
++{
++ return (txdesc_t*) (((u8*)priv->txdesc_start) + index * priv->txdesc_size);
++}
++
++static inline txdesc_t*
++move_txdesc(wlandevice_t* priv, txdesc_t* txdesc, int inc)
++{
++ return (txdesc_t*) (((u8*)txdesc) + inc * priv->txdesc_size);
++}
++
++static txhostdesc_t*
++acx_get_txhostdesc(wlandevice_t* priv, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)priv->txdesc_start;
++ if (ACX_DEBUG && (index % priv->txdesc_size)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= priv->txdesc_size;
++ if (ACX_DEBUG && (index >= TX_CNT)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return &priv->txhostdesc_start[index*2];
++}
++
++static client_t*
++acx_get_txc(wlandevice_t* priv, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)priv->txdesc_start;
++ if (ACX_DEBUG && (index % priv->txdesc_size)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= priv->txdesc_size;
++ if (ACX_DEBUG && (index >= TX_CNT)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return priv->txc[index];
++}
++
++static void
++acx_put_txc(wlandevice_t* priv, txdesc_t* txdesc, client_t* c)
++{
++ int index = (u8*)txdesc - (u8*)priv->txdesc_start;
++ if (ACX_DEBUG && (index % priv->txdesc_size)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ index /= priv->txdesc_size;
++ if (ACX_DEBUG && (index >= TX_CNT)) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ priv->txc[index] = c;
++}
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acx_read_eeprom_offset
++**
++** Function called to read an octet in the EEPROM.
++**
++** This function is used by acx_probe_pci to check if the
++** connected card is a legal one or not.
++**
++** Arguments:
++** priv ptr to wlandevice structure
++** addr address to read in the EEPROM
++** charbuf ptr to a char. This is where the read octet
++** will be stored
++**
++** Returns:
++** zero (0) - failed
++** one (1) - success
++**
++** NOT ADAPTED FOR ACX111!!
++*/
++int
++acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
++ acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for EEPROM read\n",
++ priv->netdev->name);
++ goto fail;
++ }
++ }
++
++ *charbuf = acx_read_reg8(priv, IO_ACX_EEPROM_DATA);
++ acxlog(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf);
++ result = OK;
++
++fail:
++ return result;
++}
++
++
++/***********************************************************************
++** Dummy EEPROM read? why?!
++*/
++static int
++acx_read_eeprom_area(wlandevice_t *priv)
++{
++ int offs;
++ u8 tmp[0x3b];
++
++ for (offs = 0x8c; offs < 0xb9; offs++) {
++ acx_read_eeprom_offset(priv, offs, &tmp[offs - 0x8c]);
++ }
++ return OK;
++}
++
++
++/***********************************************************************
++** We don't lock hw accesses here since we never r/w eeprom in IRQ
++** Note: this function sleeps only because of GFP_KERNEL alloc
++*/
++#ifdef UNUSED
++int
++acx_s_write_eeprom_offset(wlandevice_t *priv, u32 addr, u32 len, const u8 *charbuf)
++{
++ u8 *data_verify = NULL;
++ unsigned long flags;
++ int count, i;
++ int result = NOT_OK;
++ u16 gpio_orig;
++
++ printk("acx: WARNING! I would write to EEPROM now. "
++ "Since I really DON'T want to unless you know "
++ "what you're doing (THIS CODE WILL PROBABLY "
++ "NOT WORK YET!), I will abort that now. And "
++ "definitely make sure to make a "
++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! "
++ "(the EEPROM content includes the PCI config header!! "
++ "If you kill important stuff, then you WILL "
++ "get in trouble and people DID get in trouble already)\n");
++ return OK;
++
++ FN_ENTER;
++
++ data_verify = kmalloc(len, GFP_KERNEL);
++ if (!data_verify) {
++ goto end;
++ }
++
++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line
++ * to be able to write to the EEPROM.
++ * NOTE: an EEPROM writing success has been reported,
++ * but you probably have to modify GPIO_OUT, too,
++ * and you probably need to activate a different GPIO
++ * line instead! */
++ gpio_orig = acx_read_reg16(priv, IO_ACX_GPIO_OE);
++ acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig & ~1);
++ acx_write_flush(priv);
++
++ /* ok, now start writing the data out */
++ for (i = 0; i < len; i++) {
++ acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
++ acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i);
++ acx_write_reg32(priv, IO_ACX_EEPROM_DATA, *(charbuf + i));
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 1);
++
++ while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(++count > 0xffff)) {
++ printk("WARNING, DANGER!!! "
++ "Timeout waiting for EEPROM write\n");
++ goto end;
++ }
++ }
++ }
++
++ /* disable EEPROM writing */
++ acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig);
++ acx_write_flush(priv);
++
++ /* now start a verification run */
++ count = 0xffff;
++ for (i = 0; i < len; i++) {
++ acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0);
++ acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2);
++
++ while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("timeout waiting for EEPROM read\n");
++ goto end;
++ }
++ }
++
++ data_verify[i] = acx_read_reg16(priv, IO_ACX_EEPROM_DATA);
++ }
++
++ if (0 == memcmp(charbuf, data_verify, len))
++ result = OK; /* read data matches, success */
++
++end:
++ kfree(data_verify);
++ FN_EXIT1(result);
++ return result;
++}
++#endif /* UNUSED */
++
++
++/***********************************************************************
++** acxpci_s_read_phy_reg
++**
++** Messing with rx/tx disabling and enabling here
++** (acx_write_reg32(priv, IO_ACX_ENABLE, 0b000000xx)) kills traffic
++*/
++int
++acxpci_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ FN_ENTER;
++
++ acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_PHY_CTL, 2);
++
++ count = 0xffff;
++ while (acx_read_reg32(priv, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ priv->netdev->name);
++ *charbuf = 0;
++ goto fail;
++ }
++ }
++
++ acxlog(L_DEBUG, "count was %u\n", count);
++ *charbuf = acx_read_reg8(priv, IO_ACX_PHY_DATA);
++
++ acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
++ result = OK;
++ goto fail; /* silence compiler warning */
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++int
++acxpci_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value)
++{
++ FN_ENTER;
++
++ /* FIXME: we didn't use 32bit access here since mprusko said that
++ * it results in distorted sensitivity on his card (huh!?!?
++ * doesn't happen with my setup...)
++ * But with the access reordering and flushing it
++ * shouldn't happen any more...
++ * FIXME: which radio is in the problematic card? My working one
++ * is 0x11 */
++ acx_write_reg32(priv, IO_ACX_PHY_DATA, value);
++ acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg);
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_PHY_CTL, 1);
++ acx_write_flush(priv);
++ acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#define NO_AUTO_INCREMENT 1
++
++/***********************************************************************
++** acx_s_write_fw
++**
++** Write the firmware image into the card.
++**
++** Arguments:
++** priv wlan device structure
++** apfw_image firmware image.
++**
++** Returns:
++** 1 firmware image corrupted
++** 0 success
++*/
++static int
++acx_s_write_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset)
++{
++ int len, size;
++ u32 sum, v32;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *image = (u8*)apfw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = image[0]+image[1]+image[2]+image[3];
++ image += 4;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ acxlog(L_INIT, "not using auto increment for firmware loading\n");
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++ acx_write_flush(priv);
++#endif
++
++ len = 0;
++ size = le32_to_cpu(apfw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)image);
++ sum += image[0]+image[1]+image[2]+image[3];
++ image += 4;
++ len += 4;
++
++#if NO_AUTO_INCREMENT
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++ acx_write_flush(priv);
++#endif
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, v32);
++ }
++
++ acxlog(L_DEBUG, "%s: firmware written\n", __func__);
++
++ /* compare our checksum with the stored image checksum */
++ return (sum != le32_to_cpu(apfw_image->chksum));
++}
++
++
++/***********************************************************************
++** acx_s_validate_fw
++**
++** Compare the firmware image given with
++** the firmware image written into the card.
++**
++** Arguments:
++** priv wlan device structure
++** apfw_image firmware image.
++**
++** Returns:
++** NOT_OK firmware image corrupted or not correctly written
++** OK success
++*/
++static int
++acx_s_validate_fw(wlandevice_t *priv, const firmware_image_t *apfw_image,
++ u32 offset)
++{
++ u32 v32, w32, sum;
++ int len, size;
++ int result = OK;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *image = (u8*)apfw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = image[0]+image[1]+image[2]+image[3];
++ image += 4;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++#endif
++
++ len = 0;
++ size = le32_to_cpu(apfw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)image);
++ image += 4;
++ len += 4;
++
++#if NO_AUTO_INCREMENT
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++#endif
++ w32 = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ if (unlikely(w32 != v32)) {
++ printk("acx: FATAL: firmware upload: "
++ "data parts at offset %d don't match (0x%08X vs. 0x%08X)! "
++ "I/O timing issues or defective memory, with DWL-xx0+? "
++ "ACX_IO_WIDTH=16 may help. Please report\n",
++ len, v32, w32);
++ result = NOT_OK;
++ break;
++ }
++
++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24);
++ }
++
++ /* sum control verification */
++ if (result != NOT_OK) {
++ if (sum != le32_to_cpu(apfw_image->chksum)) {
++ printk("acx: FATAL: firmware upload: "
++ "checksums don't match!\n");
++ result = NOT_OK;
++ }
++ }
++
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_upload_fw
++**
++** Arguments:
++** wlandevice: private device that contains card device
++** Returns:
++** NOT_OK: failed
++** OK: success
++** Call context:
++** acx_reset_dev
++*/
++static int
++acx_s_upload_fw(wlandevice_t *priv)
++{
++ firmware_image_t *apfw_image = NULL;
++ int res = NOT_OK;
++ int try;
++ u32 size;
++ char filename[sizeof("tiacx1NNcNN")];
++
++ FN_ENTER;
++
++ /* Try combined, then main image */
++ priv->need_radio_fw = 0;
++ sprintf(filename, "tiacx1%02dc%02X",
++ IS_ACX111(priv)*11, priv->radio_type);
++
++ apfw_image = acx_s_read_fw(&priv->pdev->dev, filename, &size);
++ if (!apfw_image) {
++ priv->need_radio_fw = 1;
++ filename[sizeof("tiacx1NN")-1] = '\0';
++ apfw_image = acx_s_read_fw(&priv->pdev->dev, filename, &size);
++ if (!apfw_image) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++ }
++
++ for (try = 1; try <= 5; try++) {
++ res = acx_s_write_fw(priv, apfw_image, 0);
++ acxlog(L_DEBUG|L_INIT, "acx_write_fw (main/combined):%d\n", res);
++ if (OK == res) {
++ res = acx_s_validate_fw(priv, apfw_image, 0);
++ acxlog(L_DEBUG|L_INIT, "acx_validate_fw "
++ "(main/combined):%d\n", res);
++ }
++
++ if (OK == res) {
++ SET_BIT(priv->dev_state_mask, ACX_STATE_FW_LOADED);
++ break;
++ }
++ printk("acx: firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ vfree(apfw_image);
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx_s_upload_radio
++**
++** Uploads the appropriate radio module firmware
++** into the card.
++*/
++int
++acx_s_upload_radio(wlandevice_t *priv)
++{
++ acx_ie_memmap_t mm;
++ firmware_image_t *radio_image = NULL;
++ acx_cmd_radioinit_t radioinit;
++ int res = NOT_OK;
++ int try;
++ u32 offset;
++ u32 size;
++ char filename[sizeof("tiacx1NNrNN")];
++
++ if (!priv->need_radio_fw) return OK;
++
++ FN_ENTER;
++
++ acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP);
++ offset = le32_to_cpu(mm.CodeEnd);
++
++ sprintf(filename, "tiacx1%02dr%02X",
++ IS_ACX111(priv)*11,
++ priv->radio_type);
++ radio_image = acx_s_read_fw(&priv->pdev->dev, filename, &size);
++ if (!radio_image) {
++ printk("acx: can't load radio module '%s'\n", filename);
++ goto fail;
++ }
++
++ acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ for (try = 1; try <= 5; try++) {
++ res = acx_s_write_fw(priv, radio_image, offset);
++ acxlog(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res);
++ if (OK == res) {
++ res = acx_s_validate_fw(priv, radio_image, offset);
++ acxlog(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res);
++ }
++
++ if (OK == res)
++ break;
++ printk("acx: radio firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
++ radioinit.offset = cpu_to_le32(offset);
++ /* no endian conversion needed, remains in card CPU area: */
++ radioinit.len = radio_image->size;
++
++ vfree(radio_image);
++
++ if (OK != res)
++ goto fail;
++
++ /* will take a moment so let's have a big timeout */
++ acx_s_issue_cmd_timeo(priv, ACX1xx_CMD_RADIOINIT,
++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000));
++
++ res = acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP);
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx_l_reset_mac
++**
++** Arguments:
++** wlandevice: private device that contains card device
++** Side effects:
++** MAC will be reset
++** Call context:
++** acx_reset_dev
++** Comment:
++** resets onboard acx100 MAC
++**
++** Requires lock to be taken
++*/
++static void
++acx_l_reset_mac(wlandevice_t *priv)
++{
++ u16 temp;
++
++ FN_ENTER;
++
++ /* halt eCPU */
++ temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1;
++ acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp);
++
++ /* now do soft reset of eCPU */
++ temp = acx_read_reg16(priv, IO_ACX_SOFT_RESET) | 0x1;
++ acxlog(L_DEBUG, "%s: enable soft reset...\n", __func__);
++ acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp);
++ acx_write_flush(priv);
++
++ /* now reset bit again */
++ acxlog(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__);
++ /* deassert eCPU reset */
++ acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp & ~0x1);
++
++ /* now start a burst read from initial flash EEPROM */
++ temp = acx_read_reg16(priv, IO_ACX_EE_START) | 0x1;
++ acx_write_reg16(priv, IO_ACX_EE_START, temp);
++ acx_write_flush(priv);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_s_verify_init
++*/
++static int
++acx_s_verify_init(wlandevice_t *priv)
++{
++ int result = NOT_OK;
++ int timer;
++
++ FN_ENTER;
++
++ for (timer = 40; timer > 0; timer--) {
++ u16 irqstat = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqstat & HOST_INT_FCS_THRESHOLD) {
++ result = OK;
++ acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD);
++ break;
++ }
++ /* HZ / 50 resulted in 24 schedules for ACX100 on my machine,
++ * so better schedule away longer for greater efficiency,
++ * decrease loop count */
++ acx_s_msleep(50);
++ }
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** A few low-level helpers
++**
++** Note: these functions are not protected by lock
++** and thus are never allowed to be called from IRQ.
++** Also they must not race with fw upload which uses same hw regs
++*/
++
++/***********************************************************************
++** acx_read_info_status
++*/
++/* Info mailbox format:
++2 bytes: type
++2 bytes: status
++more bytes may follow
++ docs say about status:
++ 0x0000 info available (set by hw)
++ 0x0001 information received (must be set by host)
++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw)
++ but in practice we've seen:
++ 0x9000 when we did not set status to 0x0001 on prev message
++ 0x1001 when we did set it
++ 0x0000 was never seen
++ conclusion: this is really a bitfield:
++ 0x1000 is 'info available' bit
++ 'mailbox overflowed' bit is 0x8000, not 0x1000
++ value of 0x0000 probably means that there is no message at all
++ P.S. I dunno how in hell hw is supposed to notice that messages are lost -
++ it does NOT clear bit 0x0001, and this bit will probably stay forever set
++ after we set it once. Let's hope this will be fixed in firmware someday
++*/
++static void
++acx_read_info_status(wlandevice_t *priv)
++{
++ u32 value;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1);
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
++ acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS));
++
++ /* make sure we only read the data once all cfg registers are written: */
++ acx_write_flush(priv);
++ value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ priv->info_type = (u16)value;
++ priv->info_status = (value >> 16);
++
++ /* inform hw that we have read this info message */
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, priv->info_type | 0x00010000);
++ acx_write_flush(priv);
++ /* now bother hw to notice it: */
++ acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_INFOACK);
++ acx_write_flush(priv);
++
++ acxlog(L_CTL, "info_type 0x%04X, info_status 0x%04X\n",
++ priv->info_type, priv->info_status);
++}
++
++
++/***********************************************************************
++** acx_write_cmd_type_or_status
++*/
++static void
++acx_write_cmd_type_or_status(wlandevice_t *priv, u32 val)
++{
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
++ acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS));
++
++ /* make sure we only write the data once all config registers are written */
++ acx_write_flush(priv);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, val);
++ acx_write_flush(priv);
++}
++static inline void
++acx_write_cmd_type(wlandevice_t *priv, u32 val)
++{
++ acx_write_cmd_type_or_status(priv, val);
++}
++static inline void
++acx_write_cmd_status(wlandevice_t *priv, u32 val)
++{
++ acx_write_cmd_type_or_status(priv, val<<16);
++}
++
++
++/***********************************************************************
++** acx_read_cmd_status
++*/
++static void
++acx_read_cmd_status(wlandevice_t *priv)
++{
++ u32 value;
++
++ acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0);
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */
++
++ acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR,
++ acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS));
++
++ /* make sure we only read the data once all config registers are written */
++ acx_write_flush(priv);
++ value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA);
++
++ priv->cmd_type = (u16)value;
++ priv->cmd_status = (value >> 16);
++
++ acxlog(L_CTL, "cmd_type 0x%04X, cmd_status 0x%04X [%s]\n",
++ priv->cmd_type, priv->cmd_status,
++ acx_cmd_status_str(priv->cmd_status));
++}
++
++
++/***********************************************************************
++** acx_s_reset_dev
++**
++** Arguments:
++** netdevice that contains the wlandevice priv variable
++** Returns:
++** NOT_OK on fail
++** OK on success
++** Side effects:
++** device is hard reset
++** Call context:
++** acx_probe_pci
++** Comment:
++** This resets the acx100 device using low level hardware calls
++** as well as uploads and verifies the firmware to the card
++*/
++static int
++acx_s_reset_dev(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ const char* msg = "";
++ unsigned long flags;
++ int result = NOT_OK;
++ u16 hardware_info;
++ u16 ecpu_ctrl;
++
++ FN_ENTER;
++
++ /* we're doing a reset, so hardware is unavailable */
++
++ /* reset the device to make sure the eCPU is stopped
++ * to upload the firmware correctly */
++
++ acx_lock(priv, flags);
++
++ acx_l_reset_mac(priv);
++
++ ecpu_ctrl = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) & 1;
++ if (!ecpu_ctrl) {
++ msg = "eCPU is already running. ";
++ goto fail_unlock;
++ }
++
++#ifdef WE_DONT_NEED_THAT_DO_WE
++ if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 2) {
++ /* eCPU most likely means "embedded CPU" */
++ msg = "eCPU did not start after boot from flash. ";
++ goto fail_unlock;
++ }
++
++ /* check sense on reset flags */
++ if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 0x10) {
++ printk("%s: eCPU did not start after boot (SOR), "
++ "is this fatal?\n", dev->name);
++ }
++#endif
++ /* scan, if any, is stopped now, setting corresponding IRQ bit */
++ priv->irq_status |= HOST_INT_SCAN_COMPLETE;
++
++ acx_unlock(priv, flags);
++
++ /* without this delay acx100 may fail to report hardware_info
++ ** (see below). Most probably eCPU runs some init code */
++ acx_s_msleep(10);
++
++ /* Need to know radio type before fw load */
++ hardware_info = acx_read_reg16(priv, IO_ACX_EEPROM_INFORMATION);
++ priv->form_factor = hardware_info & 0xff;
++ priv->radio_type = hardware_info >> 8;
++
++ /* load the firmware */
++ if (OK != acx_s_upload_fw(priv))
++ goto fail;
++
++ acx_s_msleep(10);
++
++ /* now start eCPU by clearing bit */
++ acxlog(L_DEBUG, "booted eCPU up and waiting for completion...\n");
++ acx_write_reg16(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1);
++
++ /* wait for eCPU bootup */
++ if (OK != acx_s_verify_init(priv)) {
++ msg = "timeout waiting for eCPU. ";
++ goto fail;
++ }
++
++ acxlog(L_DEBUG, "eCPU has woken up, card is ready to be configured\n");
++
++ if (IS_ACX111(priv)) {
++ acxlog(L_DEBUG, "cleaning up cmd mailbox access area\n");
++ acx_write_cmd_status(priv, 0);
++ acx_read_cmd_status(priv);
++ if (priv->cmd_status) {
++ msg = "error cleaning cmd mailbox area. ";
++ goto fail;
++ }
++ }
++
++ /* TODO what is this one doing ?? adapt for acx111 */
++ if ((OK != acx_read_eeprom_area(priv)) && IS_ACX100(priv)) {
++ /* does "CIS" mean "Card Information Structure"?
++ * If so, then this would be a PCMCIA message...
++ */
++ msg = "CIS error. ";
++ goto fail;
++ }
++
++ result = OK;
++ FN_EXIT1(result);
++ return result;
++
++/* Finish error message. Indicate which function failed */
++fail_unlock:
++ acx_unlock(priv, flags);
++fail:
++ printk("acx: %sreset_dev() FAILED\n", msg);
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_init_mboxes
++*/
++void
++acx_init_mboxes(wlandevice_t *priv)
++{
++ u32 cmd_offs, info_offs;
++
++ FN_ENTER;
++
++ cmd_offs = acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS);
++ info_offs = acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS);
++ priv->cmd_area = (u8 *)priv->iobase2 + cmd_offs + 0x4;
++ priv->info_area = (u8 *)priv->iobase2 + info_offs + 0x4;
++ acxlog(L_DEBUG, "iobase2=%p\n"
++ "cmd_mbox_offset=%X cmd_area=%p\n"
++ "info_mbox_offset=%X info_area=%p\n",
++ priv->iobase2,
++ cmd_offs, priv->cmd_area,
++ info_offs, priv->info_area);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_issue_cmd_timeo
++* Excecutes a command in the command mailbox
++*
++* Arguments:
++* *pcmdparam = an pointer to the data. The data mustn't include
++* the 4 byte command header!
++*
++* NB: we do _not_ take lock inside, so be sure to not touch anything
++* which may interfere with IRQ handler operation
++*
++* TODO: busy wait is a bit silly, so:
++* 1) stop doing many iters - go to sleep after first
++* 2) go to waitqueue based approach: wait, not poll!
++*----------------------------------------------------------------*/
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxpci_s_issue_cmd_timeo(
++ wlandevice_t *priv,
++ unsigned int cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout)
++{
++#else
++int
++acxpci_s_issue_cmd_timeo_debug(
++ wlandevice_t *priv,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout,
++ const char* cmdstr)
++{
++ unsigned long start = jiffies;
++#endif
++ const char *devname;
++ unsigned counter;
++ u16 irqtype;
++ u16 cmd_status;
++
++ FN_ENTER;
++
++ devname = priv->netdev->name;
++ if (!devname || !devname[0])
++ devname = "acx";
++
++ acxlog(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n",
++ cmdstr, buflen, timeout,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ if (!(priv->dev_state_mask & ACX_STATE_FW_LOADED)) {
++ printk("%s: "FUNC"(): firmware is not loaded yet, "
++ "cannot execute commands!\n", devname);
++ goto bad;
++ }
++
++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) {
++ printk("input pdr (len=%u):\n", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++
++ /* wait for firmware to become idle for our command submission */
++ counter = 199; /* in ms */
++ do {
++ acx_read_cmd_status(priv);
++ /* Test for IDLE state */
++ if (!priv->cmd_status)
++ break;
++ if (counter % 10 == 0) {
++ /* we waited 10 iterations, no luck. Sleep 10 ms */
++ acx_s_msleep(10);
++ }
++ } while (--counter);
++
++ if (!counter) {
++ /* the card doesn't get idle, we're in trouble */
++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n",
++ devname, priv->cmd_status);
++ goto bad;
++ } else if (counter < 190) { /* if waited >10ms... */
++ acxlog(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. "
++ "Please report\n", 199 - counter);
++ }
++
++ /* now write the parameters of the command if needed */
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++#if CMD_DISCOVERY
++ if (cmd == ACX1xx_CMD_INTERROGATE)
++ memset(priv->cmd_area, 0xAA, buflen);
++#endif
++ memcpy(priv->cmd_area, buffer,
++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen);
++ }
++ /* now write the actual command type */
++ priv->cmd_type = cmd;
++ acx_write_cmd_type(priv, cmd);
++ /* execute command */
++ acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_CMD);
++ acx_write_flush(priv);
++
++ /* wait for firmware to process command */
++
++ /* Ensure nonzero and not too large timeout.
++ ** Also converts e.g. 100->99, 200->199
++ ** which is nice but not essential */
++ timeout = (timeout-1) | 1;
++ if (unlikely(timeout > 1199))
++ timeout = 1199;
++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */
++ priv->irq_status &= ~HOST_INT_CMD_COMPLETE;
++
++ /* we schedule away sometimes (timeout can be large) */
++ counter = timeout;
++ do {
++ if (!priv->irqs_active) { /* IRQ disabled: poll */
++ irqtype = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ acx_write_reg16(priv, IO_ACX_IRQ_ACK,
++ HOST_INT_CMD_COMPLETE);
++ break;
++ }
++ } else { /* Wait when IRQ will set the bit */
++ irqtype = priv->irq_status;
++ if (irqtype & HOST_INT_CMD_COMPLETE)
++ break;
++ }
++
++ if (counter % 10 == 0) {
++ /* we waited 10 iterations, no luck. Sleep 10 ms */
++ acx_s_msleep(10);
++ }
++ } while (--counter);
++
++ /* save state for debugging */
++ acx_read_cmd_status(priv);
++ cmd_status = priv->cmd_status;
++
++ /* put the card in IDLE state */
++ priv->cmd_status = 0;
++ acx_write_cmd_status(priv, 0);
++
++ if (!counter) { /* timed out! */
++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. "
++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms "
++ "cmd_status:%d (%s)\n",
++ devname, (priv->irqs_active) ? "waiting" : "polling",
++ irqtype, priv->irq_status, timeout,
++ cmd_status, acx_cmd_status_str(cmd_status));
++ goto bad;
++ } else if (timeout - counter > 30) { /* if waited >30ms... */
++ acxlog(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. "
++ "count:%d. Please report\n",
++ (priv->irqs_active) ? "waited" : "polled",
++ timeout - counter, counter);
++ }
++
++ if (1 != cmd_status) { /* it is not a 'Success' */
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). "
++ "Took %dms of %d\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status),
++ timeout - counter, timeout);
++ /* zero out result buffer */
++ if (buffer && buflen)
++ memset(buffer, 0, buflen);
++ goto bad;
++ }
++
++ /* read in result parameters if needed */
++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) {
++ memcpy(buffer, priv->cmd_area, buflen);
++ if (acx_debug & L_DEBUG) {
++ printk("output buffer (len=%u): ", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++ }
++/* ok: */
++ acxlog(L_CTL, FUNC"(%s): took %ld jiffies to complete\n",
++ cmdstr, jiffies - start);
++ FN_EXIT1(OK);
++ return OK;
++
++bad:
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_get_firmware_version
++*----------------------------------------------------------------*/
++static void
++acx_s_get_firmware_version(wlandevice_t *priv)
++{
++ fw_ver_t fw;
++ u8 hexarr[4] = { 0, 0, 0, 0 };
++ int hexidx = 0, val = 0;
++ const char *num;
++ char c;
++
++ FN_ENTER;
++
++ acx_s_interrogate(priv, &fw, ACX1xx_IE_FWREV);
++ memcpy(priv->firmware_version, fw.fw_id, FW_ID_SIZE);
++ priv->firmware_version[FW_ID_SIZE] = '\0';
++ acxlog(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n",
++ priv->firmware_version, fw.hw_id);
++
++ if (strncmp(fw.fw_id, "Rev ", 4) != 0) {
++ printk("acx: strange firmware version string "
++ "'%s', please report\n", priv->firmware_version);
++ priv->firmware_numver = 0x01090407; /* assume 1.9.4.7 */
++ } else {
++ num = &fw.fw_id[4];
++ while (1) {
++ c = *num++;
++ if ((c == '.') || (c == '\0')) {
++ hexarr[hexidx++] = val;
++ if ((hexidx > 3) || (c == '\0')) /* end? */
++ break;
++ val = 0;
++ continue;
++ }
++ if ((c >= '0') && (c <= '9'))
++ c -= '0';
++ else
++ c = c - 'a' + (char)10;
++ val = val*16 + c;
++ }
++
++ priv->firmware_numver = (u32)(
++ (hexarr[0] << 24) + (hexarr[1] << 16)
++ + (hexarr[2] << 8) + hexarr[3]);
++ acxlog(L_DEBUG, "firmware_numver 0x%08X\n", priv->firmware_numver);
++ }
++ if (IS_ACX111(priv)) {
++ if (priv->firmware_numver == 0x00010011) {
++ /* This one does not survive floodpinging */
++ printk("acx: firmware '%s' is known to be buggy, "
++ "please upgrade\n", priv->firmware_version);
++ }
++ if (priv->firmware_numver == 0x02030131) {
++ /* With this one, all rx packets look mangled
++ ** Most probably we simply do not know how to use it
++ ** properly */
++ printk("acx: firmware '%s' does not work well "
++ "with this driver\n", priv->firmware_version);
++ }
++ }
++
++ priv->firmware_id = le32_to_cpu(fw.hw_id);
++
++ /* we're able to find out more detailed chip names now */
++ switch (priv->firmware_id & 0xffff0000) {
++ case 0x01010000:
++ case 0x01020000:
++ priv->chip_name = name_tnetw1100a;
++ break;
++ case 0x01030000:
++ priv->chip_name = name_tnetw1100b;
++ break;
++ case 0x03000000:
++ case 0x03010000:
++ priv->chip_name = name_tnetw1130;
++ break;
++ default:
++ printk("acx: unknown chip ID 0x%08X, "
++ "please report\n", priv->firmware_id);
++ break;
++ }
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_display_hardware_details
++*
++* Arguments:
++* priv: ptr to wlandevice that contains all the details
++* displayed by this function
++* Call context:
++* acx_probe_pci
++* Comment:
++* This function will display strings to the system log according
++* to device form_factor and radio type. It will needed to be
++*----------------------------------------------------------------*/
++static void
++acx_display_hardware_details(wlandevice_t *priv)
++{
++ const char *radio_str, *form_str;
++
++ FN_ENTER;
++
++ switch (priv->radio_type) {
++ case RADIO_MAXIM_0D:
++ /* hmm, the DWL-650+ seems to have two variants,
++ * according to a windows driver changelog comment:
++ * RFMD and Maxim. */
++ radio_str = "Maxim";
++ break;
++ case RADIO_RFMD_11:
++ radio_str = "RFMD";
++ break;
++ case RADIO_RALINK_15:
++ radio_str = "Ralink";
++ break;
++ case RADIO_RADIA_16:
++ radio_str = "Radia";
++ break;
++ case RADIO_UNKNOWN_17:
++ /* TI seems to have a radio which is
++ * additionally 802.11a capable, too */
++ radio_str = "802.11a/b/g radio?! Please report";
++ break;
++ case RADIO_UNKNOWN_19:
++ radio_str = "A radio used by Safecom cards?! Please report";
++ break;
++ default:
++ radio_str = "UNKNOWN, please report the radio type name!";
++ break;
++ }
++
++ switch (priv->form_factor) {
++ case 0x00:
++ form_str = "unspecified";
++ break;
++ case 0x01:
++ form_str = "(mini-)PCI / CardBus";
++ break;
++ case 0x02:
++ form_str = "USB";
++ break;
++ case 0x03:
++ form_str = "Compact Flash";
++ break;
++ default:
++ form_str = "UNKNOWN, Please report";
++ break;
++ }
++
++ printk("acx: form factor 0x%02X (%s), "
++ "radio type 0x%02X (%s), EEPROM version 0x%02X, "
++ "uploaded firmware '%s' (0x%08X)\n",
++ priv->form_factor, form_str, priv->radio_type, radio_str,
++ priv->eeprom_version, priv->firmware_version,
++ priv->firmware_id);
++
++ FN_EXIT0;
++}
++
++/***********************************************************************
++*/
++#ifdef NONESSENTIAL_FEATURES
++typedef struct device_id {
++ unsigned char id[6];
++ char *descr;
++ char *type;
++} device_id_t;
++
++static const device_id_t
++device_ids[] =
++{
++ {
++ {'G', 'l', 'o', 'b', 'a', 'l'},
++ NULL,
++ NULL,
++ },
++ {
++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
++ "uninitialized",
++ "SpeedStream SS1021 or Gigafast WF721-AEX"
++ },
++ {
++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85},
++ "non-standard",
++ "DrayTek Vigor 520"
++ },
++ {
++ {'?', '?', '?', '?', '?', '?'},
++ "non-standard",
++ "Level One WPC-0200"
++ },
++ {
++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ "empty",
++ "DWL-650+ variant"
++ }
++};
++
++static void
++acx_show_card_eeprom_id(wlandevice_t *priv)
++{
++ unsigned char buffer[CARD_EEPROM_ID_SIZE];
++ int i;
++
++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE);
++ /* use direct EEPROM access */
++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) {
++ if (OK != acx_read_eeprom_offset(priv,
++ ACX100_EEPROM_ID_OFFSET + i,
++ &buffer[i]))
++ {
++ printk("acx: reading EEPROM FAILED\n");
++ break;
++ }
++ }
++
++ for (i = 0; i < VEC_SIZE(device_ids); i++) {
++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) {
++ if (device_ids[i].descr) {
++ printk("acx: EEPROM card ID string check "
++ "found %s card ID: is this %s?\n",
++ device_ids[i].descr, device_ids[i].type);
++ }
++ break;
++ }
++ }
++ if (i == VEC_SIZE(device_ids)) {
++ printk("acx: EEPROM card ID string check found "
++ "unknown card: expected 'Global', got '%.*s\'. "
++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer);
++ }
++}
++#endif /* NONESSENTIAL_FEATURES */
++
++
++/***********************************************************************
++*/
++static void
++acx_s_device_chain_add(struct net_device *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ down(&root_acx_dev_sem);
++ priv->prev_nd = root_acx_dev.newest;
++ root_acx_dev.newest = dev;
++ priv->netdev = dev;
++ up(&root_acx_dev_sem);
++}
++
++static void
++acx_s_device_chain_remove(struct net_device *dev)
++{
++ struct net_device *querydev;
++ struct net_device *olderdev;
++ struct net_device *newerdev;
++
++ down(&root_acx_dev_sem);
++ querydev = root_acx_dev.newest;
++ newerdev = NULL;
++ while (querydev) {
++ olderdev = ((wlandevice_t*)netdev_priv(querydev))->prev_nd;
++ if (0 == strcmp(querydev->name, dev->name)) {
++ if (!newerdev) {
++ /* if we were at the beginning of the
++ * list, then it's the list head that
++ * we need to update to point at the
++ * next older device */
++ root_acx_dev.newest = olderdev;
++ } else {
++ /* it's the device that is newer than us
++ * that we need to update to point at
++ * the device older than us */
++ ((wlandevice_t*)netdev_priv(newerdev))->
++ prev_nd = olderdev;
++ }
++ break;
++ }
++ /* "newerdev" is actually the device of the old iteration,
++ * but since the list starts (root_acx_dev.newest)
++ * with the newest devices,
++ * it's newer than the ones following.
++ * Oh the joys of iterating from newest to oldest :-\ */
++ newerdev = querydev;
++
++ /* keep checking old devices for matches until we hit the end
++ * of the list */
++ querydev = olderdev;
++ }
++ up(&root_acx_dev_sem);
++}
++
++
++/***********************************************************************
++** acx_free_desc_queues
++**
++** Releases the queues that have been allocated, the
++** others have been initialised to NULL so this
++** function can be used if only part of the queues were allocated.
++*/
++static inline void
++acx_free_coherent(struct pci_dev *hwdev, size_t size,
++ void *vaddr, dma_addr_t dma_handle)
++{
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53)
++ dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev,
++ size, vaddr, dma_handle);
++#else
++ pci_free_consistent(hwdev, size, vaddr, dma_handle);
++#endif
++}
++
++void
++acx_free_desc_queues(wlandevice_t *priv)
++{
++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \
++ if (ptr) { \
++ acx_free_coherent(0, size, ptr, phyaddr); \
++ ptr = NULL; \
++ size = 0; \
++ }
++
++ FN_ENTER;
++
++ ACX_FREE_QUEUE(priv->txhostdesc_area_size, priv->txhostdesc_start, priv->txhostdesc_startphy);
++ ACX_FREE_QUEUE(priv->txbuf_area_size, priv->txbuf_start, priv->txbuf_startphy);
++
++ priv->txdesc_start = NULL;
++
++ ACX_FREE_QUEUE(priv->rxhostdesc_area_size, priv->rxhostdesc_start, priv->rxhostdesc_startphy);
++ ACX_FREE_QUEUE(priv->rxbuf_area_size, priv->rxbuf_start, priv->rxbuf_startphy);
++
++ priv->rxdesc_start = NULL;
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_delete_dma_regions
++*----------------------------------------------------------------*/
++static void
++acx_s_delete_dma_regions(wlandevice_t *priv)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands
++ * here instead? Or are we that much down the road that it's no
++ * longer possible here? */
++ acx_write_reg16(priv, IO_ACX_ENABLE, 0);
++
++ acx_s_msleep(100);
++
++ acx_lock(priv, flags);
++ acx_free_desc_queues(priv);
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_probe_pci
++*
++* Probe routine called when a PCI device w/ matching ID is found.
++* Here's the sequence:
++* - Allocate the PCI resources.
++* - Read the PCMCIA attribute memory to make sure we have a WLAN card
++* - Reset the MAC
++* - Initialize the dev and wlan data
++* - Initialize the MAC
++*
++* Arguments:
++* pdev ptr to pci device structure containing info about
++* pci configuration.
++* id ptr to the device id entry that matched this device.
++*
++* Returns:
++* zero - success
++* negative - failed
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static const u16
++IO_ACX100[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x007c, /* IO_ACX_INT_TRIG */
++ 0x0098, /* IO_ACX_IRQ_MASK */
++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00ac, /* IO_ACX_IRQ_ACK */
++ 0x00b0, /* IO_ACX_HINT_TRIG */
++
++ 0x0104, /* IO_ACX_ENABLE */
++
++ 0x0250, /* IO_ACX_EEPROM_CTL */
++ 0x0254, /* IO_ACX_EEPROM_ADDR */
++ 0x0258, /* IO_ACX_EEPROM_DATA */
++ 0x025c, /* IO_ACX_EEPROM_CFG */
++
++ 0x0268, /* IO_ACX_PHY_ADDR */
++ 0x026c, /* IO_ACX_PHY_DATA */
++ 0x0270, /* IO_ACX_PHY_CTL */
++
++ 0x0290, /* IO_ACX_GPIO_OE */
++
++ 0x0298, /* IO_ACX_GPIO_OUT */
++
++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x02d0, /* IO_ACX_EE_START */
++ 0x02d4, /* IO_ACX_SOR_CFG */
++ 0x02d8 /* IO_ACX_ECPU_CTRL */
++};
++
++static const u16
++IO_ACX111[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x00b4, /* IO_ACX_INT_TRIG */
++ 0x00d4, /* IO_ACX_IRQ_MASK */
++ /* we need NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */
++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00e8, /* IO_ACX_IRQ_ACK */
++ 0x00ec, /* IO_ACX_HINT_TRIG */
++
++ 0x01d0, /* IO_ACX_ENABLE */
++
++ 0x0338, /* IO_ACX_EEPROM_CTL */
++ 0x033c, /* IO_ACX_EEPROM_ADDR */
++ 0x0340, /* IO_ACX_EEPROM_DATA */
++ 0x0344, /* IO_ACX_EEPROM_CFG */
++
++ 0x0350, /* IO_ACX_PHY_ADDR */
++ 0x0354, /* IO_ACX_PHY_DATA */
++ 0x0358, /* IO_ACX_PHY_CTL */
++
++ 0x0374, /* IO_ACX_GPIO_OE */
++
++ 0x037c, /* IO_ACX_GPIO_OUT */
++
++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x0100, /* IO_ACX_EE_START */
++ 0x0104, /* IO_ACX_SOR_CFG */
++ 0x0108, /* IO_ACX_ECPU_CTRL */
++};
++
++static void
++acx_netdev_init(struct net_device *dev) {}
++
++//FIXME: do the same for USB
++static int
++acx_change_mtu(struct net_device *dev, int mtu)
++{
++ enum {
++ MIN_MTU = 256,
++ MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN)
++ };
++
++ if (mtu < MIN_MTU || mtu > MAX_MTU)
++ return -EINVAL;
++
++ dev->mtu = mtu;
++ return 0;
++}
++
++static int __devinit
++acx_e_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++ unsigned long mem_region1 = 0;
++ unsigned long mem_region2 = 0;
++ unsigned long mem_region1_size;
++ unsigned long mem_region2_size;
++ unsigned long phymem1;
++ unsigned long phymem2;
++ void *mem1 = NULL;
++ void *mem2 = NULL;
++ wlandevice_t *priv = NULL;
++ struct net_device *dev = NULL;
++ const char *chip_name;
++ int result = -EIO;
++ int err;
++ u8 chip_type;
++
++#if SEPARATE_DRIVER_INSTANCES
++ struct pci_dev *tdev;
++ unsigned int inited;
++ static int turn = 0;
++#endif /* SEPARATE_DRIVER_INSTANCES */
++
++ FN_ENTER;
++
++#if SEPARATE_DRIVER_INSTANCES
++ if (card) {
++ turn++;
++ inited = 0;
++ pci_for_each_dev(tdev) {
++ if (tdev->vendor != PCI_VENDOR_ID_TI)
++ continue;
++
++ if (tdev == pdev)
++ break;
++ if (pci_get_drvdata(tdev))
++ inited++;
++ }
++ if (inited + turn != card) {
++ result = -ENODEV;
++ goto done;
++ }
++ }
++#endif /* SEPARATE_DRIVER_INSTANCES */
++
++ /* Enable the PCI device */
++ if (pci_enable_device(pdev)) {
++ printk("acx: pci_enable_device() FAILED\n");
++ result = -ENODEV;
++ goto fail_pci_enable_device;
++ }
++
++ /* enable busmastering (required for CardBus) */
++ pci_set_master(pdev);
++
++ /* chiptype is u8 but id->driver_data is ulong
++ ** Works for now (possible values are 1 and 2) */
++ chip_type = (u8)id->driver_data;
++ /* acx100 and acx111 have different PCI memory regions */
++ if (chip_type == CHIPTYPE_ACX100) {
++ chip_name = name_acx100;
++ mem_region1 = PCI_ACX100_REGION1;
++ mem_region1_size = PCI_ACX100_REGION1_SIZE;
++
++ mem_region2 = PCI_ACX100_REGION2;
++ mem_region2_size = PCI_ACX100_REGION2_SIZE;
++ } else if (chip_type == CHIPTYPE_ACX111) {
++ chip_name = name_acx111;
++ mem_region1 = PCI_ACX111_REGION1;
++ mem_region1_size = PCI_ACX111_REGION1_SIZE;
++
++ mem_region2 = PCI_ACX111_REGION2;
++ mem_region2_size = PCI_ACX111_REGION2_SIZE;
++ } else {
++ printk("acx: unknown chip type 0x%04X\n", chip_type);
++ goto fail_unknown_chiptype;
++ }
++
++ /* Figure out our resources */
++ phymem1 = pci_resource_start(pdev, mem_region1);
++ phymem2 = pci_resource_start(pdev, mem_region2);
++
++ if (!request_mem_region(phymem1, pci_resource_len(pdev, mem_region1), "ACX1xx_1")) {
++ printk("acx: cannot reserve PCI memory region 1 (are you sure "
++ "you have CardBus support in kernel?)\n");
++ goto fail_request_mem_region1;
++ }
++
++ if (!request_mem_region(phymem2, pci_resource_len(pdev, mem_region2), "ACX1xx_2")) {
++ printk("acx: cannot reserve PCI memory region 2\n");
++ goto fail_request_mem_region2;
++ }
++
++ mem1 = ioremap(phymem1, mem_region1_size);
++ if (NULL == mem1) {
++ printk("acx: ioremap() FAILED\n");
++ goto fail_ioremap1;
++ }
++
++ mem2 = ioremap(phymem2, mem_region2_size);
++ if (NULL == mem2) {
++ printk("acx: ioremap() #2 FAILED\n");
++ goto fail_ioremap2;
++ }
++
++ /* Log the device */
++ printk("acx: found %s-based wireless network card at %s, irq:%d, "
++ "phymem1:0x%lX, phymem2:0x%lX, mem1:0x%p, mem1_size:%ld, "
++ "mem2:0x%p, mem2_size:%ld\n",
++ chip_name, pci_name(pdev), pdev->irq, phymem1, phymem2,
++ mem1, mem_region1_size,
++ mem2, mem_region2_size);
++ acxlog(L_ANY, "initial debug setting is 0x%04X\n", acx_debug);
++
++ if (0 == pdev->irq) {
++ printk("acx: can't use IRQ 0\n");
++ goto fail_irq;
++ }
++
++ dev = alloc_netdev(sizeof(wlandevice_t), "wlan%d", acx_netdev_init);
++ /* (NB: memsets to 0 entire area) */
++ if (!dev) {
++ printk("acx: no memory for netdevice structure\n");
++ goto fail_alloc_netdev;
++ }
++
++ ether_setup(dev);
++ dev->open = &acx_e_open;
++ dev->stop = &acx_e_close;
++ dev->hard_start_xmit = &acx_i_start_xmit;
++ dev->get_stats = &acx_e_get_stats;
++ dev->get_wireless_stats = &acx_e_get_wireless_stats;
++#if WIRELESS_EXT >= 13
++ dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++#else
++ dev->do_ioctl = &acx_e_ioctl_old;
++#endif
++ dev->set_multicast_list = &acx_i_set_multicast_list;
++ dev->tx_timeout = &acx_i_tx_timeout;
++ dev->change_mtu = &acx_change_mtu;
++ dev->watchdog_timeo = 4 * HZ;
++ dev->irq = pdev->irq;
++ dev->base_addr = pci_resource_start(pdev, 0);
++
++ priv = netdev_priv(dev);
++ spin_lock_init(&priv->lock); /* initial state: unlocked */
++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */
++ sema_init(&priv->sem, 1); /* initial state: 1 (upped) */
++ /* since nobody can see new netdev yet, we can as well
++ ** just _presume_ that we're under sem (instead of actually taking it): */
++ /* acx_sem_lock(priv); */
++ priv->pdev = pdev;
++ priv->dev_type = DEVTYPE_PCI;
++ priv->chip_type = chip_type;
++ priv->chip_name = chip_name;
++ priv->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111;
++ priv->membase = phymem1;
++ priv->iobase = mem1;
++ priv->membase2 = phymem2;
++ priv->iobase2 = mem2;
++ /* to find crashes due to weird driver access
++ * to unconfigured interface (ifup) */
++ priv->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
++
++#ifdef NONESSENTIAL_FEATURES
++ acx_show_card_eeprom_id(priv);
++#endif /* NONESSENTIAL_FEATURES */
++
++ /* now we have our device, so make sure the kernel doesn't try
++ * to send packets even though we're not associated to a network yet */
++ acx_stop_queue(dev, "after setup");
++
++#ifdef SET_MODULE_OWNER
++ SET_MODULE_OWNER(dev);
++#endif
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70)
++ /* this define and its netdev member exist since 2.5.70 */
++ SET_NETDEV_DEV(dev, &pdev->dev);
++#endif
++
++ /* register new dev in linked list */
++ acx_s_device_chain_add(dev);
++
++ acxlog(L_IRQ|L_INIT, "using IRQ %d\n", pdev->irq);
++
++ /* need to be able to restore PCI state after a suspend */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
++ /* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced this shorter version,
++ then it made its way into 2.6.10 */
++ pci_save_state(pdev);
++#else
++ pci_save_state(pdev, priv->pci_state);
++#endif
++
++ /* NB: acx_read_reg() reads may return bogus data before reset_dev().
++ ** acx100 seems to be more affected than acx111 */
++ if (OK != acx_s_reset_dev(dev)) {
++ goto fail_reset;
++ }
++
++ /* ok, basic setup is finished, now start initialising the card */
++
++ if (OK != acx_read_eeprom_offset(priv, 0x05, &priv->eeprom_version)) {
++ goto fail_read_eeprom_version;
++ }
++
++ if (OK != acx_s_init_mac(dev)) {
++ printk("acx: init_mac() FAILED\n");
++ goto fail_init_mac;
++ }
++ if (OK != acx_s_set_defaults(priv)) {
++ printk("acx: set_defaults() FAILED\n");
++ goto fail_set_defaults;
++ }
++
++ /* needs to be after acx_s_init_mac() due to necessary init stuff */
++ acx_s_get_firmware_version(priv);
++
++ acx_display_hardware_details(priv);
++
++ pci_set_drvdata(pdev, dev);
++
++ /* ...and register the card, AFTER everything else has been set up,
++ * since otherwise an ioctl could step on our feet due to
++ * firmware operations happening in parallel or uninitialized data */
++ err = register_netdev(dev);
++ if (OK != err) {
++ printk("acx: register_netdev() FAILED: %d\n", err);
++ goto fail_register_netdev;
++ }
++
++ acx_carrier_off(dev, "on probe");
++
++#ifdef CONFIG_PROC_FS
++ if (OK != acx_proc_register_entries(dev)) {
++ goto fail_proc_register_entries;
++ }
++#endif
++
++ /* after register_netdev() userspace may start working with dev
++ * (in particular, on other CPUs), we only need to up the sem */
++ /* acx_sem_unlock(priv); */
++
++ printk("acx "WLAN_RELEASE": net device %s, driver compiled "
++ "against wireless extensions %d and Linux %s\n",
++ dev->name, WIRELESS_EXT, UTS_RELEASE);
++
++#if CMD_DISCOVERY
++ great_inquisitor(priv);
++#endif
++
++ result = OK;
++ goto done;
++
++ /* error paths: undo everything in reverse order... */
++
++#ifdef CONFIG_PROC_FS
++fail_proc_register_entries:
++
++ if (priv->dev_state_mask & ACX_STATE_IFACE_UP)
++ acx_s_down(dev);
++
++ unregister_netdev(dev);
++
++ /* after unregister_netdev() userspace is guaranteed to finish
++ * working with it. netdev does not exist anymore.
++ * For paranoid reasons I am taking sem anyway */
++ acx_sem_lock(priv);
++#endif
++
++fail_register_netdev:
++
++ acx_s_delete_dma_regions(priv);
++ pci_set_drvdata(pdev, NULL);
++
++fail_set_defaults:
++fail_init_mac:
++fail_read_eeprom_version:
++fail_reset:
++
++ acx_s_device_chain_remove(dev);
++ free_netdev(dev);
++fail_alloc_netdev:
++fail_irq:
++
++ iounmap(mem2);
++fail_ioremap2:
++
++ iounmap(mem1);
++fail_ioremap1:
++
++ release_mem_region(pci_resource_start(pdev, mem_region2),
++ pci_resource_len(pdev, mem_region2));
++fail_request_mem_region2:
++
++ release_mem_region(pci_resource_start(pdev, mem_region1),
++ pci_resource_len(pdev, mem_region1));
++fail_request_mem_region1:
++fail_unknown_chiptype:
++
++ pci_disable_device(pdev);
++fail_pci_enable_device:
++
++ pci_set_power_state(pdev, 3);
++
++done:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_remove_pci
++*
++* Deallocate PCI resources for the ACX100 chip.
++*
++* This should NOT execute any other hardware operations on the card,
++* since the card might already be ejected. Instead, that should be done
++* in cleanup_module, since the card is most likely still available there.
++*
++* Arguments:
++* pdev ptr to PCI device structure containing info about
++* PCI configuration.
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static void __devexit
++acx_e_remove_pci(struct pci_dev *pdev)
++{
++ struct net_device *dev;
++ wlandevice_t *priv;
++ unsigned long mem_region1, mem_region2;
++
++ FN_ENTER;
++
++ dev = (struct net_device *) pci_get_drvdata(pdev);
++ if (!dev) {
++ acxlog(L_DEBUG, "%s: card is unused. Skipping any release code\n",
++ __func__);
++ goto end;
++ }
++
++ priv = netdev_priv(dev);
++
++ /* unregister the device to not let the kernel
++ * (e.g. ioctls) access a half-deconfigured device
++ * NB: this will cause acx_e_close() to be called,
++ * thus we shouldn't call it under sem! */
++ acxlog(L_INIT, "removing device %s\n", dev->name);
++ unregister_netdev(dev);
++
++ /* unregister_netdev ensures that no references to us left.
++ * For paranoid reasons we continue to follow the rules */
++ acx_sem_lock(priv);
++
++ if (IS_ACX100(priv)) {
++ mem_region1 = PCI_ACX100_REGION1;
++ mem_region2 = PCI_ACX100_REGION2;
++ } else {
++ mem_region1 = PCI_ACX111_REGION1;
++ mem_region2 = PCI_ACX111_REGION2;
++ }
++
++#ifdef CONFIG_PROC_FS
++ acx_proc_unregister_entries(dev);
++#endif
++
++ /* find our PCI device in the global acx list and remove it */
++ acx_s_device_chain_remove(dev);
++
++ if (priv->dev_state_mask & ACX_STATE_IFACE_UP)
++ acx_s_down(dev);
++
++ CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++
++ acx_s_delete_dma_regions(priv);
++
++ /* finally, clean up PCI bus state */
++ if (priv->iobase) iounmap(priv->iobase);
++ if (priv->iobase2) iounmap(priv->iobase2);
++
++ release_mem_region(pci_resource_start(pdev, mem_region1),
++ pci_resource_len(pdev, mem_region1));
++
++ release_mem_region(pci_resource_start(pdev, mem_region2),
++ pci_resource_len(pdev, mem_region2));
++
++ pci_disable_device(pdev);
++
++ /* remove dev registration */
++ pci_set_drvdata(pdev, NULL);
++
++ /* Free netdev (quite late,
++ * since otherwise we might get caught off-guard
++ * by a netdev timeout handler execution
++ * expecting to see a working dev...)
++ * But don't use free_netdev() here,
++ * it's supported by newer kernels only */
++ free_netdev(dev);
++
++ /* put device into ACPI D3 mode (shutdown) */
++ pci_set_power_state(pdev, 3);
++
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++#ifdef CONFIG_PM
++static int if_was_up = 0; /* FIXME: HACK, do it correctly sometime instead */
++static int
++acx_e_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ struct net_device *dev = pci_get_drvdata(pdev);
++ wlandevice_t *priv = netdev_priv(dev);
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ printk("acx: experimental suspend handler called for %p\n", priv);
++ if (netif_device_present(dev)) {
++ if_was_up = 1;
++ acx_s_down(dev);
++ }
++ else
++ if_was_up = 0;
++
++ netif_device_detach(dev); /* This one cannot sleep */
++ acx_s_delete_dma_regions(priv);
++
++ acx_sem_unlock(priv);
++
++ FN_EXIT0;
++ return OK;
++}
++
++static int
++acx_e_resume(struct pci_dev *pdev)
++{
++ struct net_device *dev;
++ wlandevice_t *priv;
++
++ printk(KERN_WARNING "rsm: resume\n");
++ dev = pci_get_drvdata(pdev);
++ printk(KERN_WARNING "rsm: got dev\n");
++
++ if (!netif_running(dev))
++ return 0;
++
++ priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ printk(KERN_WARNING "rsm: got priv\n");
++ FN_ENTER;
++ printk("acx: experimental resume handler called for %p!\n", priv);
++ pci_set_power_state(pdev, 0);
++ acxlog(L_DEBUG, "rsm: power state set\n");
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
++ /* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced this shorter version,
++ then it made its way into 2.6.10 */
++ pci_restore_state(pdev);
++#else
++ pci_restore_state(pdev, priv->pci_state);
++#endif
++ acxlog(L_DEBUG, "rsm: PCI state restored\n");
++ acx_s_reset_dev(dev);
++ acxlog(L_DEBUG, "rsm: device reset done\n");
++
++ if (OK != acx_s_init_mac(dev)) {
++ printk("rsm: init_mac FAILED\n");
++ goto fail;
++ }
++ acxlog(L_DEBUG, "rsm: init MAC done\n");
++
++ if (1 == if_was_up)
++ acx_s_up(dev);
++ acxlog(L_DEBUG, "rsm: acx up\n");
++
++ /* now even reload all card parameters as they were before suspend,
++ * and possibly be back in the network again already :-)
++ * FIXME: should this be done in that scheduled task instead?? */
++ if (ACX_STATE_IFACE_UP & priv->dev_state_mask)
++ acx_s_update_card_settings(priv, 0, 1);
++ acxlog(L_DEBUG, "rsm: settings updated\n");
++ netif_device_attach(dev);
++ acxlog(L_DEBUG, "rsm: device attached\n");
++fail: /* we need to return OK here anyway, right? */
++ acx_sem_unlock(priv);
++ FN_EXIT0;
++ return OK;
++}
++#endif /* CONFIG_PM */
++
++
++/*----------------------------------------------------------------
++* acx_s_up
++*
++* Side effects:
++* - Enables on-card interrupt requests
++* - calls acx_start
++* Call context:
++* - process thread
++* Comment:
++* This function is called by acx_open (when ifconfig sets the
++* device as up).
++*----------------------------------------------------------------*/
++static void
++acx_s_up(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++ acx_l_enable_irq(priv);
++ acx_unlock(priv, flags);
++
++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers
++ ** used to use it. But we don't do that anymore, our OS
++ ** has reliable software timers */
++ init_timer(&priv->mgmt_timer);
++ priv->mgmt_timer.function = acx_i_timer;
++ priv->mgmt_timer.data = (unsigned long)priv;
++
++ /* Need to set ACX_STATE_IFACE_UP first, or else
++ ** timer won't be started by acx_set_status() */
++ SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* actual scan cmd will happen in start() */
++ acx_set_status(priv, ACX_STATUS_1_SCANNING); break;
++ case ACX_MODE_3_AP:
++ case ACX_MODE_MONITOR:
++ acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); break;
++ }
++
++ acx_s_start(priv);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_down
++*
++* Side effects:
++* - disables on-card interrupt request
++* Call context:
++* process thread
++* Comment:
++* this disables the netdevice
++*----------------------------------------------------------------*/
++static void
++acx_s_down(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Disable IRQs first, so that IRQs cannot race with us */
++ acx_lock(priv, flags);
++ acx_l_disable_irq(priv);
++ acx_unlock(priv, flags);
++
++ /* we really don't want to have an asynchronous tasklet disturb us
++ ** after something vital for its job has been shut down, so
++ ** end all remaining work now.
++ **
++ ** NB: carrier_off (done by set_status below) would lead to
++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK().
++ ** That's why we do FLUSH first.
++ **
++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK()
++ ** waits for acx_e_after_interrupt_task to complete if it is running
++ ** on another CPU, but acx_e_after_interrupt_task
++ ** will sleep on sem forever, because it is taken by us!
++ ** Work around that by temporary sem unlock.
++ ** This will fail miserably if we'll be hit by concurrent
++ ** iwconfig or something in between. TODO! */
++ acx_sem_unlock(priv);
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(priv);
++
++ /* This is possible:
++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task ->
++ ** -> set_status(ASSOCIATED) -> wake_queue()
++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK
++ ** lock/unlock is just paranoia, maybe not needed */
++ acx_lock(priv, flags);
++ acx_stop_queue(dev, "during close");
++ acx_set_status(priv, ACX_STATUS_0_STOPPED);
++ acx_unlock(priv, flags);
++
++ /* kernel/timer.c says it's illegal to del_timer_sync()
++ ** a timer which restarts itself. We guarantee this cannot
++ ** ever happen because acx_i_timer() never does this if
++ ** status is ACX_STATUS_0_STOPPED */
++ del_timer_sync(&priv->mgmt_timer);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_open
++*
++* WLAN device open method. Called from p80211netdev when kernel
++* device open (start) method is called in response to the
++* SIOCSIFFLAGS ioctl changing the flags bit IFF_UP
++* from clear to set.
++*
++* Returns:
++* 0 success
++* >0 f/w reported error
++* <0 driver reported error
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static int
++acx_e_open(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ int result = OK;
++
++ FN_ENTER;
++
++ acxlog(L_INIT, "module count++\n");
++ WLAN_MOD_INC_USE_COUNT;
++
++ acx_sem_lock(priv);
++
++ acx_init_task_scheduler(priv);
++
++ /* request shared IRQ handler */
++ if (request_irq(dev->irq, acx_i_interrupt, SA_SHIRQ, dev->name, dev)) {
++ printk("%s: request_irq FAILED\n", dev->name);
++ result = -EAGAIN;
++ goto done;
++ }
++ acxlog(L_DEBUG|L_IRQ, "request_irq %d successful\n", dev->irq);
++
++ /* ifup device */
++ acx_s_up(dev);
++
++ /* We don't currently have to do anything else.
++ * The setup of the MAC should be subsequently completed via
++ * the mlme commands.
++ * Higher layers know we're ready from dev->start==1 and
++ * dev->tbusy==0. Our rx path knows to pass up received/
++ * frames because of dev->flags&IFF_UP is true.
++ */
++done:
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_close
++*
++* WLAN device close method. Called from network core when kernel
++* device close method is called in response to the
++* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
++* from set to clear.
++* (i.e. called for "ifconfig DEV down")
++*
++* Returns:
++* 0 success
++* >0 f/w reported error
++* <0 driver reported error
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++static int
++acx_e_close(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ /* ifdown device */
++ CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++ if (netif_device_present(dev)) {
++ acx_s_down(dev);
++ }
++
++ /* disable all IRQs, release shared IRQ handler */
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff);
++ acx_write_reg16(priv, IO_ACX_FEMR, 0x0);
++ free_irq(dev->irq, dev);
++
++ /* We currently don't have to do anything else.
++ * Higher layers know we're not ready from dev->start==0 and
++ * dev->tbusy==1. Our rx path knows to not pass up received
++ * frames because of dev->flags&IFF_UP is false.
++ */
++ acxlog(L_INIT, "module count--\n");
++ WLAN_MOD_DEC_USE_COUNT;
++
++ acx_sem_unlock(priv);
++
++ acxlog(L_INIT, "closed device\n");
++ FN_EXIT0;
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_i_tx_timeout
++*
++* Called from network core. Must not sleep!
++*----------------------------------------------------------------*/
++static void
++acx_i_tx_timeout(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ unsigned int tx_num_cleaned;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++
++ /* clean processed tx descs, they may have been completely full */
++ tx_num_cleaned = acx_l_clean_tx_desc(priv);
++
++ /* nothing cleaned, yet (almost) no free buffers available?
++ * --> clean all tx descs, no matter which status!!
++ * Note that I strongly suspect that doing emergency cleaning
++ * may confuse the firmware. This is a last ditch effort to get
++ * ANYTHING to work again...
++ *
++ * TODO: it's best to simply reset & reinit hw from scratch...
++ */
++ if ((priv->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) {
++ printk("%s: FAILED to free any of the many full tx buffers. "
++ "Switching to emergency freeing. "
++ "Please report!\n", dev->name);
++ acx_l_clean_tx_desc_emergency(priv);
++ }
++
++ if (acx_queue_stopped(dev) && (ACX_STATUS_4_ASSOCIATED == priv->status))
++ acx_wake_queue(dev, "after tx timeout");
++
++ /* stall may have happened due to radio drift, so recalib radio */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* do unimportant work last */
++ printk("%s: tx timeout!\n", dev->name);
++ priv->stats.tx_errors++;
++
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_get_stats
++*----------------------------------------------------------------*/
++static struct net_device_stats*
++acx_e_get_stats(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ return &priv->stats;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_get_wireless_stats
++*----------------------------------------------------------------*/
++static struct iw_statistics*
++acx_e_get_wireless_stats(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ return &priv->wstats;
++}
++
++
++/*----------------------------------------------------------------
++* acx_i_set_multicast_list
++* FIXME: most likely needs refinement
++*----------------------------------------------------------------*/
++static void
++acx_i_set_multicast_list(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++
++ /* firmwares don't have allmulti capability,
++ * so just use promiscuous mode instead in this case. */
++ if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
++ SET_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ CLEAR_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(priv->set_mask, SET_RXCONFIG);
++ /* let kernel know in case *we* needed to set promiscuous */
++ dev->flags |= (IFF_PROMISC|IFF_ALLMULTI);
++ } else {
++ CLEAR_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ SET_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(priv->set_mask, SET_RXCONFIG);
++ dev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
++ }
++
++ /* cannot update card settings directly here, atomic context */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++
++static void
++acx_l_update_link_quality_led(wlandevice_t *priv)
++{
++ int qual;
++
++ qual = acx_signal_determine_quality(priv->wstats.qual.level, priv->wstats.qual.noise);
++ if (qual > priv->brange_max_quality)
++ qual = priv->brange_max_quality;
++
++ if (time_after(jiffies, priv->brange_time_last_state_change +
++ (HZ/2 - HZ/2 * (unsigned long) qual/priv->brange_max_quality ) )) {
++ acx_l_power_led(priv, (priv->brange_last_state == 0));
++ priv->brange_last_state ^= 1; /* toggle */
++ priv->brange_time_last_state_change = jiffies;
++ }
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_enable_irq
++*----------------------------------------------------------------*/
++static void
++acx_l_enable_irq(wlandevice_t *priv)
++{
++ FN_ENTER;
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask);
++ acx_write_reg16(priv, IO_ACX_FEMR, 0x8000);
++ priv->irqs_active = 1;
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_disable_irq
++*----------------------------------------------------------------*/
++static void
++acx_l_disable_irq(wlandevice_t *priv)
++{
++ FN_ENTER;
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask_off);
++ acx_write_reg16(priv, IO_ACX_FEMR, 0x0);
++ priv->irqs_active = 0;
++ FN_EXIT0;
++}
++
++/* scan is complete. all frames now on the receive queue are valid */
++#define INFO_SCAN_COMPLETE 0x0001
++#define INFO_WEP_KEY_NOT_FOUND 0x0002
++/* hw has been reset as the result of a watchdog timer timeout */
++#define INFO_WATCH_DOG_RESET 0x0003
++/* failed to send out NULL frame from PS mode notification to AP */
++/* recommended action: try entering 802.11 PS mode again */
++#define INFO_PS_FAIL 0x0004
++/* encryption/decryption process on a packet failed */
++#define INFO_IV_ICV_FAILURE 0x0005
++
++static void
++acx_l_handle_info_irq(wlandevice_t *priv)
++{
++#if ACX_DEBUG
++ static const char * const info_type_msg[] = {
++ "(unknown)",
++ "scan complete",
++ "WEP key not found",
++ "internal watchdog reset was done",
++ "failed to send powersave (NULL frame) notification to AP",
++ "encrypt/decrypt on a packet has failed",
++ "TKIP tx keys disabled",
++ "TKIP rx keys disabled",
++ "TKIP rx: key ID not found",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "TKIP IV value exceeds thresh"
++ };
++#endif
++ acx_read_info_status(priv);
++ acxlog(L_IRQ, "got Info IRQ: status 0x%04X type 0x%04X: %s\n",
++ priv->info_status, priv->info_type,
++ info_type_msg[(priv->info_type >= VEC_SIZE(info_type_msg)) ?
++ 0 : priv->info_type]
++ );
++}
++
++
++/*----------------------------------------------------------------
++* acx_i_interrupt
++*
++* IRQ handler (atomic context, must not sleep, blah, blah)
++*----------------------------------------------------------------*/
++static void
++acx_log_unusual_irq(u16 irqtype) {
++ /*
++ if (!printk_ratelimit())
++ return;
++ */
++
++ printk("acx: got");
++ if (irqtype & HOST_INT_RX_DATA) {
++ printk(" Rx_Data");
++ }
++ /* HOST_INT_TX_COMPLETE */
++ if (irqtype & HOST_INT_TX_XFER) {
++ printk(" Tx_Xfer");
++ }
++ /* HOST_INT_RX_COMPLETE */
++ if (irqtype & HOST_INT_DTIM) {
++ printk(" DTIM");
++ }
++ if (irqtype & HOST_INT_BEACON) {
++ printk(" Beacon");
++ }
++ if (irqtype & HOST_INT_TIMER) {
++ acxlog(L_IRQ, " Timer");
++ }
++ if (irqtype & HOST_INT_KEY_NOT_FOUND) {
++ printk(" Key_Not_Found");
++ }
++ if (irqtype & HOST_INT_IV_ICV_FAILURE) {
++ printk(" IV_ICV_Failure");
++ }
++ /* HOST_INT_CMD_COMPLETE */
++ /* HOST_INT_INFO */
++ if (irqtype & HOST_INT_OVERFLOW) {
++ printk(" Overflow");
++ }
++ if (irqtype & HOST_INT_PROCESS_ERROR) {
++ printk(" Process_Error");
++ }
++ /* HOST_INT_SCAN_COMPLETE */
++ if (irqtype & HOST_INT_FCS_THRESHOLD) {
++ printk(" FCS_Threshold");
++ }
++ if (irqtype & HOST_INT_UNKNOWN) {
++ printk(" Unknown");
++ }
++ printk(" IRQ(s)\n");
++}
++
++static irqreturn_t
++acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ wlandevice_t *priv;
++ unsigned long flags;
++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY;
++ u16 irqtype, unmasked;
++
++ priv = (wlandevice_t *) (((netdevice_t *) dev_id)->priv);
++
++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway.
++ * I am paranoid */
++ acx_lock(priv, flags);
++
++ unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR);
++ if (unlikely(0xffff == unmasked)) {
++ /* 0xffff value hints at missing hardware,
++ * so don't do anything.
++ * FIXME: that's not very clean - maybe we are able to
++ * establish a flag which definitely tells us that some
++ * hardware is absent and which we could check here?
++ * Hmm, but other drivers do the very same thing... */
++ acxlog(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n");
++ goto none;
++ }
++
++ /* We will check only "interesting" IRQ types */
++ irqtype = unmasked & ~priv->irq_mask;
++ if (!irqtype) {
++ /* We are on a shared IRQ line and it wasn't our IRQ */
++ acxlog(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n",
++ unmasked, priv->irq_mask);
++ goto none;
++ }
++
++ /* Done here because IRQ_NONEs taking three lines of log
++ ** drive me crazy */
++ FN_ENTER;
++
++#define IRQ_ITERATE 1
++#if IRQ_ITERATE
++if (jiffies != priv->irq_last_jiffies) {
++ priv->irq_loops_this_jiffy = 0;
++ priv->irq_last_jiffies = jiffies;
++}
++
++/* safety condition; we'll normally abort loop below
++ * in case no IRQ type occurred */
++while (--irqcount) {
++#endif
++ /* ACK all IRQs asap */
++ acx_write_reg16(priv, IO_ACX_IRQ_ACK, 0xffff);
++
++ acxlog(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n",
++ unmasked, priv->irq_mask, irqtype);
++
++ /* Handle most important IRQ types first */
++ if (irqtype & HOST_INT_RX_COMPLETE) {
++ acxlog(L_IRQ, "got Rx_Complete IRQ\n");
++ acx_l_process_rx_desc(priv);
++ }
++ if (irqtype & HOST_INT_TX_COMPLETE) {
++ acxlog(L_IRQ, "got Tx_Complete IRQ\n");
++ /* don't clean up on each Tx complete, wait a bit
++ * unless we're going towards full, in which case
++ * we do it immediately, too (otherwise we might lockup
++ * with a full Tx buffer if we go into
++ * acx_l_clean_tx_desc() at a time when we won't wakeup
++ * the net queue in there for some reason...) */
++ if (priv->tx_free <= TX_START_CLEAN) {
++#if TX_CLEANUP_IN_SOFTIRQ
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_TX_CLEANUP);
++#else
++ acx_l_clean_tx_desc(priv);
++#endif
++ }
++ }
++
++ /* Less frequent ones */
++ if (irqtype & (0
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_SCAN_COMPLETE
++ )) {
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ acxlog(L_IRQ, "got Command_Complete IRQ\n");
++ /* save the state for the running issue_cmd() */
++ SET_BIT(priv->irq_status, HOST_INT_CMD_COMPLETE);
++ }
++ if (irqtype & HOST_INT_INFO) {
++ acx_l_handle_info_irq(priv);
++ }
++ if (irqtype & HOST_INT_SCAN_COMPLETE) {
++ acxlog(L_IRQ, "got Scan_Complete IRQ\n");
++ /* need to do that in process context */
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ /* remember that fw is not scanning anymore */
++ SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE);
++ }
++ }
++
++ /* These we just log, but either they happen rarely
++ * or we keep them masked out */
++ if (irqtype & (0
++ | HOST_INT_RX_DATA
++ /* | HOST_INT_TX_COMPLETE */
++ | HOST_INT_TX_XFER
++ /* | HOST_INT_RX_COMPLETE */
++ | HOST_INT_DTIM
++ | HOST_INT_BEACON
++ | HOST_INT_TIMER
++ | HOST_INT_KEY_NOT_FOUND
++ | HOST_INT_IV_ICV_FAILURE
++ /* | HOST_INT_CMD_COMPLETE */
++ /* | HOST_INT_INFO */
++ | HOST_INT_OVERFLOW
++ | HOST_INT_PROCESS_ERROR
++ /* | HOST_INT_SCAN_COMPLETE */
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ )) {
++ acx_log_unusual_irq(irqtype);
++ }
++
++#if IRQ_ITERATE
++ unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR);
++ irqtype = unmasked & ~priv->irq_mask;
++ /* Bail out if no new IRQ bits or if all are masked out */
++ if (!irqtype)
++ break;
++
++ if (unlikely(++priv->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) {
++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n");
++ /* Looks like card floods us with IRQs! Try to stop that */
++ acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff);
++ /* This will short-circuit all future attempts to handle IRQ.
++ * We cant do much more... */
++ priv->irq_mask = 0;
++ break;
++ }
++}
++#endif
++ /* Routine to perform blink with range */
++ if (unlikely(priv->led_power == 2))
++ acx_l_update_link_quality_led(priv);
++
++/* handled: */
++ /* acx_write_flush(priv); - not needed, last op was read anyway */
++ acx_unlock(priv, flags);
++ FN_EXIT0;
++ return IRQ_HANDLED;
++
++none:
++ acx_unlock(priv, flags);
++ return IRQ_NONE;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_power_led
++*----------------------------------------------------------------*/
++void
++acx_l_power_led(wlandevice_t *priv, int enable)
++{
++ u16 gpio_pled = IS_ACX111(priv) ? 0x0040 : 0x0800;
++
++ /* A hack. Not moving message rate limiting to priv->xxx
++ * (it's only a debug message after all) */
++ static int rate_limit = 0;
++
++ if (rate_limit++ < 3)
++ acxlog(L_IOCTL, "Please report in case toggling the power "
++ "LED doesn't work for your card!\n");
++ if (enable)
++ acx_write_reg16(priv, IO_ACX_GPIO_OUT,
++ acx_read_reg16(priv, IO_ACX_GPIO_OUT) & ~gpio_pled);
++ else
++ acx_write_reg16(priv, IO_ACX_GPIO_OUT,
++ acx_read_reg16(priv, IO_ACX_GPIO_OUT) | gpio_pled);
++}
++
++
++/***********************************************************************
++** Ioctls
++*/
++
++/***********************************************************************
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++#if ACX_DEBUG
++ wlandevice_t *priv = netdev_priv(dev);
++ rxdesc_t *rxdesc;
++ txdesc_t *txdesc;
++ rxhostdesc_t *rxhostdesc;
++ txhostdesc_t *txhostdesc;
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ unsigned long flags;
++ int i;
++ char memmap[0x34];
++ char rxconfig[0x8];
++ char fcserror[0x8];
++ char ratefallback[0x5];
++
++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) )
++ return OK;
++ /* using printk() since we checked debug flag already */
++
++ acx_sem_lock(priv);
++
++ if (!IS_ACX111(priv)) {
++ printk("acx111-specific function called "
++ "with non-acx111 chip, aborting\n");
++ goto end_ok;
++ }
++
++ /* get Acx111 Memory Configuration */
++ memset(&memconf, 0, sizeof(memconf));
++ /* BTW, fails with 12 (Write only) error code.
++ ** Retained for easy testing of issue_cmd error handling :) */
++ acx_s_interrogate(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG);
++
++ /* get Acx111 Queue Configuration */
++ memset(&queueconf, 0, sizeof(queueconf));
++ acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++
++ /* get Acx111 Memory Map */
++ memset(memmap, 0, sizeof(memmap));
++ acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP);
++
++ /* get Acx111 Rx Config */
++ memset(rxconfig, 0, sizeof(rxconfig));
++ acx_s_interrogate(priv, &rxconfig, ACX1xx_IE_RXCONFIG);
++
++ /* get Acx111 fcs error count */
++ memset(fcserror, 0, sizeof(fcserror));
++ acx_s_interrogate(priv, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT);
++
++ /* get Acx111 rate fallback */
++ memset(ratefallback, 0, sizeof(ratefallback));
++ acx_s_interrogate(priv, &ratefallback, ACX1xx_IE_RATE_FALLBACK);
++
++ /* force occurrence of a beacon interrupt */
++ /* TODO: comment why is this necessary */
++ acx_write_reg16(priv, IO_ACX_HINT_TRIG, HOST_INT_BEACON);
++
++ /* dump Acx111 Mem Configuration */
++ printk("dump mem config:\n"
++ "data read: %d, struct size: %d\n"
++ "Number of stations: %1X\n"
++ "Memory block size: %1X\n"
++ "tx/rx memory block allocation: %1X\n"
++ "count rx: %X / tx: %X queues\n"
++ "options %1X\n"
++ "fragmentation %1X\n"
++ "Rx Queue 1 Count Descriptors: %X\n"
++ "Rx Queue 1 Host Memory Start: %X\n"
++ "Tx Queue 1 Count Descriptors: %X\n"
++ "Tx Queue 1 Attributes: %X\n",
++ memconf.len, (int) sizeof(memconf),
++ memconf.no_of_stations,
++ memconf.memory_block_size,
++ memconf.tx_rx_memory_block_allocation,
++ memconf.count_rx_queues, memconf.count_tx_queues,
++ memconf.options,
++ memconf.fragmentation,
++ memconf.rx_queue1_count_descs,
++ acx2cpu(memconf.rx_queue1_host_rx_start),
++ memconf.tx_queue1_count_descs,
++ memconf.tx_queue1_attributes);
++
++ /* dump Acx111 Queue Configuration */
++ printk("dump queue head:\n"
++ "data read: %d, struct size: %d\n"
++ "tx_memory_block_address (from card): %X\n"
++ "rx_memory_block_address (from card): %X\n"
++ "rx1_queue address (from card): %X\n"
++ "tx1_queue address (from card): %X\n"
++ "tx1_queue attributes (from card): %X\n",
++ queueconf.len, (int) sizeof(queueconf),
++ queueconf.tx_memory_block_address,
++ queueconf.rx_memory_block_address,
++ queueconf.rx1_queue_address,
++ queueconf.tx1_queue_address,
++ queueconf.tx1_attributes);
++
++ /* dump Acx111 Mem Map */
++ printk("dump mem map:\n"
++ "data read: %d, struct size: %d\n"
++ "Code start: %X\n"
++ "Code end: %X\n"
++ "WEP default key start: %X\n"
++ "WEP default key end: %X\n"
++ "STA table start: %X\n"
++ "STA table end: %X\n"
++ "Packet template start: %X\n"
++ "Packet template end: %X\n"
++ "Queue memory start: %X\n"
++ "Queue memory end: %X\n"
++ "Packet memory pool start: %X\n"
++ "Packet memory pool end: %X\n"
++ "iobase: %p\n"
++ "iobase2: %p\n",
++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap),
++ *((u32 *)&memmap[0x04]),
++ *((u32 *)&memmap[0x08]),
++ *((u32 *)&memmap[0x0C]),
++ *((u32 *)&memmap[0x10]),
++ *((u32 *)&memmap[0x14]),
++ *((u32 *)&memmap[0x18]),
++ *((u32 *)&memmap[0x1C]),
++ *((u32 *)&memmap[0x20]),
++ *((u32 *)&memmap[0x24]),
++ *((u32 *)&memmap[0x28]),
++ *((u32 *)&memmap[0x2C]),
++ *((u32 *)&memmap[0x30]),
++ priv->iobase,
++ priv->iobase2);
++
++ /* dump Acx111 Rx Config */
++ printk("dump rx config:\n"
++ "data read: %d, struct size: %d\n"
++ "rx config: %X\n"
++ "rx filter config: %X\n",
++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig),
++ *((u16 *)&rxconfig[0x04]),
++ *((u16 *)&rxconfig[0x06]));
++
++ /* dump Acx111 fcs error */
++ printk("dump fcserror:\n"
++ "data read: %d, struct size: %d\n"
++ "fcserrors: %X\n",
++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror),
++ *((u32 *)&fcserror[0x04]));
++
++ /* dump Acx111 rate fallback */
++ printk("dump rate fallback:\n"
++ "data read: %d, struct size: %d\n"
++ "ratefallback: %X\n",
++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback),
++ *((u8 *)&ratefallback[0x04]));
++
++ /* protect against IRQ */
++ acx_lock(priv, flags);
++
++ /* dump acx111 internal rx descriptor ring buffer */
++ rxdesc = priv->rxdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump internal rxdesc %d:\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n"
++ "RxStatus (dynamic) 0x%X\n"
++ "Mod/Pre (dynamic) 0x%X\n",
++ i,
++ rxdesc,
++ acx2cpu(rxdesc->pNextDesc),
++ acx2cpu(rxdesc->ACXMemPtr),
++ rxdesc->Ctl_8,
++ rxdesc->rate,
++ rxdesc->error,
++ rxdesc->SNR);
++ rxdesc++;
++ }
++
++ /* dump host rx descriptor ring buffer */
++
++ rxhostdesc = priv->rxhostdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump host rxdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ rxhostdesc,
++ acx2cpu(rxhostdesc->data_phy),
++ rxhostdesc->data_offset,
++ le16_to_cpu(rxhostdesc->Ctl_16),
++ le16_to_cpu(rxhostdesc->length),
++ acx2cpu(rxhostdesc->desc_phy_next),
++ rxhostdesc->Status);
++ rxhostdesc++;
++ }
++
++ /* dump acx111 internal tx descriptor ring buffer */
++ txdesc = priv->txdesc_start;
++
++ /* loop over complete transmit pool */
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ printk("\ndump internal txdesc %d:\n"
++ "size 0x%X\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "host mem pointer (dynamic) 0x%X\n"
++ "length (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "CTL2 (dynamic) 0x%X\n"
++ "Status (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n",
++ i,
++ (int) sizeof(struct txdesc),
++ txdesc,
++ acx2cpu(txdesc->pNextDesc),
++ acx2cpu(txdesc->AcxMemPtr),
++ acx2cpu(txdesc->HostMemPtr),
++ le16_to_cpu(txdesc->total_length),
++ txdesc->Ctl_8,
++ txdesc->Ctl2_8, txdesc->error,
++ txdesc->u.r1.rate);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++
++ /* dump host tx descriptor ring buffer */
++
++ txhostdesc = priv->txhostdesc_start;
++
++ /* loop over complete host send pool */
++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) {
++ printk("\ndump host txdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ txhostdesc,
++ acx2cpu(txhostdesc->data_phy),
++ txhostdesc->data_offset,
++ le16_to_cpu(txhostdesc->Ctl_16),
++ le16_to_cpu(txhostdesc->length),
++ acx2cpu(txhostdesc->desc_phy_next),
++ le32_to_cpu(txhostdesc->Status));
++ txhostdesc++;
++ }
++
++ /* acx_write_reg16(priv, 0xb4, 0x4); */
++
++ acx_unlock(priv, flags);
++end_ok:
++
++ acx_sem_unlock(priv);
++#endif /* ACX_DEBUG */
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acx100pci_ioctl_set_phy_amp_bias(
++ struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ u16 gpio_old;
++
++ if (!IS_ACX100(priv)) {
++ /* WARNING!!!
++ * Removing this check *might* damage
++ * hardware, since we're tweaking GPIOs here after all!!!
++ * You've been warned...
++ * WARNING!!! */
++ printk("acx: sorry, setting bias level for non-acx100 "
++ "is not supported yet\n");
++ return OK;
++ }
++
++ if (*extra > 7) {
++ printk("acx: invalid bias parameter, range is 0-7\n");
++ return -EINVAL;
++ }
++
++ acx_sem_lock(priv);
++
++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]:
++ * IRQ handler uses it to update LED */
++ acx_lock(priv, flags);
++ gpio_old = acx_read_reg16(priv, IO_ACX_GPIO_OUT);
++ acx_write_reg16(priv, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8));
++ acx_unlock(priv, flags);
++
++ acxlog(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old);
++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n",
++ dev->name,
++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra);
++
++ acx_sem_unlock(priv);
++
++ return OK;
++}
++
++
++/***************************************************************
++** acxpci_l_alloc_tx
++** Actually returns a txdesc_t* ptr
++*/
++tx_t*
++acxpci_l_alloc_tx(wlandevice_t* priv)
++{
++ struct txdesc *txdesc;
++ u8 ctl8;
++
++ FN_ENTER;
++
++ txdesc = get_txdesc(priv, priv->tx_head);
++ ctl8 = txdesc->Ctl_8;
++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_DONE))) {
++ /* whoops, descr at current index is not free, so probably
++ * ring buffer already full */
++ /* FIXME: this causes a deadlock situation (endless
++ * loop) in case the current descriptor remains busy,
++ * so handle it a bit better in the future!! */
++ printk("acx: BUG: tx_head->Ctl8=0x%02X, (0x%02X & "
++ "0x"DESC_CTL_DONE_STR") != 0x"DESC_CTL_HOSTOWN_STR
++ ": failed to find free tx descr\n",
++ ctl8, ctl8);
++ txdesc = NULL;
++ goto end;
++ }
++
++ priv->tx_free--;
++ acxlog(L_BUFT, "tx: got desc %u, %u remain\n",
++ priv->tx_head, priv->tx_free);
++
++/*
++ * This comment is probably not entirely correct, needs further discussion
++ * (restored commented-out code below to fix Tx ring buffer overflow,
++ * since it's much better to have a slightly less efficiently used ring
++ * buffer rather than one which easily overflows):
++ *
++ * This doesn't do anything other than limit our maximum number of
++ * buffers used at a single time (we might as well just declare
++ * TX_STOP_QUEUE less descriptors when we open up.) We should just let it
++ * slide here, and back off TX_STOP_QUEUE in acx_l_clean_tx_desc, when given the
++ * opportunity to let the queue start back up.
++ */
++ if (priv->tx_free < TX_STOP_QUEUE) {
++ acxlog(L_BUF, "stop queue (%u tx desc left)\n",
++ priv->tx_free);
++ acx_stop_queue(priv->netdev, NULL);
++ }
++
++ /* returning current descriptor, so advance to next free one */
++ priv->tx_head = (priv->tx_head + 1) % TX_CNT;
++end:
++ FN_EXIT0;
++
++ return (tx_t*)txdesc;
++}
++
++
++/***********************************************************************
++*/
++void*
++acxpci_l_get_txbuf(wlandevice_t *priv, tx_t* tx_opaque)
++{
++ return acx_get_txhostdesc(priv, (txdesc_t*)tx_opaque)->data;
++}
++
++
++/***********************************************************************
++** acxpci_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++*/
++void
++acxpci_l_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int len)
++{
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txhostdesc_t *hostdesc1, *hostdesc2;
++ client_t *clt;
++ u8 Ctl_8, Ctl2_8;
++
++ FN_ENTER;
++
++ /* fw doesn't tx such packets anyhow */
++ if (len < WLAN_HDR_A3_LEN)
++ goto end;
++
++ hostdesc1 = acx_get_txhostdesc(priv, txdesc);
++ hostdesc2 = hostdesc1 + 1;
++
++ /* modify flag status in separate variable to be able to write it back
++ * in one big swoop later (also in order to have less device memory
++ * accesses) */
++ Ctl_8 = txdesc->Ctl_8;
++ Ctl2_8 = txdesc->Ctl2_8;
++
++ /* DON'T simply set Ctl field to 0 here globally,
++ * it needs to maintain a consistent flag status (those are state flags!!),
++ * otherwise it may lead to severe disruption. Only set or reset particular
++ * flags at the exact moment this is needed...
++ * FIXME: what about Ctl2? Equally problematic? */
++
++ /* let chip do RTS/CTS handshaking before sending
++ * in case packet size exceeds threshold */
++ if (len > priv->rts_threshold)
++ SET_BIT(Ctl2_8, DESC_CTL2_RTS);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS);
++
++#ifdef DEBUG_WEP
++ if (priv->wep_enabled)
++ SET_BIT(Ctl2_8, DESC_CTL2_WEP);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_WEP);
++#endif
++
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(priv, ((wlan_hdr_t*)hostdesc1->data)->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = priv->ap_client;
++ break;
++#if 0
++/* testing was done on acx111: */
++ case ACX_MODE_MONITOR:
++ SET_BIT(Ctl2_8, 0
++/* sends CTS to self before packet */
++ + DESC_CTL2_SEQ /* don't increase sequence field */
++/* not working (looks like good fcs is still added) */
++ + DESC_CTL2_FCS /* don't add the FCS */
++/* not tested */
++ + DESC_CTL2_MORE_FRAG
++/* not tested */
++ + DESC_CTL2_RETRY /* don't increase retry field */
++/* not tested */
++ + DESC_CTL2_POWER /* don't increase power mgmt. field */
++/* no effect */
++ + DESC_CTL2_WEP /* encrypt this frame */
++/* not tested */
++ + DESC_CTL2_DUR /* don't increase duration field */
++ );
++ /* fallthrough */
++#endif
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ if (unlikely(clt && !clt->rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* used in tx cleanup routine for auto rate and accounting: */
++ acx_put_txc(priv, txdesc, clt);
++
++ txdesc->total_length = cpu_to_le16(len);
++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN);
++ if (IS_ACX111(priv)) {
++ u16 rate_cur = clt ? clt->rate_cur : priv->rate_bcast;
++ /* note that if !txdesc->do_auto, txrate->cur
++ ** has only one nonzero bit */
++ txdesc->u.r2.rate111 = cpu_to_le16(
++ rate_cur
++ /* WARNING: I was never able to make it work with prism54 AP.
++ ** It was falling down to 1Mbit where shortpre is not applicable,
++ ** and not working at all at "5,11 basic rates only" setting.
++ ** I even didn't see tx packets in radio packet capture.
++ ** Disabled for now --vda */
++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */
++ );
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ /* should add this to rate111 above as necessary */
++ | (clt->pbcc511 ? RATE111_PBCC511 : 0)
++#endif
++ hostdesc1->length = cpu_to_le16(len);
++ } else { /* ACX100 */
++ u8 rate_100 = clt ? clt->rate_100 : priv->rate_bcast100;
++ txdesc->u.r1.rate = rate_100;
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ if (clt->pbcc511) {
++ if (n == RATE100_5 || n == RATE100_11)
++ n |= RATE100_PBCC511;
++ }
++
++ if (clt->shortpre && (clt->cur != RATE111_1))
++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */
++#endif
++ /* set autodma and reclaim and 1st mpdu */
++ SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG);
++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN);
++ }
++ /* don't need to clean ack/rts statistics here, already
++ * done on descr cleanup */
++
++ /* clears Ctl DESC_CTL_HOSTOWN bit, thus telling that the descriptors
++ * are now owned by the acx100; do this as LAST operation */
++ CLEAR_BIT(Ctl_8, DESC_CTL_HOSTOWN);
++ /* flush writes before we release hostdesc to the adapter here */
++ wmb();
++ CLEAR_BIT(hostdesc1->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++ CLEAR_BIT(hostdesc2->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++
++ /* write back modified flags */
++ txdesc->Ctl2_8 = Ctl2_8;
++ txdesc->Ctl_8 = Ctl_8;
++
++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */
++//TODO: should it be a mmiowb() instead? we are protecting against race with write[bwl]()
++ /* flush writes before we tell the adapter that it's its turn now */
++ wmb();
++ acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ acx_write_flush(priv);
++
++ /* log the packet content AFTER sending it,
++ * in order to not delay sending any further than absolutely needed
++ * Do separate logs for acx100/111 to have human-readable rates */
++ if (unlikely(acx_debug & (L_XFER|L_DATA))) {
++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc;
++ if (IS_ACX111(priv))
++ printk("tx: pkt (%s): len %d "
++ "rate %04X%s status %u\n",
++ acx_get_packet_type_string(le16_to_cpu(fc)), len,
++ le16_to_cpu(txdesc->u.r2.rate111),
++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "",
++ priv->status);
++ else
++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n",
++ acx_get_packet_type_string(fc), len,
++ txdesc->u.r1.rate,
++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "",
++ priv->status);
++
++ if (acx_debug & L_DATA) {
++ printk("tx: 802.11 [%d]: ", len);
++ acx_dump_bytes(hostdesc1->data, len);
++ }
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++static void
++acx_l_handle_tx_error(wlandevice_t *priv, u8 error, unsigned int finger)
++{
++ const char *err = "unknown error";
++
++ /* hmm, should we handle this as a mask
++ * of *several* bits?
++ * For now I think only caring about
++ * individual bits is ok... */
++ switch (error) {
++ case 0x01:
++ err = "no Tx due to error in other fragment";
++ priv->wstats.discard.fragment++;
++ break;
++ case 0x02:
++ err = "Tx aborted";
++ priv->stats.tx_aborted_errors++;
++ break;
++ case 0x04:
++ err = "Tx desc wrong parameters";
++ priv->wstats.discard.misc++;
++ break;
++ case 0x08:
++ err = "WEP key not found";
++ priv->wstats.discard.misc++;
++ break;
++ case 0x10:
++ err = "MSDU lifetime timeout? - try changing "
++ "'iwconfig retry lifetime XXX'";
++ priv->wstats.discard.misc++;
++ break;
++ case 0x20:
++ err = "excessive Tx retries due to either distance "
++ "too high or unable to Tx or Tx frame error - "
++ "try changing 'iwconfig txpower XXX' or "
++ "'sens'itivity or 'retry'";
++ priv->wstats.discard.retries++;
++ /* FIXME: set (GETSET_TX|GETSET_RX) here
++ * (this seems to recalib radio on ACX100)
++ * after some more jiffies passed??
++ * But OTOH Tx error 0x20 also seems to occur on
++ * overheating, so I'm not sure whether we
++ * actually want that, since people maybe won't notice
++ * then that their hardware is slowly getting
++ * cooked...
++ * Or is it still a safe long distance from utter
++ * radio non-functionality despite many radio
++ * recalibs
++ * to final destructive overheating of the hardware?
++ * In this case we really should do recalib here...
++ * I guess the only way to find out is to do a
++ * potentially fatal self-experiment :-\
++ * Or maybe only recalib in case we're using Tx
++ * rate auto (on errors switching to lower speed
++ * --> less heat?) or 802.11 power save mode? */
++
++ /* ok, just do it.
++ * ENABLE_TX|ENABLE_RX helps, so even do
++ * DISABLE_TX and DISABLE_RX in order to perhaps
++ * have more impact. */
++ if (++priv->retry_errors_msg_ratelimit % 4 == 0) {
++ if (priv->retry_errors_msg_ratelimit <= 20)
++ printk("%s: several excessive Tx "
++ "retry errors occurred, attempting "
++ "to recalibrate radio. Radio "
++ "drift might be caused by increasing "
++ "card temperature, please check the card "
++ "before it's too late!\n",
++ priv->netdev->name);
++ if (priv->retry_errors_msg_ratelimit == 20)
++ printk("disabling above "
++ "notification message\n");
++
++ acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ break;
++ case 0x40:
++ err = "Tx buffer overflow";
++ priv->stats.tx_fifo_errors++;
++ break;
++ case 0x80:
++ err = "DMA error";
++ priv->wstats.discard.misc++;
++ break;
++ }
++ priv->stats.tx_errors++;
++ if (priv->stats.tx_errors <= 20)
++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n",
++ priv->netdev->name, error, finger, err);
++ else
++ printk("%s: tx error 0x%02X, buf %02u!\n",
++ priv->netdev->name, error, finger);
++}
++
++
++/***********************************************************************
++*/
++/* Theory of operation:
++** client->rate_cap is a bitmask of rates client is capable of.
++** client->rate_cfg is a bitmask of allowed (configured) rates.
++** It is set as a result of iwconfig rate N [auto]
++** or iwpriv set_rates "N,N,N N,N,N" commands.
++** It can be fixed (e.g. 0x0080 == 18Mbit only),
++** auto (0x00ff == 18Mbit or any lower value),
++** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_).
++**
++** client->rate_cur is a value for rate111 field in tx descriptor.
++** It is always set to txrate_cfg sans zero or more most significant
++** bits. This routine handles selection of new rate_cur value depending on
++** outcome of last tx event.
++**
++** client->rate_100 is a precalculated rate value for acx100
++** (we can do without it, but will need to calculate it on each tx).
++**
++** You cannot configure mixed usage of 5.5 and/or 11Mbit rate
++** with PBCC and CCK modulation. Either both at CCK or both at PBCC.
++** In theory you can implement it, but so far it is considered not worth doing.
++**
++** 22Mbit, of course, is PBCC always. */
++
++/* maps acx100 tx descr rate field to acx111 one */
++static u16
++rate100to111(u8 r)
++{
++ switch (r) {
++ case RATE100_1: return RATE111_1;
++ case RATE100_2: return RATE111_2;
++ case RATE100_5:
++ case (RATE100_5 | RATE100_PBCC511): return RATE111_5;
++ case RATE100_11:
++ case (RATE100_11 | RATE100_PBCC511): return RATE111_11;
++ case RATE100_22: return RATE111_22;
++ default:
++ printk("acx: unexpected acx100 txrate: %u! "
++ "Please report\n", r);
++ return RATE111_2;
++ }
++}
++
++
++static void
++acx_l_handle_txrate_auto(wlandevice_t *priv, struct client *txc,
++ unsigned int idx, u8 rate100, u16 rate111, u8 error)
++{
++ u16 sent_rate;
++ u16 cur = txc->rate_cur;
++ int slower_rate_was_used;
++
++ /* FIXME: need to implement some kind of rate success memory
++ * which stores the success percentage per rate, to be taken
++ * into account when considering allowing a new rate, since it
++ * doesn't really help to stupidly count fallback/stepup,
++ * since one invalid rate will spoil the party anyway
++ * (such as 22M in case of 11M-only peers) */
++
++ /* vda: hmm. current code will do this:
++ ** 1. send packets at 11 Mbit, stepup++
++ ** 2. will try to send at 22Mbit. hardware will see no ACK,
++ ** retries at 11Mbit, success. code notes that used rate
++ ** is lower. stepup = 0, fallback++
++ ** 3. repeat step 2 fallback_count times. Fall back to
++ ** 11Mbit. go to step 1.
++ ** If stepup_count is large (say, 16) and fallback_count
++ ** is small (3), this wouldn't be too bad wrt throughput */
++
++ /* do some preparations, i.e. calculate the one rate that was
++ * used to send this packet */
++ if (IS_ACX111(priv)) {
++ sent_rate = 1 << highest_bit(rate111 & RATE111_ALL);
++ } else {
++ sent_rate = rate100to111(rate100);
++ }
++ /* sent_rate has only one bit set now, corresponding to tx rate
++ * which was used by hardware to tx this particular packet */
++
++ /* now do the actual auto rate management */
++ acxlog(L_DEBUG, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X "
++ "__=%u/%u ^^=%u/%u\n",
++ (txc->ignore_count > 0) ? "[IGN] " : "",
++ txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg,
++ txc->fallback_count, priv->fallback_threshold,
++ txc->stepup_count, priv->stepup_threshold
++ );
++
++ /* we need to ignore old packets already in the tx queue since
++ * they use older rate bytes configured before our last rate change,
++ * otherwise our mechanism will get confused by interpreting old data.
++ * Do it here only, in order to have the logging above */
++ if (txc->ignore_count) {
++ txc->ignore_count--;
++ return;
++ }
++
++ /* true only if the only nonzero bit in sent_rate is
++ ** less significant than highest nonzero bit in cur */
++ slower_rate_was_used = ( cur > ((sent_rate<<1)-1) );
++
++ if (slower_rate_was_used || (error & 0x30)) {
++ txc->stepup_count = 0;
++ if (++txc->fallback_count <= priv->fallback_threshold)
++ return;
++ txc->fallback_count = 0;
++
++ /* clear highest 1 bit in cur */
++ sent_rate = RATE111_54;
++ while (!(cur & sent_rate)) sent_rate >>= 1;
++ CLEAR_BIT(cur, sent_rate);
++
++ if (cur) { /* we can't disable all rates! */
++ acxlog(L_XFER, "tx: falling back to ratemask %04X\n", cur);
++ txc->rate_cur = cur;
++ txc->ignore_count = TX_CNT - priv->tx_free;
++ }
++ } else if (!slower_rate_was_used) {
++ txc->fallback_count = 0;
++ if (++txc->stepup_count <= priv->stepup_threshold)
++ return;
++ txc->stepup_count = 0;
++
++ /* sanitize. Sort of not needed, but I dont trust hw that much...
++ ** what if it can report bogus tx rates sometimes? */
++ while (!(cur & sent_rate)) sent_rate >>= 1;
++ /* try to find a higher sent_rate that isn't yet in our
++ * current set, but is an allowed cfg */
++ while (1) {
++ sent_rate <<= 1;
++ if (sent_rate > txc->rate_cfg)
++ /* no higher rates allowed by config */
++ return;
++ if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate))
++ /* found */
++ break;
++ /* not found, try higher one */
++ }
++ SET_BIT(cur, sent_rate);
++ acxlog(L_XFER, "tx: stepping up to ratemask %04X\n", cur);
++ txc->rate_cur = cur;
++ /* FIXME: totally bogus - we could be sending to many peers at once... */
++ txc->ignore_count = TX_CNT - priv->tx_free;
++ }
++
++ /* calculate acx100 style rate byte if needed */
++ if (IS_ACX100(priv)) {
++ txc->rate_100 = bitpos2rate100[highest_bit(cur)];
++ }
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_log_txbuffer
++*----------------------------------------------------------------*/
++#if !ACX_DEBUG
++static inline void acx_l_log_txbuffer(const wlandevice_t *priv) {}
++#else
++static void
++acx_l_log_txbuffer(wlandevice_t *priv)
++{
++ txdesc_t *txdesc;
++ int i;
++
++ /* no FN_ENTER here, we don't want that */
++ /* no locks here, since it's entirely non-critical code */
++ txdesc = priv->txdesc_start;
++ if (!txdesc) return;
++ for (i = 0; i < TX_CNT; i++) {
++ if ((txdesc->Ctl_8 & DESC_CTL_DONE) == DESC_CTL_DONE)
++ printk("tx: buf %d done\n", i);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++}
++#endif
++
++
++/*----------------------------------------------------------------
++* acx_l_clean_tx_desc
++*
++* This function resets the txdescs' status when the ACX100
++* signals the TX done IRQ (txdescs have been processed), starting with
++* the pool index of the descriptor which we would use next,
++* in order to make sure that we can be as fast as possible
++* in filling new txdescs.
++* Oops, now we have our own index, so everytime we get called we know
++* where the next packet to be cleaned is.
++* Hmm, still need to loop through the whole ring buffer now,
++* since we lost sync for some reason when ping flooding or so...
++* (somehow we don't get the IRQ for acx_l_clean_tx_desc any more when
++* too many packets are being sent!)
++* FIXME: currently we only process one packet, but this gets out of
++* sync for some reason when ping flooding, so we need to loop,
++* but the previous smart loop implementation causes the ping latency
++* to rise dramatically (~3000 ms), at least on CardBus PheeNet WL-0022.
++* Dunno what to do :-\
++*----------------------------------------------------------------*/
++unsigned int
++acx_l_clean_tx_desc(wlandevice_t *priv)
++{
++ txdesc_t *txdesc;
++ struct client *txc;
++ int finger;
++ int num_cleaned;
++ int to_process;
++ u16 r111;
++ u8 error, ack_failures, rts_failures, rts_ok, r100;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_DEBUG))
++ acx_l_log_txbuffer(priv);
++
++ acxlog(L_BUFT, "tx: cleaning up bufs from %u\n", priv->tx_tail);
++
++ finger = priv->tx_tail;
++ num_cleaned = 0;
++ to_process = TX_CNT;
++ do {
++ txdesc = get_txdesc(priv, finger);
++
++ /* abort if txdesc is not marked as "Tx finished" and "owned" */
++ if ((txdesc->Ctl_8 & DESC_CTL_DONE) != DESC_CTL_DONE) {
++ /* we do need to have at least one cleaned,
++ * otherwise we wouldn't get called in the first place
++ */
++ if (num_cleaned)
++ break;
++ }
++
++ /* remember descr values... */
++ error = txdesc->error;
++ ack_failures = txdesc->ack_failures;
++ rts_failures = txdesc->rts_failures;
++ rts_ok = txdesc->rts_ok;
++ r100 = txdesc->u.r1.rate;
++ r111 = txdesc->u.r2.rate111;
++
++#if WIRELESS_EXT > 13 /* wireless_send_event() and IWEVTXDROP are WE13 */
++ /* need to check for certain error conditions before we
++ * clean the descriptor: we still need valid descr data here */
++ if (unlikely(0x30 & error)) {
++ /* only send IWEVTXDROP in case of retry or lifetime exceeded;
++ * all other errors mean we screwed up locally */
++ union iwreq_data wrqu;
++ wlan_hdr_t *hdr;
++ txhostdesc_t *hostdesc;
++
++ hostdesc = acx_get_txhostdesc(priv, txdesc);
++ hdr = (wlan_hdr_t *)hostdesc->data;
++ MAC_COPY(wrqu.addr.sa_data, hdr->a1);
++ wireless_send_event(priv->netdev, IWEVTXDROP, &wrqu, NULL);
++ }
++#endif
++ /* ...and free the descr */
++ txdesc->error = 0;
++ txdesc->ack_failures = 0;
++ txdesc->rts_failures = 0;
++ txdesc->rts_ok = 0;
++ /* signal host owning it LAST, since ACX already knows that this
++ * descriptor is finished since it set Ctl_8 accordingly:
++ * if _OWN is set at the beginning instead, our own get_tx
++ * might choose a Tx desc that isn't fully cleared
++ * (in case of bad locking). */
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ priv->tx_free++;
++ num_cleaned++;
++
++ if ((priv->tx_free >= TX_START_QUEUE)
++ && (priv->status == ACX_STATUS_4_ASSOCIATED)
++ && (acx_queue_stopped(priv->netdev))
++ ) {
++ acxlog(L_BUF, "tx: wake queue (avail. Tx desc %u)\n",
++ priv->tx_free);
++ acx_wake_queue(priv->netdev, NULL);
++ }
++
++ /* do error checking, rate handling and logging
++ * AFTER having done the work, it's faster */
++
++ /* do rate handling */
++ txc = acx_get_txc(priv, txdesc);
++ if (txc && priv->rate_auto) {
++ acx_l_handle_txrate_auto(priv, txc, finger, r100, r111, error);
++ }
++
++ if (unlikely(error))
++ acx_l_handle_tx_error(priv, error, finger);
++
++ if (IS_ACX111(priv))
++ acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n",
++ finger, ack_failures, rts_failures, rts_ok, r111);
++ else
++ acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n",
++ finger, ack_failures, rts_failures, rts_ok, r100);
++
++ /* update pointer for descr to be cleaned next */
++ finger = (finger + 1) % TX_CNT;
++ } while (--to_process);
++
++ /* remember last position */
++ priv->tx_tail = finger;
++/* end: */
++ FN_EXIT1(num_cleaned);
++ return num_cleaned;
++}
++
++/* clean *all* Tx descriptors, and regardless of their previous state.
++ * Used for brute-force reset handling. */
++void
++acx_l_clean_tx_desc_emergency(wlandevice_t *priv)
++{
++ txdesc_t *txdesc;
++ unsigned int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc = get_txdesc(priv, i);
++
++ /* free it */
++ txdesc->ack_failures = 0;
++ txdesc->rts_failures = 0;
++ txdesc->rts_ok = 0;
++ txdesc->error = 0;
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ }
++
++ priv->tx_free = TX_CNT;
++
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_l_log_rxbuffer
++*
++* Called from IRQ context only
++*----------------------------------------------------------------*/
++#if !ACX_DEBUG
++static inline void acx_l_log_rxbuffer(const wlandevice_t *priv) {}
++#else
++static void
++acx_l_log_rxbuffer(const wlandevice_t *priv)
++{
++ const struct rxhostdesc *rxhostdesc;
++ int i;
++
++ /* no FN_ENTER here, we don't want that */
++
++ rxhostdesc = priv->rxhostdesc_start;
++ if (!rxhostdesc) return;
++ for (i = 0; i < RX_CNT; i++) {
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ printk("rx: buf %d full\n", i);
++ rxhostdesc++;
++ }
++}
++#endif
++
++
++/***************************************************************
++** acx_l_process_rx_desc
++**
++** Called directly and only from the IRQ handler
++*/
++void
++acx_l_process_rx_desc(wlandevice_t *priv)
++{
++ rxhostdesc_t *hostdesc;
++ /* unsigned int curr_idx; */
++ unsigned int count = 0;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_BUFR)) {
++ acx_l_log_rxbuffer(priv);
++ }
++
++ /* First, have a loop to determine the first descriptor that's
++ * full, just in case there's a mismatch between our current
++ * rx_tail and the full descriptor we're supposed to handle. */
++ while (1) {
++ /* curr_idx = priv->rx_tail; */
++ hostdesc = &priv->rxhostdesc_start[priv->rx_tail];
++ priv->rx_tail = (priv->rx_tail + 1) % RX_CNT;
++ if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) {
++ /* found it! */
++ break;
++ }
++ count++;
++ if (unlikely(count > RX_CNT)) {
++ /* hmm, no luck: all descriptors empty, bail out */
++ goto end;
++ }
++ }
++
++ /* now process descriptors, starting with the first we figured out */
++ while (1) {
++ acxlog(L_BUFR, "rx: tail=%u Ctl_16=%04X Status=%08X\n",
++ priv->rx_tail, hostdesc->Ctl_16, hostdesc->Status);
++
++ acx_l_process_rxbuf(priv, hostdesc->data);
++
++ hostdesc->Status = 0;
++ /* flush all writes before adapter sees CTL_HOSTOWN change */
++ wmb();
++ /* Host no longer owns this, needs to be LAST */
++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++
++ /* ok, descriptor is handled, now check the next descriptor */
++ /* curr_idx = priv->rx_tail; */
++ hostdesc = &priv->rxhostdesc_start[priv->rx_tail];
++
++ /* if next descriptor is empty, then bail out */
++ /* FIXME: is this check really entirely correct?? */
++ /*
++//FIXME: inconsistent with check in prev while() loop
++ if (!(hostdesc->Ctl & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) */
++ if (!(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ break;
++
++ priv->rx_tail = (priv->rx_tail + 1) % RX_CNT;
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/*----------------------------------------------------------------
++* acx_s_create_tx_host_desc_queue
++*----------------------------------------------------------------*/
++static inline void*
++acx_alloc_coherent(struct pci_dev *hwdev, size_t size,
++ dma_addr_t *dma_handle, int flag)
++{
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53)
++ return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev,
++ size, dma_handle, flag);
++#else
++#warning Using old PCI-specific DMA allocation, may fail with out-of-mem!
++#warning Upgrade kernel if it does...
++ return pci_alloc_consistent(hwdev, size, dma_handle);
++#endif
++}
++
++static void*
++allocate(wlandevice_t *priv, size_t size, dma_addr_t *phy, const char *msg)
++{
++ void *ptr = acx_alloc_coherent(priv->pdev, size, phy, GFP_KERNEL);
++ if (ptr) {
++ acxlog(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
++ msg, (int)size, ptr, (unsigned long long)*phy);
++ memset(ptr, 0, size);
++ return ptr;
++ }
++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n",
++ msg, (int)size);
++ return NULL;
++}
++
++static int
++acx_s_create_tx_host_desc_queue(wlandevice_t *priv)
++{
++ txhostdesc_t *hostdesc;
++ u8 *txbuf;
++ dma_addr_t hostdesc_phy;
++ dma_addr_t txbuf_phy;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate TX buffer */
++ priv->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS;
++ priv->txbuf_start = allocate(priv, priv->txbuf_area_size,
++ &priv->txbuf_startphy, "txbuf_start");
++ if (!priv->txbuf_start)
++ goto fail;
++
++ /* allocate the TX host descriptor queue pool */
++ priv->txhostdesc_area_size = TX_CNT * 2*sizeof(txhostdesc_t);
++ priv->txhostdesc_start = allocate(priv, priv->txhostdesc_area_size,
++ &priv->txhostdesc_startphy, "txhostdesc_start");
++ if (!priv->txhostdesc_start)
++ goto fail;
++ /* check for proper alignment of TX host descriptor pool */
++ if ((long) priv->txhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++/* Each tx frame buffer is accessed by hardware via
++** txdesc -> txhostdesc(s) -> framebuffer(s)
++** We use only one txhostdesc per txdesc, but it looks like
++** acx111 is buggy: it accesses second txhostdesc
++** (via hostdesc.desc_phy_next field) even if
++** txdesc->length == hostdesc->length and thus
++** entire packet was placed into first txhostdesc.
++** Due to this bug acx111 hangs unless second txhostdesc
++** has hostdesc.length = 3 (or larger)
++** Storing NULL into hostdesc.desc_phy_next
++** doesn't seem to help.
++*/
++/* It is not known whether we need to have 'extra' second
++** txhostdescs for acx100. Maybe it is acx111-only bug.
++*/
++ hostdesc = priv->txhostdesc_start;
++ hostdesc_phy = priv->txhostdesc_startphy;
++ txbuf = priv->txbuf_start;
++ txbuf_phy = priv->txbuf_startphy;
++
++#if 0
++/* Works for xterasys xn2522g, does not for WG311v2 !!? */
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(txhostdesc_t);
++ if (!(i & 1)) {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc->pNext = ptr2acx(NULL);
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS;
++ } else {
++ /* hostdesc->data_phy = ... */
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ /* hostdesc->Ctl_16 = ... */
++ hostdesc->length = 3; /* bug workaround */
++ /* hostdesc->desc_phy_next = ... */
++ /* hostdesc->pNext = ... */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ /* hostdesc->data = ... */
++ }
++ hostdesc++;
++ }
++#endif
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(txhostdesc_t);
++ if (!(i & 1)) {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* done by memset(0): hostdesc->data_offset = 0; */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_HDR_A3_LEN;
++ txbuf_phy += WLAN_HDR_A3_LEN;
++ } else {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* done by memset(0): hostdesc->data_offset = 0; */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ...; */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ }
++ hostdesc++;
++ }
++ hostdesc--;
++ hostdesc->desc_phy_next = cpu2acx(priv->txhostdesc_startphy);
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_tx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acx_s_create_rx_host_desc_queue
++*/
++/* the whole size of a data buffer (header plus data body)
++ * plus 32 bytes safety offset at the end */
++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32)
++
++static int
++acx_s_create_rx_host_desc_queue(wlandevice_t *priv)
++{
++ rxhostdesc_t *hostdesc;
++ rxbuffer_t *rxbuf;
++ dma_addr_t hostdesc_phy;
++ dma_addr_t rxbuf_phy;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate the RX host descriptor queue pool */
++ priv->rxhostdesc_area_size = RX_CNT * sizeof(rxhostdesc_t);
++ priv->rxhostdesc_start = allocate(priv, priv->rxhostdesc_area_size,
++ &priv->rxhostdesc_startphy, "rxhostdesc_start");
++ if (!priv->rxhostdesc_start)
++ goto fail;
++ /* check for proper alignment of RX host descriptor pool */
++ if ((long) priv->rxhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ /* allocate Rx buffer pool which will be used by the acx
++ * to store the whole content of the received frames in it */
++ priv->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE;
++ priv->rxbuf_start = allocate(priv, priv->rxbuf_area_size,
++ &priv->rxbuf_startphy, "rxbuf_start");
++ if (!priv->rxbuf_start)
++ goto fail;
++
++ rxbuf = priv->rxbuf_start;
++ rxbuf_phy = priv->rxbuf_startphy;
++ hostdesc = priv->rxhostdesc_start;
++ hostdesc_phy = priv->rxhostdesc_startphy;
++
++ /* don't make any popular C programming pointer arithmetic mistakes
++ * here, otherwise I'll kill you...
++ * (and don't dare asking me why I'm warning you about that...) */
++ for (i = 0; i < RX_CNT; i++) {
++ hostdesc->data = rxbuf;
++ hostdesc->data_phy = cpu2acx(rxbuf_phy);
++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE);
++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++ rxbuf++;
++ rxbuf_phy += sizeof(rxbuffer_t);
++ hostdesc_phy += sizeof(rxhostdesc_t);
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc++;
++ }
++ hostdesc--;
++ hostdesc->desc_phy_next = cpu2acx(priv->rxhostdesc_startphy);
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_rx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acx_s_create_hostdesc_queues
++*/
++int
++acx_s_create_hostdesc_queues(wlandevice_t *priv)
++{
++ int result;
++ result = acx_s_create_tx_host_desc_queue(priv);
++ if (OK != result) return result;
++ result = acx_s_create_rx_host_desc_queue(priv);
++ return result;
++}
++
++
++/***************************************************************
++** acx_create_tx_desc_queue
++*/
++static void
++acx_create_tx_desc_queue(wlandevice_t *priv, u32 tx_queue_start)
++{
++ txdesc_t *txdesc;
++ txhostdesc_t *hostdesc;
++ dma_addr_t hostmemptr;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ priv->txdesc_size = sizeof(txdesc_t);
++
++ if (IS_ACX111(priv)) {
++ /* the acx111 txdesc is 4 bytes larger */
++ priv->txdesc_size = sizeof(txdesc_t) + 4;
++ }
++
++ priv->txdesc_start = (txdesc_t *) (priv->iobase2 + tx_queue_start);
++
++ acxlog(L_DEBUG, "priv->iobase2=%p\n"
++ "tx_queue_start=%08X\n"
++ "priv->txdesc_start=%p\n",
++ priv->iobase2,
++ tx_queue_start,
++ priv->txdesc_start);
++
++ priv->tx_free = TX_CNT;
++ /* done by memset: priv->tx_head = 0; */
++ /* done by memset: priv->tx_tail = 0; */
++ txdesc = priv->txdesc_start;
++ mem_offs = tx_queue_start;
++ hostmemptr = priv->txhostdesc_startphy;
++ hostdesc = priv->txhostdesc_start;
++
++ if (IS_ACX111(priv)) {
++ /* ACX111 has a preinitialized Tx buffer! */
++ /* loop over whole send pool */
++ /* FIXME: do we have to do the hostmemptr stuff here?? */
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc->HostMemPtr = ptr2acx(hostmemptr);
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ /* reserve two (hdr desc and payload desc) */
++ hostdesc += 2;
++ hostmemptr += 2 * sizeof(txhostdesc_t);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++ } else {
++ /* ACX100 Tx buffer needs to be initialized by us */
++ /* clear whole send pool. sizeof is safe here (we are acx100) */
++ memset(priv->txdesc_start, 0, TX_CNT * sizeof(txdesc_t));
++
++ /* loop over whole send pool */
++ for (i = 0; i < TX_CNT; i++) {
++ acxlog(L_DEBUG, "configure card tx descriptor: 0x%p, "
++ "size: 0x%X\n", txdesc, priv->txdesc_size);
++
++ /* pointer to hostdesc memory */
++ /* FIXME: type-incorrect assignment, might cause trouble
++ * in some cases */
++ txdesc->HostMemPtr = ptr2acx(hostmemptr);
++ /* initialise ctl */
++ txdesc->Ctl_8 = DESC_CTL_INIT;
++ txdesc->Ctl2_8 = 0;
++ /* point to next txdesc */
++ txdesc->pNextDesc = cpu2acx(mem_offs + priv->txdesc_size);
++ /* reserve two (hdr desc and payload desc) */
++ hostdesc += 2;
++ hostmemptr += 2 * sizeof(txhostdesc_t);
++ /* go to the next one */
++ mem_offs += priv->txdesc_size;
++ /* ++ is safe here (we are acx100) */
++ txdesc++;
++ }
++ /* go back to the last one */
++ txdesc--;
++ /* and point to the first making it a ring buffer */
++ txdesc->pNextDesc = cpu2acx(tx_queue_start);
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acx_create_rx_desc_queue
++*/
++static void
++acx_create_rx_desc_queue(wlandevice_t *priv, u32 rx_queue_start)
++{
++ rxdesc_t *rxdesc;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ /* done by memset: priv->rx_tail = 0; */
++
++ /* ACX111 doesn't need any further config: preconfigures itself.
++ * Simply print ring buffer for debugging */
++ if (IS_ACX111(priv)) {
++ /* rxdesc_start already set here */
++
++ priv->rxdesc_start = (rxdesc_t *) ((u8 *)priv->iobase2 + rx_queue_start);
++
++ rxdesc = priv->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ acxlog(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc);
++ rxdesc = priv->rxdesc_start = (rxdesc_t *)
++ (priv->iobase2 + acx2cpu(rxdesc->pNextDesc));
++ }
++ } else {
++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */
++ /* rxdesc_start should be right AFTER Tx pool */
++ priv->rxdesc_start = (rxdesc_t *)
++ ((u8 *) priv->txdesc_start + (TX_CNT * sizeof(txdesc_t)));
++ /* NB: sizeof(txdesc_t) above is valid because we know
++ ** we are in if(acx100) block. Beware of cut-n-pasting elsewhere!
++ ** acx111's txdesc is larger! */
++
++ memset(priv->rxdesc_start, 0, RX_CNT * sizeof(rxdesc_t));
++
++ /* loop over whole receive pool */
++ rxdesc = priv->rxdesc_start;
++ mem_offs = rx_queue_start;
++ for (i = 0; i < RX_CNT; i++) {
++ acxlog(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc);
++ rxdesc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA;
++ /* point to next rxdesc */
++ rxdesc->pNextDesc = cpu2acx(mem_offs + sizeof(rxdesc_t));
++ /* go to the next one */
++ mem_offs += sizeof(rxdesc_t);
++ rxdesc++;
++ }
++ /* go to the last one */
++ rxdesc--;
++
++ /* and point to the first making it a ring buffer */
++ rxdesc->pNextDesc = cpu2acx(rx_queue_start);
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acx_create_desc_queues
++*/
++void
++acx_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start)
++{
++ acx_create_tx_desc_queue(priv, tx_queue_start);
++ acx_create_rx_desc_queue(priv, rx_queue_start);
++}
++
++
++/***************************************************************
++** acxpci_s_proc_diag_output
++*/
++char*
++acxpci_s_proc_diag_output(char *p, wlandevice_t *priv)
++{
++ const char *rtl, *thd, *ttl;
++ rxhostdesc_t *rxhostdesc;
++ txdesc_t *txdesc;
++ int i;
++
++ FN_ENTER;
++
++ p += sprintf(p, "** Rx buf **\n");
++ rxhostdesc = priv->rxhostdesc_start;
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ rtl = (i == priv->rx_tail) ? " [tail]" : "";
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)) )
++ p += sprintf(p, "%02u FULL%s\n", i, rtl);
++ else
++ p += sprintf(p, "%02u empty%s\n", i, rtl);
++ rxhostdesc++;
++ }
++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", priv->tx_free,
++ acx_queue_stopped(priv->netdev) ? "STOPPED" : "running");
++ txdesc = priv->txdesc_start;
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ thd = (i == priv->tx_head) ? " [head]" : "";
++ ttl = (i == priv->tx_tail) ? " [tail]" : "";
++ if (txdesc->Ctl_8 & DESC_CTL_ACXDONE)
++ p += sprintf(p, "%02u DONE (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ else
++ if (!(txdesc->Ctl_8 & DESC_CTL_HOSTOWN))
++ p += sprintf(p, "%02u TxWait (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ else
++ p += sprintf(p, "%02u empty (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ txdesc = move_txdesc(priv, txdesc, 1);
++ }
++ p += sprintf(p,
++ "\n"
++ "** PCI data **\n"
++ "txbuf_start %p, txbuf_area_size %u, txbuf_startphy %08llx\n"
++ "txdesc_size %u, txdesc_start %p\n"
++ "txhostdesc_start %p, txhostdesc_area_size %u, txhostdesc_startphy %08llx\n"
++ "rxdesc_start %p\n"
++ "rxhostdesc_start %p, rxhostdesc_area_size %u, rxhostdesc_startphy %08llx\n"
++ "rxbuf_start %p, rxbuf_area_size %u, rxbuf_startphy %08llx\n",
++ priv->txbuf_start, priv->txbuf_area_size, (u64)priv->txbuf_startphy,
++ priv->txdesc_size, priv->txdesc_start,
++ priv->txhostdesc_start, priv->txhostdesc_area_size, (u64)priv->txhostdesc_startphy,
++ priv->rxdesc_start,
++ priv->rxhostdesc_start, priv->rxhostdesc_area_size, (u64)priv->rxhostdesc_startphy,
++ priv->rxbuf_start, priv->rxbuf_area_size, (u64)priv->rxbuf_startphy);
++
++ FN_EXIT0;
++ return p;
++}
++
++
++/***********************************************************************
++*/
++int
++acx_proc_eeprom_output(char *buf, wlandevice_t *priv)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < 0x400; i++) {
++ acx_read_eeprom_offset(priv, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++void
++acx_set_interrupt_mask(wlandevice_t *priv)
++{
++ if (IS_ACX111(priv)) {
++ priv->irq_mask = (u16) ~(0
++ /* | HOST_INT_RX_DATA */
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ | HOST_INT_RX_COMPLETE
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ | HOST_INT_IV_ICV_FAILURE
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ | HOST_INT_FCS_THRESHOLD
++ /* | HOST_INT_UNKNOWN */
++ );
++ priv->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */
++ } else {
++ priv->irq_mask = (u16) ~(0
++ /* | HOST_INT_RX_DATA */
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ | HOST_INT_RX_COMPLETE
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ /* | HOST_INT_IV_ICV_FAILURE */
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ /* | HOST_INT_FCS_THRESHOLD */
++ /* | HOST_INT_UNKNOWN */
++ );
++ priv->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */
++ }
++}
++
++
++/***********************************************************************
++*/
++int
++acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm)
++{
++ /* since it can be assumed that at least the Maxim radio has a
++ * maximum power output of 20dBm and since it also can be
++ * assumed that these values drive the DAC responsible for
++ * setting the linear Tx level, I'd guess that these values
++ * should be the corresponding linear values for a dBm value,
++ * in other words: calculate the values from that formula:
++ * Y [dBm] = 10 * log (X [mW])
++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm)
++ * and you're done...
++ * Hopefully that's ok, but you never know if we're actually
++ * right... (especially since Windows XP doesn't seem to show
++ * actual Tx dBm values :-P) */
++
++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the
++ * values are EXACTLY mW!!! Not sure about RFMD and others,
++ * though... */
++ static const u8 dbm2val_maxim[21] = {
++ 63, 63, 63, 62,
++ 61, 61, 60, 60,
++ 59, 58, 57, 55,
++ 53, 50, 47, 43,
++ 38, 31, 23, 13,
++ 0
++ };
++ static const u8 dbm2val_rfmd[21] = {
++ 0, 0, 0, 1,
++ 2, 2, 3, 3,
++ 4, 5, 6, 8,
++ 10, 13, 16, 20,
++ 25, 32, 41, 50,
++ 63
++ };
++ const u8 *table;
++
++ switch (priv->radio_type) {
++ case RADIO_MAXIM_0D:
++ table = &dbm2val_maxim[0];
++ break;
++ case RADIO_RFMD_11:
++ case RADIO_RALINK_15:
++ table = &dbm2val_rfmd[0];
++ break;
++ default:
++ printk("%s: unknown/unsupported radio type, "
++ "cannot modify tx power level yet!\n",
++ priv->netdev->name);
++ return NOT_OK;
++ }
++ printk("%s: changing radio power level to %u dBm (%u)\n",
++ priv->netdev->name, level_dbm, table[level_dbm]);
++ acxpci_s_write_phy_reg(priv, 0x11, table[level_dbm]);
++ return OK;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_init_module
++*
++* Module initialization routine, called once at module load time.
++*
++* Returns:
++* 0 - success
++* ~0 - failure, module is unloaded.
++*
++* Call context:
++* process thread (insmod or modprobe)
++----------------------------------------------------------------*/
++int __init
++acxpci_e_init_module(void)
++{
++ int res;
++
++ FN_ENTER;
++
++#if (ACX_IO_WIDTH==32)
++ printk("acx: compiled to use 32bit I/O access. "
++ "I/O timing issues might occur, such as "
++ "non-working firmware upload. Report them\n");
++#else
++ printk("acx: compiled to use 16bit I/O access only "
++ "(compatibility mode)\n");
++#endif
++
++#ifdef __LITTLE_ENDIAN
++ acxlog(L_INIT, "running on a little-endian CPU\n");
++#else
++ acxlog(L_INIT, "running on a BIG-ENDIAN CPU\n");
++#endif
++ acxlog(L_INIT, "PCI module " WLAN_RELEASE " initialized, "
++ "waiting for cards to probe...\n");
++
++ res = pci_module_init(&acx_pci_drv_id);
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/*----------------------------------------------------------------
++* acx_e_cleanup_module
++*
++* Called at module unload time. This is our last chance to
++* clean up after ourselves.
++*
++* Call context:
++* process thread
++----------------------------------------------------------------*/
++void __exit
++acxpci_e_cleanup_module(void)
++{
++ struct net_device *dev;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Since the whole module is about to be unloaded,
++ * we recursively shutdown all cards we handled instead
++ * of doing it in remove_pci() (which will be activated by us
++ * via pci_unregister_driver at the end).
++ * remove_pci() might just get called after a card eject,
++ * that's why hardware operations have to be done here instead
++ * when the hardware is available. */
++
++ down(&root_acx_dev_sem);
++
++ dev = root_acx_dev.newest;
++ while (dev != NULL) {
++ /* doh, netdev_priv() doesn't have const! */
++ wlandevice_t *priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ /* disable both Tx and Rx to shut radio down properly */
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++
++#ifdef REDUNDANT
++ /* put the eCPU to sleep to save power
++ * Halting is not possible currently,
++ * since not supported by all firmware versions */
++ acx_s_issue_cmd(priv, ACX100_CMD_SLEEP, NULL, 0);
++#endif
++ acx_lock(priv, flags);
++
++ /* disable power LED to save power :-) */
++ acxlog(L_INIT, "switching off power LED to save power :-)\n");
++ acx_l_power_led(priv, 0);
++
++ /* stop our eCPU */
++ if (IS_ACX111(priv)) {
++ /* FIXME: does this actually keep halting the eCPU?
++ * I don't think so...
++ */
++ acx_l_reset_mac(priv);
++ } else {
++ u16 temp;
++
++ /* halt eCPU */
++ temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1;
++ acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp);
++ acx_write_flush(priv);
++ }
++
++ acx_unlock(priv, flags);
++
++ acx_sem_unlock(priv);
++
++ dev = priv->prev_nd;
++ }
++
++ up(&root_acx_dev_sem);
++
++ /* now let the PCI layer recursively remove
++ * all PCI related things (acx_e_remove_pci()) */
++ pci_unregister_driver(&acx_pci_drv_id);
++
++ FN_EXIT0;
++}
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/setrate.c bt_kernel/drivers/net/wireless/tiacx/setrate.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/setrate.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/setrate.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,213 @@
++/* TODO: stop #including, move into wireless.c
++ * until then, keep in sync copies in prism54/ and acx/ dirs
++ * code+data size: less than 1k */
++
++enum {
++ DOT11_RATE_1,
++ DOT11_RATE_2,
++ DOT11_RATE_5,
++ DOT11_RATE_11,
++ DOT11_RATE_22,
++ DOT11_RATE_33,
++ DOT11_RATE_6,
++ DOT11_RATE_9,
++ DOT11_RATE_12,
++ DOT11_RATE_18,
++ DOT11_RATE_24,
++ DOT11_RATE_36,
++ DOT11_RATE_48,
++ DOT11_RATE_54
++};
++enum {
++ DOT11_MOD_DBPSK,
++ DOT11_MOD_DQPSK,
++ DOT11_MOD_CCK,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_CCKOFDM,
++ DOT11_MOD_PBCC
++};
++static const u8 ratelist[] = { 1,2,5,11,22,33,6,9,12,18,24,36,48,54 };
++static const u8 dot11ratebyte[] = { 1*2,2*2,11,11*2,22*2,33*2,6*2,9*2,12*2,18*2,24*2,36*2,48*2,54*2 };
++static const u8 default_modulation[] = {
++ DOT11_MOD_DBPSK,
++ DOT11_MOD_DQPSK,
++ DOT11_MOD_CCK,
++ DOT11_MOD_CCK,
++ DOT11_MOD_PBCC,
++ DOT11_MOD_PBCC,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM
++};
++
++static /* TODO: remove 'static' when moved to wireless.c */
++int
++rate_mbit2enum(int n) {
++ int i=0;
++ while(i<sizeof(ratelist)) {
++ if(n==ratelist[i]) return i;
++ i++;
++ }
++ return -EINVAL;
++}
++
++static int
++get_modulation(int r_enum, char suffix) {
++ if(suffix==',' || suffix==' ' || suffix=='\0') {
++ /* could shorten default_mod by 8 bytes:
++ if(r_enum>=DOT11_RATE_6) return DOT11_MOD_OFDM; */
++ return default_modulation[r_enum];
++ }
++ if(suffix=='c') {
++ if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_11) return -EINVAL;
++ return DOT11_MOD_CCK;
++ }
++ if(suffix=='p') {
++ if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_33) return -EINVAL;
++ return DOT11_MOD_PBCC;
++ }
++ if(suffix=='o') {
++ if(r_enum<DOT11_RATE_6) return -EINVAL;
++ return DOT11_MOD_OFDM;
++ }
++ if(suffix=='d') {
++ if(r_enum<DOT11_RATE_6) return -EINVAL;
++ return DOT11_MOD_CCKOFDM;
++ }
++ return -EINVAL;
++}
++
++#ifdef UNUSED
++static int
++fill_ratevector(const char **pstr, u8 *vector, int size,
++ int (*supported)(int mbit, int mod, void *opaque), void *opaque, int or_mask)
++{
++ unsigned long rate_mbit;
++ int rate_enum,mod;
++ const char *str = *pstr;
++ char c;
++
++ do {
++ rate_mbit = simple_strtoul(str, (char**)&str, 10);
++ if(rate_mbit>INT_MAX) return -EINVAL;
++
++ rate_enum = rate_mbit2enum(rate_mbit);
++ if(rate_enum<0) return rate_enum;
++
++ c = *str;
++ mod = get_modulation(rate_enum, c);
++ if(mod<0) return mod;
++
++ if(c>='a' && c<='z') c = *++str;
++ if(c!=',' && c!=' ' && c!='\0') return -EINVAL;
++
++ if(supported) {
++ int r = supported(rate_mbit, mod, opaque);
++ if(r) return r;
++ }
++
++ *vector++ = dot11ratebyte[rate_enum] | or_mask;
++
++ size--;
++ str++;
++ } while(size>0 && c==',');
++
++ if(size<1) return -E2BIG;
++ *vector=0; /* TODO: sort, remove dups? */
++
++ *pstr = str-1;
++ return 0;
++}
++
++static /* TODO: remove 'static' when moved to wireless.c */
++int
++fill_ratevectors(const char *str, u8 *brate, u8 *orate, int size,
++ int (*supported)(int mbit, int mod, void *opaque), void *opaque)
++{
++ int r;
++
++ r = fill_ratevector(&str, brate, size, supported, opaque, 0x80);
++ if(r) return r;
++
++ orate[0] = 0;
++ if(*str==' ') {
++ str++;
++ r = fill_ratevector(&str, orate, size, supported, opaque, 0);
++ if(r) return r;
++ /* TODO: sanitize, e.g. remove/error on rates already in basic rate set? */
++ }
++ if(*str)
++ return -EINVAL;
++
++ return 0;
++}
++#endif
++
++/* TODO: use u64 masks? */
++
++static int
++fill_ratemask(const char **pstr, u32* mask,
++ int (*supported)(int mbit, int mod,void *opaque),
++ u32 (*gen_mask)(int mbit, int mod,void *opaque),
++ void *opaque)
++{
++ unsigned long rate_mbit;
++ int rate_enum,mod;
++ u32 m = 0;
++ const char *str = *pstr;
++ char c;
++
++ do {
++ rate_mbit = simple_strtoul(str, (char**)&str, 10);
++ if(rate_mbit>INT_MAX) return -EINVAL;
++
++ rate_enum = rate_mbit2enum(rate_mbit);
++ if(rate_enum<0) return rate_enum;
++
++ c = *str;
++ mod = get_modulation(rate_enum, c);
++ if(mod<0) return mod;
++
++ if(c>='a' && c<='z') c = *++str;
++ if(c!=',' && c!=' ' && c!='\0') return -EINVAL;
++
++ if(supported) {
++ int r = supported(rate_mbit, mod, opaque);
++ if(r) return r;
++ }
++
++ m |= gen_mask(rate_mbit, mod, opaque);
++ str++;
++ } while(c==',');
++
++ *pstr = str-1;
++ *mask |= m;
++ return 0;
++}
++
++static /* TODO: remove 'static' when moved to wireless.c */
++int
++fill_ratemasks(const char *str, u32 *bmask, u32 *omask,
++ int (*supported)(int mbit, int mod,void *opaque),
++ u32 (*gen_mask)(int mbit, int mod,void *opaque),
++ void *opaque)
++{
++ int r;
++
++ r = fill_ratemask(&str, bmask, supported, gen_mask, opaque);
++ if(r) return r;
++
++ if(*str==' ') {
++ str++;
++ r = fill_ratemask(&str, omask, supported, gen_mask, opaque);
++ if(r) return r;
++ }
++ if(*str)
++ return -EINVAL;
++ return 0;
++}
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/usb.c bt_kernel/drivers/net/wireless/tiacx/usb.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/usb.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/usb.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,1700 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** USB support for TI ACX100 based devices. Many parts are taken from
++** the PCI driver.
++**
++** Authors:
++** Martin Wawro <martin.wawro AT uni-dortmund.de>
++** Andreas Mohr <andi AT lisas.de>
++**
++** Issues:
++** - Note that this driver relies on a native little-endian byteformat
++** at some points
++**
++** LOCKING
++** callback functions called by USB core are running in interrupt context
++** and thus have names with _i_.
++*/
++#define ACX_USB 1
++
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
++#include <linux/moduleparam.h>
++#endif
++#include <linux/kernel.h>
++#include <linux/usb.h>
++#include <linux/netdevice.h>
++#include <linux/rtnetlink.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif
++
++#include "acx.h"
++
++/* number of endpoints of an interface */
++#define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints
++#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc
++#define GET_DEV(udev) usb_get_dev((udev))
++#define PUT_DEV(udev) usb_put_dev((udev))
++#define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */
++
++#define QUEUE_BULK 0
++#define ZERO_PACKET URB_ZERO_PACKET
++
++static inline int
++submit_urb(struct urb *urb, int mem_flags)
++{
++ return usb_submit_urb(urb, mem_flags);
++}
++static inline struct urb*
++alloc_urb(int iso_pk, int mem_flags)
++{
++ return usb_alloc_urb(iso_pk, mem_flags);
++}
++
++
++/***********************************************************************
++*/
++#define ACX100_VENDOR_ID 0x2001
++#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01
++#define ACX100_PRODUCT_ID_BOOTED 0x3B00
++
++/* RX-Timeout: NONE (request waits forever) */
++#define ACX100_USB_RX_TIMEOUT (0)
++
++#define ACX100_USB_TX_TIMEOUT (4*HZ)
++
++#define USB_CTRL_HARD_TIMEOUT 5500 /* steps in ms */
++
++
++/***********************************************************************
++** Prototypes
++*/
++static int acx100usb_e_probe(struct usb_interface *, const struct usb_device_id *);
++static void acx100usb_e_disconnect(struct usb_interface *);
++static void acx100usb_i_complete_tx(struct urb *, struct pt_regs *);
++static void acx100usb_i_complete_rx(struct urb *, struct pt_regs *);
++static int acx100usb_e_open(struct net_device *);
++static int acx100usb_e_close(struct net_device *);
++static void acx100usb_i_set_rx_mode(struct net_device *);
++static int acx100usb_e_init_network_device(struct net_device *);
++static int acx100usb_boot(struct usb_device *);
++
++static struct net_device_stats * acx_e_get_stats(struct net_device *);
++static struct iw_statistics *acx_e_get_wireless_stats(struct net_device *);
++
++static void acx100usb_l_poll_rx(wlandevice_t *, int number);
++
++static void acx100usb_i_tx_timeout(struct net_device *);
++
++/* static void dump_device(struct usb_device *); */
++/* static void dump_device_descriptor(struct usb_device_descriptor *); */
++/* static void dump_config_descriptor(struct usb_config_descriptor *); */
++
++/***********************************************************************
++** Module Data
++*/
++#define TXBUFSIZE sizeof(usb_txbuffer_t)
++//// Bogus! We CANNOT pretend that rxbuffer_t is larger than it is.
++/* make it a multiply of 64 */
++/* #define RXBUFSIZE ((sizeof(rxbuffer_t)+63) & ~63) */
++#define RXBUFSIZE sizeof(rxbuffer_t)
++
++static const struct usb_device_id
++acx100usb_ids[] = {
++ { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) },
++ { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) },
++ {}
++};
++
++
++/* USB driver data structure as required by the kernel's USB core */
++static struct usb_driver
++acx100usb_driver = {
++ .name = "acx_usb",
++ .owner = THIS_MODULE,
++ .probe = acx100usb_e_probe,
++ .disconnect = acx100usb_e_disconnect,
++ .id_table = acx100usb_ids
++};
++
++
++/***********************************************************************
++** USB helper
++**
++** ldd3 ch13 says:
++** When the function is usb_kill_urb, the urb lifecycle is stopped. This
++** function is usually used when the device is disconnected from the system,
++** in the disconnect callback. For some drivers, the usb_unlink_urb function
++** should be used to tell the USB core to stop an urb. This function does not
++** wait for the urb to be fully stopped before returning to the caller.
++** This is useful for stoppingthe urb while in an interrupt handler or when
++** a spinlock is held, as waiting for a urb to fully stop requires the ability
++** for the USB core to put the calling process to sleep. This function requires
++** that the URB_ASYNC_UNLINK flag value be set in the urb that is being asked
++** to be stopped in order to work properly.
++**
++** URB_ASYNC_UNLINK is osolete, usb_unlink_urb will always be
++** asynchronuos while usb_kill_urb is synchronuos and should be called
++** directly. -> drivers/usb/core/urb.c
++**
++** In light of this, timeout is just for paranoid reasons...
++*/
++static void
++acx_unlink_and_free_urb(struct urb* urb)
++{
++ if (!urb)
++ return;
++
++ if (urb->status == -EINPROGRESS) {
++ int timeout = 10;
++
++ usb_unlink_urb(urb);
++ while (--timeout && urb->status == -EINPROGRESS) {
++ mdelay(1);
++ }
++ /* if (!timeout) then what?? */
++ }
++
++ /* just a refcounted kfree, safe undef lock */
++ usb_free_urb(urb);
++}
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG
++static char*
++acx100usb_pstatus(int val)
++{
++ static char status[20];
++
++ if (val < 0)
++ sprintf(status, "errno %d", -val);
++ else
++ sprintf(status, "length %d", val);
++
++ return status;
++}
++#endif /* ACX_DEBUG */
++
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acxusb_s_read_phy_reg
++*/
++int
++acxusb_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf)
++{
++ mem_read_write_t mem;
++
++ FN_ENTER;
++
++ mem.addr = cpu_to_le16(reg);
++ mem.type = cpu_to_le16(0x82);
++ mem.len = cpu_to_le32(4);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_READ, &mem, sizeof(mem) - 4);
++ *charbuf = mem.data;
++ acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acxusb_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value)
++{
++ mem_read_write_t mem;
++
++ FN_ENTER;
++
++ mem.addr = cpu_to_le16(reg);
++ mem.type = cpu_to_le16(0x82);
++ mem.len = cpu_to_le32(4);
++ mem.data = value;
++//FIXME: maybe sizeof() - 4?
++ acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem));
++ acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_s_issue_cmd_timeo
++** Excecutes a command in the command mailbox
++**
++** buffer = a pointer to the data.
++** The data must not include 4 byte command header
++*/
++
++/* TODO: ideally we shall always know how much we need
++** and this shall be 0 */
++#define BOGUS_SAFETY_PADDING 0x40
++
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxusb_s_issue_cmd_timeo(
++ wlandevice_t *priv,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout)
++{
++#else
++int
++acxusb_s_issue_cmd_timeo_debug(
++ wlandevice_t *priv,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout,
++ const char* cmdstr)
++{
++#endif
++ /* USB ignores timeout param */
++
++ struct usb_device *usbdev;
++ struct {
++ u16 cmd ACX_PACKED;
++ u16 status ACX_PACKED;
++ u8 data[1] ACX_PACKED;
++ } *loc;
++ const char *devname;
++ int acklen, blocklen, inpipe, outpipe;
++ int cmd_status;
++ int result;
++
++ FN_ENTER;
++
++ devname = priv->netdev->name;
++ if (!devname || !devname[0])
++ devname = "acx";
++
++ acxlog(L_CTL, FUNC"(cmd:%s,buflen:%u,type:0x%04X)\n",
++ cmdstr, buflen,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ loc = kmalloc(buflen + 4 + BOGUS_SAFETY_PADDING, GFP_KERNEL);
++ if (!loc) {
++ printk("%s: "FUNC"(): no memory for data buffer\n", devname);
++ goto bad;
++ }
++
++ /* get context from wlandevice */
++ usbdev = priv->usbdev;
++
++ /* check which kind of command was issued */
++ loc->cmd = cpu_to_le16(cmd);
++ loc->status = 0;
++
++/* NB: buflen == frmlen + 4
++**
++** Interrogate: write 8 bytes: (cmd,status,rid,frmlen), then
++** read (cmd,status,rid,frmlen,data[frmlen]) back
++**
++** Configure: write (cmd,status,rid,frmlen,data[frmlen])
++**
++** Possibly bogus special handling of ACX1xx_IE_SCAN_STATUS removed
++*/
++
++ /* now write the parameters of the command if needed */
++ acklen = buflen + 4 + BOGUS_SAFETY_PADDING;
++ blocklen = buflen;
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++ if (cmd == ACX1xx_CMD_INTERROGATE) {
++ blocklen = 4;
++ acklen = buflen + 4;
++ }
++ memcpy(loc->data, buffer, blocklen);
++ }
++ blocklen += 4; /* account for cmd,status */
++
++ /* obtain the I/O pipes */
++ outpipe = usb_sndctrlpipe(usbdev, 0);
++ inpipe = usb_rcvctrlpipe(usbdev, 0);
++ acxlog(L_CTL, "ctrl inpipe=0x%X outpipe=0x%X\n", inpipe, outpipe);
++ acxlog(L_CTL, "sending USB control msg (out) (blocklen=%d)\n", blocklen);
++ if (acx_debug & L_DATA)
++ acx_dump_bytes(loc, blocklen);
++
++ result = usb_control_msg(usbdev, outpipe,
++ ACX_USB_REQ_CMD, /* request */
++ USB_TYPE_VENDOR|USB_DIR_OUT, /* requesttype */
++ 0, /* value */
++ 0, /* index */
++ loc, /* dataptr */
++ blocklen, /* size */
++ USB_CTRL_HARD_TIMEOUT /* timeout in ms */
++ );
++ acxlog(L_CTL, "wrote %d bytes\n", result);
++ if (result < 0) {
++ goto bad;
++ }
++
++ /* check for device acknowledge */
++ acxlog(L_CTL, "sending USB control msg (in) (acklen=%d)\n", acklen);
++ loc->status = 0; /* delete old status flag -> set to IDLE */
++//shall we zero out the rest?
++ result = usb_control_msg(usbdev, inpipe,
++ ACX_USB_REQ_CMD, /* request */
++ USB_TYPE_VENDOR|USB_DIR_IN, /* requesttype */
++ 0, /* value */
++ 0, /* index */
++ loc, /* dataptr */
++ acklen, /* size */
++ USB_CTRL_HARD_TIMEOUT /* timeout in ms */
++ );
++ if (result < 0) {
++ printk("%s: "FUNC"(): USB read error %d\n", devname, result);
++ goto bad;
++ }
++ if (acx_debug & L_CTL) {
++ printk("read %d bytes: ", result);
++ acx_dump_bytes(loc, result);
++ }
++
++//check for result==buflen+4? Was seen:
++//interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4)
++//issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,buflen:8,type:4111)
++//ctrl inpipe=0x80000280 outpipe=0x80000200
++//sending USB control msg (out) (blocklen=8)
++//01 00 00 00 0F 10 04 00
++//wrote 8 bytes
++//sending USB control msg (in) (acklen=12) sizeof(loc->data
++//read 4 bytes <==== MUST BE 12!!
++
++ cmd_status = le16_to_cpu(loc->status);
++ if (cmd_status != 1) {
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s)\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status));
++ /* TODO: goto bad; ? */
++ }
++ if ((cmd == ACX1xx_CMD_INTERROGATE) && buffer && buflen) {
++ memcpy(buffer, loc->data, buflen);
++ acxlog(L_CTL, "response frame: cmd=0x%04X status=%d\n",
++ le16_to_cpu(loc->cmd),
++ cmd_status);
++ }
++ kfree(loc);
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ kfree(loc);
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx100usb_e_probe()
++**
++** Inputs:
++** dev -> Pointer to usb_device structure that may or may not be claimed
++** ifNum -> Interface number
++** devID -> Device ID (vendor and product specific stuff)
++************************************************************************
++** Returns:
++** (void *) Pointer to (custom) driver context or NULL if we are not interested
++** or unable to handle the offered device.
++**
++** Description:
++** This function is invoked by the kernel's USB core whenever a new device is
++** attached to the system or the module is loaded. It is presented a usb_device
++** structure from which information regarding the device is obtained and evaluated.
++** In case this driver is able to handle one of the offered devices, it returns
++** a non-null pointer to a driver context and thereby claims the device.
++*/
++static void
++acx_netdev_init(struct net_device *dev) {}
++
++static int
++acx100usb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID)
++{
++ struct usb_device *usbdev = interface_to_usbdev(intf);
++ wlandevice_t *priv = NULL;
++ struct net_device *dev = NULL;
++ struct usb_config_descriptor *config;
++ struct usb_endpoint_descriptor *epdesc;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ struct usb_host_endpoint *ep;
++#endif
++ struct usb_interface_descriptor *ifdesc;
++ const char* msg;
++ int numconfigs, numfaces, numep;
++ int result = OK;
++ int i;
++
++ FN_ENTER;
++
++ /* First check if this is the "unbooted" hardware */
++ if ((usbdev->descriptor.idVendor == ACX100_VENDOR_ID)
++ && (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED)) {
++ /* Boot the device (i.e. upload the firmware) */
++ acx100usb_boot(usbdev);
++
++ /* OK, we are done with booting. Normally, the
++ ** ID for the unbooted device should disappear
++ ** and it will not need a driver anyway...so
++ ** return a NULL
++ */
++ acxlog(L_INIT, "finished booting, returning from probe()\n");
++ result = OK; /* success */
++ goto end;
++ }
++
++ if ((usbdev->descriptor.idVendor != ACX100_VENDOR_ID)
++ || (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED)) {
++ goto end_nodev;
++ }
++
++ /* Ok, so it's our device and it is already booted */
++
++ /* Allocate memory for a network device */
++ dev = alloc_netdev(sizeof(wlandevice_t), "wlan%d", acx_netdev_init);
++ /* (NB: memsets to 0 entire area) */
++ if (!dev) {
++ msg = "acx: no memory for netdev\n";
++ goto end_nomem;
++ }
++ dev->init = (void *)&acx100usb_e_init_network_device;
++
++ /* Setup private driver context */
++ priv = netdev_priv(dev);
++ priv->netdev = dev;
++ priv->dev_type = DEVTYPE_USB;
++ priv->chip_type = CHIPTYPE_ACX100;
++ /* FIXME: should be read from register (via firmware) using standard ACX code */
++ priv->radio_type = RADIO_MAXIM_0D;
++ priv->usbdev = usbdev;
++
++ spin_lock_init(&priv->lock); /* initial state: unlocked */
++ sema_init(&priv->sem, 1); /* initial state: 1 (upped) */
++
++ /* Initialize the device context and also check
++ ** if this is really the hardware we know about.
++ ** If not sure, at least notify the user that he
++ ** may be in trouble...
++ */
++ numconfigs = (int)usbdev->descriptor.bNumConfigurations;
++ if (numconfigs != 1)
++ printk("acx: number of configurations is %d, "
++ "this driver only knows how to handle 1, "
++ "be prepared for surprises\n", numconfigs);
++
++ config = &usbdev->config->desc;
++ numfaces = config->bNumInterfaces;
++ if (numfaces != 1)
++ printk("acx: number of interfaces is %d, "
++ "this driver only knows how to handle 1, "
++ "be prepared for surprises\n", numfaces);
++
++ ifdesc = &intf->altsetting->desc;
++ numep = ifdesc->bNumEndpoints;
++ acxlog(L_DEBUG, "# of endpoints: %d\n", numep);
++
++ /* obtain information about the endpoint
++ ** addresses, begin with some default values
++ */
++ priv->bulkoutep = 1;
++ priv->bulkinep = 1;
++ for (i = 0; i < numep; i++) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ ep = usbdev->ep_in[i];
++ if (!ep)
++ continue;
++ epdesc = &ep->desc;
++#else
++ epdesc = usb_epnum_to_ep_desc(usbdev, i);
++ if (!epdesc)
++ continue;
++#endif
++ if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) {
++ if (epdesc->bEndpointAddress & 0x80)
++ priv->bulkinep = epdesc->bEndpointAddress & 0xF;
++ else
++ priv->bulkoutep = epdesc->bEndpointAddress & 0xF;
++ }
++ }
++ acxlog(L_DEBUG, "bulkout ep: 0x%X\n", priv->bulkoutep);
++ acxlog(L_DEBUG, "bulkin ep: 0x%X\n", priv->bulkinep);
++
++ /* Set the packet-size equivalent to the buffer size */
++ /* already done by memset: priv->rxtruncsize = 0; */
++ acxlog(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n",
++ (int) TXBUFSIZE, (int) RXBUFSIZE);
++
++ priv->tx_free = ACX100_USB_NUM_BULK_URBS;
++ /* already done by memset:
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ priv->usb_tx[i].busy = 0;
++ }
++ */
++
++ /* Setup URBs for bulk-in/out messages */
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ priv->bulkrx_urbs[i] = alloc_urb(0, GFP_KERNEL);
++ if (!priv->bulkrx_urbs[i]) {
++ msg = "acx: no memory for input URB\n";
++ goto end_nomem;
++ }
++ priv->bulkrx_urbs[i]->status = 0;
++
++ priv->usb_tx[i].urb = alloc_urb(0, GFP_KERNEL);
++ if (!priv->usb_tx[i].urb) {
++ msg = "acx: no memory for output URB\n";
++ goto end_nomem;
++ }
++ priv->usb_tx[i].urb->status = 0;
++
++ priv->usb_tx[i].priv = priv;
++ }
++
++ usb_set_intfdata(intf, priv);
++ SET_NETDEV_DEV(dev, &intf->dev);
++
++ /* Register the network device */
++ acxlog(L_INIT, "registering network device\n");
++ result = register_netdev(dev);
++ if (result != 0) {
++ msg = "acx: failed to register network device "
++ "for USB WLAN (errcode=%d)\n";
++ goto end_nomem;
++ }
++#ifdef CONFIG_PROC_FS
++ if (OK != acx_proc_register_entries(dev)) {
++ printk("acx: /proc registration failed\n");
++ }
++#endif
++
++ printk("acx: USB module " WLAN_RELEASE " loaded successfully\n");
++
++#if CMD_DISCOVERY
++ great_inquisitor(priv);
++#endif
++
++ /* Everything went OK, we are happy now */
++ result = OK;
++ goto end;
++
++end_nomem:
++ printk(msg, result);
++
++ if (dev) {
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ usb_free_urb(priv->bulkrx_urbs[i]);
++ usb_free_urb(priv->usb_tx[i].urb);
++ }
++ free_netdev(dev);
++ }
++ result = -ENOMEM;
++ goto end;
++
++end_nodev:
++
++ /* no device we could handle, return error. */
++ result = -EIO;
++
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx100usb_e_disconnect():
++** Inputs:
++** dev -> Pointer to usb_device structure handled by this module
++** devContext -> Pointer to own device context (acx100usb_context)
++************************************************************************
++** Description:
++** This function is invoked whenever the user pulls the plug from the USB
++** device or the module is removed from the kernel. In these cases, the
++** network devices have to be taken down and all allocated memory has
++** to be freed.
++*/
++static void
++acx100usb_e_disconnect(struct usb_interface *intf)
++{
++ wlandevice_t *priv = usb_get_intfdata(intf);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++ /* No WLAN device...no sense */
++ if (!priv)
++ goto end;
++
++ /*
++ * We get the sem *after* FLUSH to avoid a deadlock.
++ * See pci.c:acx_s_down() for deails.
++ */
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(priv);
++
++ acx_lock(priv, flags);
++
++ /* I wonder if above is enough to prevent tx/rx callbacks
++ ** to start queue again? Like this:
++ ** complete_rx -> acx_l_process_rxbuf -> associated -> acx_start_queue()
++ ** Oh well... */
++
++ /* This device exists no more. */
++ usb_set_intfdata(intf, NULL);
++
++ /* stop the transmit queue */
++ if (priv->netdev) {
++ acx_stop_queue(priv->netdev, "on USB disconnect");
++#ifdef CONFIG_PROC_FS
++ acx_proc_unregister_entries(priv->netdev);
++#endif
++ }
++
++ /* now abort pending URBs and free them */
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ acx_unlink_and_free_urb(priv->bulkrx_urbs[i]);
++ acx_unlink_and_free_urb(priv->usb_tx[i].urb);
++ }
++
++ acx_unlock(priv, flags);
++ acx_sem_unlock(priv);
++
++ /* Unregister the network devices */
++ if (priv->netdev) {
++ unregister_netdev(priv->netdev);
++ free_netdev(priv->netdev);
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx100usb_boot():
++** Inputs:
++** usbdev -> Pointer to kernel's usb_device structure
++** endpoint -> Address of the endpoint for control transfers
++************************************************************************
++** Returns:
++** (int) Errorcode or 0 on success
++**
++** Description:
++** This function triggers the loading of the firmware image from harddisk
++** and then uploads the firmware to the USB device. After uploading the
++** firmware and transmitting the checksum, the device resets and appears
++** as a new device on the USB bus (the device we can finally deal with)
++*/
++static int
++acx100usb_boot(struct usb_device *usbdev)
++{
++ static const char filename[] = "tiacx100usb";
++
++ char *firmware = NULL;
++ char *usbbuf;
++ unsigned int offset;
++ unsigned int len, inpipe, outpipe;
++ u32 checksum;
++ u32 size;
++ int result;
++
++ FN_ENTER;
++
++ usbbuf = kmalloc(ACX100_USB_RWMEM_MAXLEN, GFP_KERNEL);
++ if (!usbbuf) {
++ printk(KERN_ERR "acx: no memory for USB transfer buffer ("
++ STRING(ACX100_USB_RWMEM_MAXLEN)" bytes)\n");
++ result = -ENOMEM;
++ goto end;
++ }
++ firmware = (char *)acx_s_read_fw(&usbdev->dev, filename, &size);
++ if (!firmware) {
++ result = -EIO;
++ goto end;
++ }
++ acxlog(L_INIT, "firmware size: %d bytes\n", size);
++
++ /* Obtain the I/O pipes */
++ outpipe = usb_sndctrlpipe(usbdev, 0);
++ inpipe = usb_rcvctrlpipe(usbdev, 0);
++
++ /* now upload the firmware, slice the data into blocks */
++ offset = 8;
++ while (offset < size) {
++ len = size - offset;
++ if (len >= ACX100_USB_RWMEM_MAXLEN) {
++ len = ACX100_USB_RWMEM_MAXLEN;
++ }
++ acxlog(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
++ len, offset);
++ result = 0;
++ memcpy(usbbuf, firmware + offset, len);
++ result = usb_control_msg(usbdev, outpipe,
++ ACX_USB_REQ_UPLOAD_FW,
++ USB_TYPE_VENDOR|USB_DIR_OUT,
++ size - 8, /* value */
++ 0, /* index */
++ usbbuf, /* dataptr */
++ len, /* size */
++ 3000 /* timeout in ms */
++ );
++ offset += len;
++ if (result < 0) {
++#if ACX_DEBUG
++ printk(KERN_ERR "acx: error %d (%s) during upload "
++ "of firmware, aborting\n", result,
++ acx100usb_pstatus(result));
++#else
++ printk(KERN_ERR "acx: error %d during upload "
++ "of firmware, aborting\n", result);
++#endif
++ goto end;
++ }
++ }
++
++ /* finally, send the checksum and reboot the device */
++ checksum = le32_to_cpu(*(u32 *)firmware);
++ /* is this triggers the reboot? */
++ result = usb_control_msg(usbdev, outpipe,
++ ACX_USB_REQ_UPLOAD_FW,
++ USB_TYPE_VENDOR|USB_DIR_OUT,
++ checksum & 0xffff, /* value */
++ checksum >> 16, /* index */
++ NULL, /* dataptr */
++ 0, /* size */
++ 3000 /* timeout in ms */
++ );
++ if (result < 0) {
++ printk(KERN_ERR "acx: error %d during tx of checksum, "
++ "aborting\n", result);
++ goto end;
++ }
++ result = usb_control_msg(usbdev, inpipe,
++ ACX_USB_REQ_ACK_CS,
++ USB_TYPE_VENDOR|USB_DIR_IN,
++ checksum & 0xffff, /* value */
++ checksum >> 16, /* index */
++ usbbuf, /* dataptr */
++ 8, /* size */
++ 3000 /* timeout in ms */
++ );
++ if (result < 0) {
++ printk(KERN_ERR "acx: error %d during ACK of checksum, "
++ "aborting\n", result);
++ goto end;
++ }
++ if (*usbbuf != 0x10) {
++ kfree(usbbuf);
++ printk(KERN_ERR "acx: invalid checksum?\n");
++ result = -EINVAL;
++ goto end;
++ }
++ result = 0;
++end:
++ vfree(firmware);
++ kfree(usbbuf);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx100usb_e_init_network_device():
++** Inputs:
++** dev -> Pointer to network device
++************************************************************************
++** Description:
++** Basic setup of a network device for use with the WLAN device.
++*/
++static int
++acx100usb_e_init_network_device(struct net_device *dev)
++{
++ wlandevice_t *priv;
++ int result = 0;
++
++ FN_ENTER;
++
++ /* Setup the device and stop the queue */
++ ether_setup(dev);
++ acx_stop_queue(dev, "on init");
++
++ priv = netdev_priv(dev);
++
++ acx_sem_lock(priv);
++
++ /* put the ACX100 out of sleep mode */
++ acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
++
++ /* Register the callbacks for the network device functions */
++ dev->open = &acx100usb_e_open;
++ dev->stop = &acx100usb_e_close;
++ dev->hard_start_xmit = (void *)&acx_i_start_xmit;
++ dev->get_stats = (void *)&acx_e_get_stats;
++ dev->get_wireless_stats = (void *)&acx_e_get_wireless_stats;
++#if WIRELESS_EXT >= 13
++ dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++#else
++ dev->do_ioctl = (void *)&acx_e_ioctl_old;
++#endif
++ dev->set_multicast_list = (void *)&acx100usb_i_set_rx_mode;
++#ifdef HAVE_TX_TIMEOUT
++ dev->tx_timeout = &acx100usb_i_tx_timeout;
++ dev->watchdog_timeo = 4 * HZ;
++#endif
++ result = acx_s_init_mac(dev);
++ if (OK != result)
++ goto end;
++ result = acx_s_set_defaults(priv);
++ if (OK != result) {
++ printk("%s: acx_set_defaults FAILED\n", dev->name);
++ goto end;
++ }
++
++ SET_MODULE_OWNER(dev);
++end:
++ acx_sem_unlock(priv);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx100usb_e_open
++** This function is called when the user sets up the network interface.
++** It initializes a management timer, sets up the USB card and starts
++** the network tx queue and USB receive.
++*/
++static int
++acx100usb_e_open(struct net_device *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++ acx_sem_lock(priv);
++
++ /* put the ACX100 out of sleep mode */
++ acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0);
++
++ acx_init_task_scheduler(priv);
++
++ init_timer(&priv->mgmt_timer);
++ priv->mgmt_timer.function = acx_i_timer;
++ priv->mgmt_timer.data = (unsigned long)priv;
++
++ /* set ifup to 1, since acx_start needs it */
++ SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++ acx_s_start(priv);
++
++ acx_start_queue(dev, "on open");
++
++ acx_lock(priv, flags);
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ acx100usb_l_poll_rx(priv, i);
++ }
++ acx_unlock(priv, flags);
++
++ WLAN_MOD_INC_USE_COUNT;
++
++ acx_sem_unlock(priv);
++
++ FN_EXIT0;
++ return 0;
++}
++
++
++/***********************************************************************
++** acx100usb_l_poll_rx
++** This function initiates a bulk-in USB transfer (in case the interface
++** is up).
++*/
++static void
++acx100usb_l_poll_rx(wlandevice_t *priv, int number)
++{
++ struct usb_device *usbdev;
++ rxbuffer_t *inbuf;
++ acx_usb_bulk_context_t *rxcon;
++ struct urb *rxurb;
++ int errcode;
++ unsigned int inpipe;
++
++ FN_ENTER;
++
++ if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ goto end;
++ }
++
++ rxcon = &priv->rxcons[number];
++ inbuf = &priv->bulkins[number];
++ rxurb = priv->bulkrx_urbs[number];
++ usbdev = priv->usbdev;
++
++ rxcon->device = priv;
++ rxcon->number = number;
++ inpipe = usb_rcvbulkpipe(usbdev, priv->bulkinep);
++ if (rxurb->status == -EINPROGRESS) {
++ printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n");
++ /* FIXME: this is nasty, receive is being cancelled by this code
++ * on the other hand, this should not happen anyway...
++ */
++ usb_unlink_urb(rxurb);
++ }
++ rxurb->actual_length = 0;
++ usb_fill_bulk_urb(rxurb, usbdev, inpipe,
++ inbuf, /* dataptr */
++ RXBUFSIZE, /* size */
++ acx100usb_i_complete_rx, /* handler */
++ rxcon /* handler param */
++ );
++ rxurb->transfer_flags = QUEUE_BULK;
++
++ /* ATOMIC: we may be called from complete_rx() usb callback */
++ errcode = submit_urb(rxurb, GFP_ATOMIC);
++ /* FIXME: evaluate the error code! */
++ acxlog(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n",
++ number, inpipe, (int) RXBUFSIZE, errcode);
++
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx100usb_i_complete_rx():
++** Inputs:
++** urb -> Pointer to USB request block
++** regs -> Pointer to register-buffer for syscalls (see asm/ptrace.h)
++************************************************************************
++** Description:
++** This function is invoked by USB subsystem whenever a bulk receive
++** request returns.
++** The received data is then committed to the network stack and the next
++** USB receive is triggered.
++*/
++static void
++acx100usb_i_complete_rx(struct urb *urb, struct pt_regs *regs)
++{
++ wlandevice_t *priv;
++ rxbuffer_t *ptr;
++ rxbuffer_t *inbuf;
++ unsigned long flags;
++ int size, number, remsize, packetsize;
++
++ FN_ENTER;
++
++ if (!urb->context) {
++ printk(KERN_ERR "acx: error, urb context was NULL\n");
++ goto end; /* at least try to prevent the worst */
++ }
++
++ priv = ((acx_usb_bulk_context_t *)urb->context)->device;
++
++ acx_lock(priv, flags);
++
++ /* TODO: we maybe need to check whether urb was unlinked
++ ** (happens on disconnect and close, see there). How? */
++
++ number = ((acx_usb_bulk_context_t *)urb->context)->number;
++ size = urb->actual_length;
++ remsize = size;
++
++ acxlog(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n",
++ number, urb->status, size);
++
++ inbuf = &priv->bulkins[number];
++ ptr = inbuf;
++
++ /* check if the transfer was aborted */
++ switch (urb->status) {
++ case 0: /* No error */
++ break;
++ case -EOVERFLOW:
++ printk(KERN_ERR "acx: error in rx, data overrun -> emergency stop\n");
++ /* LOCKING BUG! acx100usb_e_close(priv->netdev); */
++ goto end_unlock;
++ case -ECONNRESET:
++ goto do_poll_rx;
++ default:
++ priv->stats.rx_errors++;
++ printk("acx: rx error (urb status=%d)\n", urb->status);
++ goto do_poll_rx;
++ }
++
++ if (!size)
++ printk("acx: warning, encountered zerolength rx packet\n");
++
++ if (urb->transfer_buffer != inbuf)
++ goto do_poll_rx;
++
++ /* check if previous frame was truncated
++ ** FIXME: this code can only handle truncation
++ ** of consecutive packets!
++ */
++ if (priv->rxtruncsize) {
++ int tail_size;
++
++ ptr = &priv->rxtruncbuf;
++ packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE;
++ if (acx_debug & L_USBRXTX) {
++ printk("handling truncated frame (truncsize=%d size=%d "
++ "packetsize(from trunc)=%d)\n",
++ priv->rxtruncsize, size, packetsize);
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ acx_dump_bytes(inbuf, RXBUF_HDRSIZE);
++ }
++
++ /* bytes needed for rxtruncbuf completion: */
++ tail_size = packetsize - priv->rxtruncsize;
++
++ if (size < tail_size) {
++ /* there is not enough data to complete this packet,
++ ** simply append the stuff to the truncation buffer
++ */
++ memcpy(((char *)ptr) + priv->rxtruncsize, inbuf, size);
++ priv->rxtruncsize += size;
++ remsize = 0;
++ } else {
++ /* ok, this data completes the previously
++ ** truncated packet. copy it into a descriptor
++ ** and give it to the rest of the stack */
++
++ /* append tail to previously truncated part
++ ** NB: priv->rxtruncbuf (pointed to by ptr) can't
++ ** overflow because this is already checked before
++ ** truncation buffer was filled. See below,
++ ** "if (packetsize > sizeof(rxbuffer_t))..." code */
++ memcpy(((char *)ptr) + priv->rxtruncsize, inbuf, tail_size);
++
++ if (acx_debug & L_USBRXTX) {
++ printk("full trailing packet + 12 bytes:\n");
++ acx_dump_bytes(inbuf, tail_size + RXBUF_HDRSIZE);
++ }
++ acx_l_process_rxbuf(priv, ptr);
++ priv->rxtruncsize = 0;
++ ptr = (rxbuffer_t *) (((char *)inbuf) + tail_size);
++ remsize -= tail_size;
++ }
++ acxlog(L_USBRXTX, "post-merge size=%d remsize=%d\n",
++ size, remsize);
++ }
++
++ /* size = USB data block size
++ ** remsize = unprocessed USB bytes left
++ ** ptr = current pos in USB data block
++ */
++ while (remsize) {
++ if (remsize < RXBUF_HDRSIZE) {
++ printk("acx: truncated rx header (%d bytes)!\n",
++ remsize);
++ break;
++ }
++ packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE;
++ acxlog(L_USBRXTX, "packet with packetsize=%d\n", packetsize);
++ if (packetsize > sizeof(rxbuffer_t)) {
++ printk("acx: packet exceeds max wlan "
++ "frame size (%d > %d). size=%d\n",
++ packetsize, (int) sizeof(rxbuffer_t), size);
++ /* FIXME: put some real error-handling in here! */
++ break;
++ }
++
++ /* skip null packets (does this really happen?!) */
++ if (packetsize == RXBUF_HDRSIZE) {
++ remsize -= RXBUF_HDRSIZE;
++ if (acx_debug & L_USBRXTX) {
++ printk("acx: null packet, new remsize=%d. "
++ "header follows:\n", remsize);
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ }
++ ptr = (rxbuffer_t *)(((char *)ptr) + RXBUF_HDRSIZE);
++ continue;
++ }
++
++ if (packetsize > remsize) {
++ /* frame truncation handling */
++ if (acx_debug & L_USBRXTX) {
++ printk("need to truncate packet, "
++ "packetsize=%d remsize=%d "
++ "size=%d\n",
++ packetsize, remsize, size);
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ }
++ memcpy(&priv->rxtruncbuf, ptr, remsize);
++ priv->rxtruncsize = remsize;
++ break;
++ } else { /* packetsize <= remsize */
++ /* now handle the received data */
++ acx_l_process_rxbuf(priv, ptr);
++
++ ptr = (rxbuffer_t *)(((char *)ptr) + packetsize);
++ remsize -= packetsize;
++ if ((acx_debug & L_USBRXTX) && remsize) {
++ printk("more than one packet in buffer, "
++ "second packet hdr follows\n");
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ }
++ }
++ }
++
++do_poll_rx:
++ /* look for the next rx */
++ if (priv->dev_state_mask & ACX_STATE_IFACE_UP) {
++ /* receive of frame completed, now look for the next one */
++ acx100usb_l_poll_rx(priv, number);
++ }
++
++end_unlock:
++ acx_unlock(priv, flags);
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx100usb_i_complete_tx():
++** Inputs:
++** urb -> Pointer to USB request block
++** regs -> Pointer to register-buffer for syscalls (see asm/ptrace.h)
++************************************************************************
++** Description:
++** This function is invoked upon termination of a USB transfer. As the
++** USB device is only capable of sending a limited amount of bytes per
++** transfer to the bulk-out endpoint, this routine checks if there are
++** more bytes to send and triggers subsequent transfers. In case the
++** transfer size exactly matches the maximum bulk-out size, it triggers
++** a transfer of a null-frame, telling the card that this is it. Upon
++** completion of a frame, it checks whether the Tx ringbuffer contains
++** more data to send and invokes the Tx routines if this is the case.
++** If there are no more occupied Tx descriptors, the Tx Mutex is unlocked
++** and the network queue is switched back to life again.
++**
++** FIXME: unlike PCI code, we do not analyze tx rate used, retries, etc...
++** Thus we have no automatic rate control in USB!
++*/
++static void
++acx100usb_i_complete_tx(struct urb *urb, struct pt_regs *regs)
++{
++ wlandevice_t *priv;
++ usb_tx_t *tx;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ if (!urb->context) {
++ printk(KERN_ERR "acx: error, NULL context in tx completion callback\n");
++ /* FIXME: real error-handling code must go here! */
++ goto end;
++ }
++
++ tx = (usb_tx_t *)urb->context;
++ priv = tx->priv;
++
++ acx_lock(priv, flags);
++
++ /* TODO: we maybe need to check whether urb was unlinked
++ ** (happens on disconnect and close, see there). How? */
++
++ acxlog(L_USBRXTX, "RETURN TX (%p): status=%d size=%d\n",
++ tx, urb->status, urb->actual_length);
++
++ /* handle USB transfer errors */
++ switch (urb->status) {
++ case 0: /* No error */
++ break;
++ case -ECONNRESET:
++ break;
++ /* FIXME: real error-handling code here please */
++ default:
++ printk(KERN_ERR "acx: tx error, urb status=%d\n", urb->status);
++ /* FIXME: real error-handling code here please */
++ }
++
++ /* free the URB and check for more data */
++ priv->tx_free++;
++ tx->busy = 0;
++
++/* end_unlock: */
++ acx_unlock(priv, flags);
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx100usb_e_close():
++**
++** This function stops the network functionality of the interface (invoked
++** when the user calls ifconfig <wlan> down). The tx queue is halted and
++** the device is marked as down. In case there were any pending USB bulk
++** transfers, these are unlinked (asynchronously). The module in-use count
++** is also decreased in this function.
++*/
++static int
++acx100usb_e_close(struct net_device *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++#if WE_STILL_DONT_CARE_ABOUT_IT
++ /* Transmit a disassociate frame */
++ lock
++ acx_l_transmit_disassoc(priv, &client);
++ unlock
++#endif
++
++ /*
++ * We get the sem *after* FLUSH to avoid a deadlock.
++ * See pci.c:acx_s_down() for deails.
++ */
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(priv);
++
++ /* stop the transmit queue, mark the device as DOWN */
++ acx_lock(priv, flags);
++ acx_stop_queue(dev, "on iface stop");
++ CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP);
++
++ /* I wonder if above is enough to prevent tx/rx callbacks
++ ** to start queue again? Like this:
++ ** complete_rx -> acx_l_process_rxbuf -> associated -> acx_start_queue()
++ ** Oh well... */
++
++ /* stop pending rx/tx urb transfers */
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ acx_unlink_and_free_urb(priv->bulkrx_urbs[i]);
++ acx_unlink_and_free_urb(priv->usb_tx[i].urb);
++ }
++ acx_unlock(priv, flags);
++
++ /* disable rx and tx */
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++
++ /* power down the device */
++ acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ acx_sem_unlock(priv);
++
++ /* decrease module-in-use count (if necessary) */
++
++ WLAN_MOD_DEC_USE_COUNT;
++
++ FN_EXIT0;
++ return 0;
++}
++
++
++/***************************************************************
++** acxusb_l_alloc_tx
++** Actually returns a usb_tx_t* ptr
++*/
++tx_t*
++acxusb_l_alloc_tx(wlandevice_t* priv)
++{
++ int i;
++ usb_tx_t *tx = NULL;
++
++ FN_ENTER;
++
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ if (!priv->usb_tx[i].busy) {
++ tx = &priv->usb_tx[i];
++ tx->busy = 1;
++ break;
++ }
++ }
++ if (i >= ACX100_USB_NUM_BULK_URBS) {
++ printk("acx: tx buffers full\n");
++ }
++
++ FN_EXIT0;
++
++ return (tx_t*)tx;
++}
++
++
++/***************************************************************
++*/
++void*
++acxusb_l_get_txbuf(wlandevice_t *priv, tx_t* tx_opaque)
++{
++ usb_tx_t* tx = (usb_tx_t*)tx_opaque;
++ return &tx->bulkout.data;
++}
++
++
++/***************************************************************
++** acxusb_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++*/
++void
++acxusb_l_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int wlanpkt_len)
++{
++ struct usb_device *usbdev;
++ struct urb* txurb;
++ usb_tx_t* tx;
++ usb_txbuffer_t* txbuf;
++ client_t *clt;
++ wlan_hdr_t* whdr;
++ unsigned int outpipe;
++ int ucode;
++ u8 rate100;
++
++ FN_ENTER;
++
++ tx = ((usb_tx_t *)tx_opaque);
++ txurb = tx->urb;
++ txbuf = &tx->bulkout;
++ whdr = (wlan_hdr_t *)txbuf->data;
++
++ priv->tx_free--;
++ acxlog(L_DEBUG, "using buf#%d free=%d len=%d\n",
++ (int)(tx - priv->usb_tx),
++ priv->tx_free, wlanpkt_len);
++
++ switch (priv->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(priv, whdr->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = priv->ap_client;
++ break;
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ if (unlikely(clt && !clt->rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* used in tx cleanup routine for auto rate and accounting: */
++//TODO: currently unused - fix that
++ tx->txc = clt;
++
++ rate100 = clt ? clt->rate_100 : priv->rate_bcast100;
++
++ /* fill the USB transfer header */
++ txbuf->desc = cpu_to_le16(USB_TXBUF_TXDESC);
++ txbuf->MPDUlen = cpu_to_le16(wlanpkt_len);
++ txbuf->ctrl1 = 0;
++ txbuf->ctrl2 = 0;
++ txbuf->hostData = cpu_to_le32(wlanpkt_len | (rate100 << 24));
++ if (1 == priv->preamble_cur)
++ SET_BIT(txbuf->ctrl1, DESC_CTL_SHORT_PREAMBLE);
++ SET_BIT(txbuf->ctrl1, DESC_CTL_FIRSTFRAG);
++ txbuf->txRate = rate100;
++ txbuf->index = 1;
++ txbuf->dataLength = cpu_to_le16(wlanpkt_len);
++
++ if ( (WF_FC_FTYPEi & whdr->fc) == WF_FTYPE_DATAi )
++ SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_ISDATA));
++ if (mac_is_directed(whdr->a1))
++ SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_DIRECTED));
++ else if (mac_is_bcast(whdr->a1))
++ SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_BROADCAST));
++
++ if (acx_debug & L_DATA) {
++ printk("dump of bulk out urb:\n");
++ acx_dump_bytes(txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE);
++ }
++
++ if (txurb->status == -EINPROGRESS) {
++ printk("acx: trying to submit tx urb while already in progress\n");
++ }
++
++ /* now schedule the USB transfer */
++ usbdev = priv->usbdev;
++ outpipe = usb_sndbulkpipe(usbdev, priv->bulkoutep);
++//can be removed, please try & test:
++ tx->priv = priv;
++
++ usb_fill_bulk_urb(txurb, usbdev, outpipe,
++ txbuf, /* dataptr */
++ wlanpkt_len + USB_TXBUF_HDRSIZE, /* size */
++ acx100usb_i_complete_tx, /* handler */
++ tx /* handler param */
++ );
++
++ txurb->transfer_flags = QUEUE_BULK|ZERO_PACKET;
++ ucode = submit_urb(txurb, GFP_ATOMIC);
++ acxlog(L_USBRXTX, "SUBMIT TX (%p): outpipe=0x%X buf=%p txsize=%d "
++ "errcode=%d\n", tx, outpipe, txbuf,
++ wlanpkt_len + USB_TXBUF_HDRSIZE, ucode);
++
++ if (ucode) {
++ printk(KERN_ERR "acx: submit_urb() error=%d txsize=%d\n",
++ ucode, wlanpkt_len + USB_TXBUF_HDRSIZE);
++
++ /* on error, just mark the frame as done and update
++ ** the statistics
++ */
++ priv->stats.tx_errors++;
++ tx->busy = 0;
++ priv->tx_free++;
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++static struct net_device_stats*
++acx_e_get_stats(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ return &priv->stats;
++}
++
++
++/***********************************************************************
++*/
++static struct iw_statistics*
++acx_e_get_wireless_stats(netdevice_t *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ FN_ENTER;
++ FN_EXIT0;
++ return &priv->wstats;
++}
++
++
++/***********************************************************************
++*/
++static void
++acx100usb_i_set_rx_mode(struct net_device *dev)
++{
++}
++
++
++/***********************************************************************
++*/
++#ifdef HAVE_TX_TIMEOUT
++static void
++acx100usb_i_tx_timeout(struct net_device *dev)
++{
++ wlandevice_t *priv = netdev_priv(dev);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++ acx_lock(priv, flags);
++ /* unlink the URBs */
++ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) {
++ if (priv->usb_tx[i].urb->status == -EINPROGRESS)
++ usb_unlink_urb(priv->usb_tx[i].urb);
++ }
++ /* TODO: stats update */
++ acx_unlock(priv, flags);
++
++ FN_EXIT0;
++}
++#endif
++
++
++/***********************************************************************
++** init_module():
++**
++** This function is invoked upon loading of the kernel module.
++** It registers itself at the kernel's USB subsystem.
++**
++** Returns: Errorcode on failure, 0 on success
++*/
++int __init
++acxusb_e_init_module(void)
++{
++ acxlog(L_INIT, "USB module " WLAN_RELEASE " initialized, "
++ "probing for devices...\n");
++ return usb_register(&acx100usb_driver);
++}
++
++
++
++/***********************************************************************
++** cleanup_module():
++**
++** This function is invoked as last step of the module unloading. It simply
++** deregisters this module at the kernel's USB subsystem.
++*/
++void __exit
++acxusb_e_cleanup_module()
++{
++ usb_deregister(&acx100usb_driver);
++}
++
++
++/***********************************************************************
++** DEBUG STUFF
++*/
++#if ACX_DEBUG
++
++#ifdef UNUSED
++static void
++dump_device(struct usb_device *usbdev)
++{
++ int i;
++ struct usb_config_descriptor *cd;
++
++ printk("acx device dump:\n");
++ printk(" devnum: %d\n", usbdev->devnum);
++ printk(" speed: %d\n", usbdev->speed);
++ printk(" tt: 0x%X\n", (unsigned int)(usbdev->tt));
++ printk(" ttport: %d\n", (unsigned int)(usbdev->ttport));
++ printk(" toggle[0]: 0x%X toggle[1]: 0x%X\n", (unsigned int)(usbdev->toggle[0]), (unsigned int)(usbdev->toggle[1]));
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8)
++ /* halted removed in 2.6.9-rc1 */
++ /* DOH, Canbreak... err... Mandrake decided to do their very own very
++ * special version "2.6.8.1" which already includes this change, so we
++ * need to blacklist that version already (i.e. 2.6.8) */
++ printk(" halted[0]: 0x%X halted[1]: 0x%X\n", usbdev->halted[0], usbdev->halted[1]);
++#endif
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ /* This saw a change after 2.6.10 */
++ printk(" ep_in wMaxPacketSize: ");
++ for (i = 0; i < 16; ++i)
++ printk("%d ", usbdev->ep_in[i]->desc.wMaxPacketSize);
++ printk("\n");
++ printk(" ep_out wMaxPacketSize: ");
++ for (i = 0; i < 15; ++i)
++ printk("%d ", usbdev->ep_out[i]->desc.wMaxPacketSize);
++ printk("\n");
++#else
++ printk(" epmaxpacketin: ");
++ for (i = 0; i < 16; i++)
++ printk("%d ", usbdev->epmaxpacketin[i]);
++ printk("\n");
++ printk(" epmaxpacketout: ");
++ for (i = 0; i < 16; i++)
++ printk("%d ", usbdev->epmaxpacketout[i]);
++ printk("\n");
++#endif
++ printk(" parent: 0x%X\n", (unsigned int)usbdev->parent);
++ printk(" bus: 0x%X\n", (unsigned int)usbdev->bus);
++#if NO_DATATYPE
++ printk(" configs: ");
++ for (i = 0; i < usbdev->descriptor.bNumConfigurations; i++)
++ printk("0x%X ", usbdev->config[i]);
++ printk("\n");
++#endif
++ printk(" actconfig: %p\n", usbdev->actconfig);
++ dump_device_descriptor(&usbdev->descriptor);
++
++ cd = &usbdev->config->desc;
++ dump_config_descriptor(cd);
++}
++
++
++/***********************************************************************
++*/
++static void
++dump_config_descriptor(struct usb_config_descriptor *cd)
++{
++ printk("Configuration Descriptor:\n");
++ if (!cd) {
++ printk("NULL\n");
++ return;
++ }
++ printk(" bLength: %d (0x%X)\n", cd->bLength, cd->bLength);
++ printk(" bDescriptorType: %d (0x%X)\n", cd->bDescriptorType, cd->bDescriptorType);
++ printk(" bNumInterfaces: %d (0x%X)\n", cd->bNumInterfaces, cd->bNumInterfaces);
++ printk(" bConfigurationValue: %d (0x%X)\n", cd->bConfigurationValue, cd->bConfigurationValue);
++ printk(" iConfiguration: %d (0x%X)\n", cd->iConfiguration, cd->iConfiguration);
++ printk(" bmAttributes: %d (0x%X)\n", cd->bmAttributes, cd->bmAttributes);
++ /* printk(" MaxPower: %d (0x%X)\n", cd->bMaxPower, cd->bMaxPower); */
++}
++
++
++static void
++dump_device_descriptor(struct usb_device_descriptor *dd)
++{
++ printk("Device Descriptor:\n");
++ if (!dd) {
++ printk("NULL\n");
++ return;
++ }
++ printk(" bLength: %d (0x%X)\n", dd->bLength, dd->bLength);
++ printk(" bDescriptortype: %d (0x%X)\n", dd->bDescriptorType, dd->bDescriptorType);
++ printk(" bcdUSB: %d (0x%X)\n", dd->bcdUSB, dd->bcdUSB);
++ printk(" bDeviceClass: %d (0x%X)\n", dd->bDeviceClass, dd->bDeviceClass);
++ printk(" bDeviceSubClass: %d (0x%X)\n", dd->bDeviceSubClass, dd->bDeviceSubClass);
++ printk(" bDeviceProtocol: %d (0x%X)\n", dd->bDeviceProtocol, dd->bDeviceProtocol);
++ printk(" bMaxPacketSize0: %d (0x%X)\n", dd->bMaxPacketSize0, dd->bMaxPacketSize0);
++ printk(" idVendor: %d (0x%X)\n", dd->idVendor, dd->idVendor);
++ printk(" idProduct: %d (0x%X)\n", dd->idProduct, dd->idProduct);
++ printk(" bcdDevice: %d (0x%X)\n", dd->bcdDevice, dd->bcdDevice);
++ printk(" iManufacturer: %d (0x%X)\n", dd->iManufacturer, dd->iManufacturer);
++ printk(" iProduct: %d (0x%X)\n", dd->iProduct, dd->iProduct);
++ printk(" iSerialNumber: %d (0x%X)\n", dd->iSerialNumber, dd->iSerialNumber);
++ printk(" bNumConfigurations: %d (0x%X)\n", dd->bNumConfigurations, dd->bNumConfigurations);
++}
++#endif /* UNUSED */
++
++#endif /* ACX_DEBUG */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan.c bt_kernel/drivers/net/wireless/tiacx/wlan.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/wlan.c 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,392 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++#include <linux/config.h>
++#include <linux/version.h>
++
++#include <linux/types.h>
++#include <linux/if_arp.h>
++#include <linux/wireless.h>
++#if WIRELESS_EXT >= 13
++#include <net/iw_handler.h>
++#endif
++
++#include "acx.h"
++
++
++/***********************************************************************
++*/
++#define LOG_BAD_EID(hdr,len,ie_ptr) acx_log_bad_eid(hdr, len, ((wlan_ie_t*)ie_ptr))
++
++#define IE_EID(ie_ptr) (((wlan_ie_t*)(ie_ptr))->eid)
++#define IE_LEN(ie_ptr) (((wlan_ie_t*)(ie_ptr))->len)
++#define OFFSET(hdr,off) (WLAN_HDR_A3_DATAP(hdr) + (off))
++
++
++/***********************************************************************
++** wlan_mgmt_decode_XXX
++**
++** Given a complete frame in f->hdr, sets the pointers in f to
++** the areas that correspond to the parts of the frame.
++**
++** Assumptions:
++** 1) f->len and f->hdr are already set
++** 2) f->len is the length of the MAC header + data, the FCS
++** is NOT included
++** 3) all members except len and hdr are zero
++** Arguments:
++** f frame structure
++**
++** Returns:
++** nothing
++**
++** Side effects:
++** frame structure members are pointing at their
++** respective portions of the frame buffer.
++*/
++void
++wlan_mgmt_decode_beacon(wlan_fr_beacon_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_BEACON;
++
++ /*-- Fixed Fields ----*/
++ f->ts = (u64 *) OFFSET(f->hdr, WLAN_BEACON_OFF_TS);
++ f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_BCN_INT);
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_CAPINFO);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_BEACON_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_FH_PARMS:
++ f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_DS_PARMS:
++ f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_CF_PARMS:
++ f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_IBSS_PARMS:
++ f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_TIM:
++ f->tim = (wlan_ie_tim_t *) ie_ptr;
++ break;
++ case WLAN_EID_ERP_INFO:
++ f->erp = (wlan_ie_erp_t *) ie_ptr;
++ break;
++ case WLAN_EID_COUNTRY:
++ /* was seen: 07 06 47 42 20 01 0D 14 */
++ case WLAN_EID_NONERP:
++ /* was seen from WRT54GS with OpenWrt: 2F 01 07 */
++ case WLAN_EID_GENERIC:
++ /* WPA: hostap code:
++ if (pos[1] >= 4 &&
++ pos[2] == 0x00 && pos[3] == 0x50 &&
++ pos[4] == 0xf2 && pos[5] == 1) {
++ wpa = pos;
++ wpa_len = pos[1] + 2;
++ }
++ TI x4 mode: seen DD 04 08 00 28 00
++ (08 00 28 is TI's OUI)
++ last byte is probably 0/1 - disabled/enabled
++ */
++ case WLAN_EID_RSN:
++ /* hostap does something with it:
++ rsn = pos;
++ rsn_len = pos[1] + 2;
++ */
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++#ifdef UNUSED
++void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t * f)
++{
++ f->type = WLAN_FSTYPE_ATIM;
++ /*-- Fixed Fields ----*/
++ /*-- Information elements */
++}
++#endif /* UNUSED */
++
++void
++wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t * f)
++{
++ f->type = WLAN_FSTYPE_DISASSOC;
++
++ /*-- Fixed Fields ----*/
++ f->reason = (u16 *) OFFSET(f->hdr, WLAN_DISASSOC_OFF_REASON);
++
++ /*-- Information elements */
++}
++
++
++void
++wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++
++ f->type = WLAN_FSTYPE_ASSOCREQ;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_CAP_INFO);
++ f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_LISTEN_INT);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++void
++wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t * f)
++{
++ f->type = WLAN_FSTYPE_ASSOCRESP;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_CAP_INFO);
++ f->status = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_STATUS);
++ f->aid = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_AID);
++
++ /*-- Information elements */
++ f->supp_rates = (wlan_ie_supp_rates_t *)
++ OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_SUPP_RATES);
++}
++
++
++void
++wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_REASSOCREQ;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CAP_INFO);
++ f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_LISTEN_INT);
++ f->curr_ap = (u8 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CURR_AP);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++void
++wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t * f)
++{
++ f->type = WLAN_FSTYPE_REASSOCRESP;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_CAP_INFO);
++ f->status = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_STATUS);
++ f->aid = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_AID);
++
++ /*-- Information elements */
++ f->supp_rates = (wlan_ie_supp_rates_t *)
++ OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_SUPP_RATES);
++}
++
++
++void
++wlan_mgmt_decode_probereq(wlan_fr_probereq_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_PROBEREQ;
++
++ /*-- Fixed Fields ----*/
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_PROBEREQ_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++/* TODO: decoding of beacon and proberesp can be merged (similar structure) */
++void
++wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_PROBERESP;
++
++ /*-- Fixed Fields ----*/
++ f->ts = (u64 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_TS);
++ f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_BCN_INT);
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_CAP_INFO);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_PROBERESP_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_FH_PARMS:
++ f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_DS_PARMS:
++ f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_CF_PARMS:
++ f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_IBSS_PARMS:
++ f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++void
++wlan_mgmt_decode_authen(wlan_fr_authen_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_AUTHEN;
++
++ /*-- Fixed Fields ----*/
++ f->auth_alg = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_ALG);
++ f->auth_seq = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_SEQ);
++ f->status = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_STATUS);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_AUTHEN_OFF_CHALLENGE);
++ if ((ie_ptr < end) && (IE_EID(ie_ptr) == WLAN_EID_CHALLENGE)) {
++ f->challenge = (wlan_ie_challenge_t *) ie_ptr;
++ }
++}
++
++
++void
++wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t * f)
++{
++ f->type = WLAN_FSTYPE_DEAUTHEN;
++
++ /*-- Fixed Fields ----*/
++ f->reason = (u16 *) OFFSET(f->hdr, WLAN_DEAUTHEN_OFF_REASON);
++
++ /*-- Information elements */
++}
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan_compat.h bt_kernel/drivers/net/wireless/tiacx/wlan_compat.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan_compat.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/wlan_compat.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,297 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++/*=============================================================*/
++/*------ Establish Platform Identity --------------------------*/
++/*=============================================================*/
++/* Key macros: */
++/* WLAN_CPU_FAMILY */
++#define WLAN_Ix86 1
++#define WLAN_PPC 2
++#define WLAN_Ix96 3
++#define WLAN_ARM 4
++#define WLAN_ALPHA 5
++#define WLAN_MIPS 6
++#define WLAN_HPPA 7
++#define WLAN_SPARC 8
++#define WLAN_SH 9
++#define WLAN_x86_64 10
++/* WLAN_CPU_CORE */
++#define WLAN_I386CORE 1
++#define WLAN_PPCCORE 2
++#define WLAN_I296 3
++#define WLAN_ARMCORE 4
++#define WLAN_ALPHACORE 5
++#define WLAN_MIPSCORE 6
++#define WLAN_HPPACORE 7
++/* WLAN_CPU_PART */
++#define WLAN_I386PART 1
++#define WLAN_MPC860 2
++#define WLAN_MPC823 3
++#define WLAN_I296SA 4
++#define WLAN_PPCPART 5
++#define WLAN_ARMPART 6
++#define WLAN_ALPHAPART 7
++#define WLAN_MIPSPART 8
++#define WLAN_HPPAPART 9
++/* WLAN_SYSARCH */
++#define WLAN_PCAT 1
++#define WLAN_MBX 2
++#define WLAN_RPX 3
++#define WLAN_LWARCH 4
++#define WLAN_PMAC 5
++#define WLAN_SKIFF 6
++#define WLAN_BITSY 7
++#define WLAN_ALPHAARCH 7
++#define WLAN_MIPSARCH 9
++#define WLAN_HPPAARCH 10
++/* WLAN_HOSTIF (generally set on the command line, not detected) */
++#define WLAN_PCMCIA 1
++#define WLAN_ISA 2
++#define WLAN_PCI 3
++#define WLAN_USB 4
++#define WLAN_PLX 5
++
++/* Note: the PLX HOSTIF above refers to some vendors implementations for */
++/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */
++/* isn't a real PCMCIA host interface adapter providing all the */
++/* card&socket services. */
++
++#ifdef __powerpc__
++#ifndef __ppc__
++#define __ppc__
++#endif
++#endif
++
++#if (defined(CONFIG_PPC) || defined(CONFIG_8xx))
++#ifndef __ppc__
++#define __ppc__
++#endif
++#endif
++
++#if defined(__x86_64__)
++ #define WLAN_CPU_FAMILY WLAN_x86_64
++ #define WLAN_SYSARCH WLAN_PCAT
++#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
++ #define WLAN_CPU_FAMILY WLAN_Ix86
++ #define WLAN_CPU_CORE WLAN_I386CORE
++ #define WLAN_CPU_PART WLAN_I386PART
++ #define WLAN_SYSARCH WLAN_PCAT
++#elif defined(__ppc__)
++ #define WLAN_CPU_FAMILY WLAN_PPC
++ #define WLAN_CPU_CORE WLAN_PPCCORE
++ #if defined(CONFIG_MBX)
++ #define WLAN_CPU_PART WLAN_MPC860
++ #define WLAN_SYSARCH WLAN_MBX
++ #elif defined(CONFIG_RPXLITE)
++ #define WLAN_CPU_PART WLAN_MPC823
++ #define WLAN_SYSARCH WLAN_RPX
++ #elif defined(CONFIG_RPXCLASSIC)
++ #define WLAN_CPU_PART WLAN_MPC860
++ #define WLAN_SYSARCH WLAN_RPX
++ #else
++ #define WLAN_CPU_PART WLAN_PPCPART
++ #define WLAN_SYSARCH WLAN_PMAC
++ #endif
++#elif defined(__arm__)
++ #define WLAN_CPU_FAMILY WLAN_ARM
++ #define WLAN_CPU_CORE WLAN_ARMCORE
++ #define WLAN_CPU_PART WLAN_ARM_PART
++ #define WLAN_SYSARCH WLAN_SKIFF
++#elif defined(__alpha__)
++ #define WLAN_CPU_FAMILY WLAN_ALPHA
++ #define WLAN_CPU_CORE WLAN_ALPHACORE
++ #define WLAN_CPU_PART WLAN_ALPHAPART
++ #define WLAN_SYSARCH WLAN_ALPHAARCH
++#elif defined(__mips__)
++ #define WLAN_CPU_FAMILY WLAN_MIPS
++ #define WLAN_CPU_CORE WLAN_MIPSCORE
++ #define WLAN_CPU_PART WLAN_MIPSPART
++ #define WLAN_SYSARCH WLAN_MIPSARCH
++#elif defined(__hppa__)
++ #define WLAN_CPU_FAMILY WLAN_HPPA
++ #define WLAN_CPU_CORE WLAN_HPPACORE
++ #define WLAN_CPU_PART WLAN_HPPAPART
++ #define WLAN_SYSARCH WLAN_HPPAARCH
++#elif defined(__sparc__)
++ #define WLAN_CPU_FAMILY WLAN_SPARC
++ #define WLAN_SYSARCH WLAN_SPARC
++#elif defined(__sh__)
++ #define WLAN_CPU_FAMILY WLAN_SH
++ #define WLAN_SYSARCH WLAN_SHARCH
++ #ifndef __LITTLE_ENDIAN__
++ #define __LITTLE_ENDIAN__
++ #endif
++#else
++ #error "No CPU identified!"
++#endif
++
++/*
++ Some big endian machines implicitly do all I/O in little endian mode.
++
++ In particular:
++ Linux/PPC on PowerMacs (PCI)
++ Arm/Intel Xscale (PCI)
++
++ This may also affect PLX boards and other BE &| PPC platforms;
++ as new ones are discovered, add them below.
++*/
++
++#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC))
++#define REVERSE_ENDIAN
++#endif
++
++/*=============================================================*/
++/*------ Hardware Portability Macros --------------------------*/
++/*=============================================================*/
++#if (WLAN_CPU_FAMILY == WLAN_PPC)
++#define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE))
++#define wlan_inw_le16_to_cpu(a) inw((a))
++#define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v))
++#define wlan_outw_cpu_to_le16(v,a) outw((v),(a))
++#else
++#define wlan_inw(a) inw((a))
++#define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a)))
++#define wlan_outw(v,a) outw((v),(a))
++#define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a))
++#endif
++
++/*=============================================================*/
++/*------ Bit settings -----------------------------------------*/
++/*=============================================================*/
++#define ieee2host16(n) __le16_to_cpu(n)
++#define ieee2host32(n) __le32_to_cpu(n)
++#define host2ieee16(n) __cpu_to_le16(n)
++#define host2ieee32(n) __cpu_to_le32(n)
++
++/* for constants */
++#ifdef __LITTLE_ENDIAN
++ #define IEEE16(a,n) a = n, a##i = n,
++#else
++ #ifdef __BIG_ENDIAN
++ /* shifts would produce gcc warnings. Oh well... */
++ #define IEEE16(a,n) a = n, a##i = ((n&0xff)*256 + ((n&0xff00)/256)),
++ #else
++ #error give me endianness or give me death
++ #endif
++#endif
++
++/*=============================================================*/
++/*------ Compiler Portability Macros --------------------------*/
++/*=============================================================*/
++#define __WLAN_ATTRIB_PACK__ __attribute__ ((packed))
++#define __WLAN_PRAGMA_PACK1__
++#define __WLAN_PRAGMA_PACKDFLT__
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38))
++ typedef struct device netdevice_t;
++#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4))
++ typedef struct net_device netdevice_t;
++#else
++ #undef netdevice_t
++ typedef struct net_device netdevice_t;
++#endif
++
++#ifdef WIRELESS_EXT
++#if (WIRELESS_EXT < 13)
++struct iw_request_info {
++ __u16 cmd; /* Wireless Extension command */
++ __u16 flags; /* More to come ;-) */
++};
++#endif
++#endif
++
++/* Interrupt handler backwards compatibility stuff */
++#ifndef IRQ_NONE
++#define IRQ_NONE
++#define IRQ_HANDLED
++typedef void irqreturn_t;
++#endif
++
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) /* more or less */
++#define WLAN_MOD_INC_USE_COUNT MOD_INC_USE_COUNT
++#define WLAN_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
++#else
++#define WLAN_MOD_INC_USE_COUNT
++#define WLAN_MOD_DEC_USE_COUNT
++#endif
++
++#ifndef ARPHRD_IEEE80211_PRISM
++#define ARPHRD_IEEE80211_PRISM 802
++#endif
++
++#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
++
++/*============================================================================*
++ * Constants *
++ *============================================================================*/
++#define WLAN_IEEE_OUI_LEN 3
++/* unused
++#define WLAN_ETHHDR_LEN 14
++#define WLAN_ETHCONV_ENCAP 1
++#define WLAN_ETHCONV_RFC1042 2
++#define WLAN_ETHCONV_8021h 3
++#define WLAN_MIN_ETHFRM_LEN 60
++#define WLAN_MAX_ETHFRM_LEN 1514
++*/
++
++/*============================================================================*
++ * Types *
++ *============================================================================*/
++
++/* local ether header type */
++typedef struct wlan_ethhdr {
++ u8 daddr[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 saddr[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 type __WLAN_ATTRIB_PACK__;
++} wlan_ethhdr_t;
++
++/* local llc header type */
++typedef struct wlan_llc {
++ u8 dsap __WLAN_ATTRIB_PACK__;
++ u8 ssap __WLAN_ATTRIB_PACK__;
++ u8 ctl __WLAN_ATTRIB_PACK__;
++} wlan_llc_t;
++
++/* local snap header type */
++typedef struct wlan_snap {
++ u8 oui[WLAN_IEEE_OUI_LEN] __WLAN_ATTRIB_PACK__;
++ u16 type __WLAN_ATTRIB_PACK__;
++} wlan_snap_t;
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan_hdr.h bt_kernel/drivers/net/wireless/tiacx/wlan_hdr.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan_hdr.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/wlan_hdr.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,497 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++/* mini-doc
++
++Here are all 11b/11g/11a rates and modulations:
++
++ 11b 11g 11a
++ --- --- ---
++ 1 |B |B |
++ 2 |Q |Q |
++ 5.5|Cp |C p|
++ 6 | |Od |O
++ 9 | |od |o
++11 |Cp |C p|
++12 | |Od |O
++18 | |od |o
++22 | | p|
++24 | |Od |O
++33 | | p|
++36 | |od |o
++48 | |od |o
++54 | |od |o
++
++Mandatory:
++ B - DBPSK (Differential Binary Phase Shift Keying)
++ Q - DQPSK (Differential Quaternary Phase Shift Keying)
++ C - CCK (Complementary Code Keying, a form of DSSS
++ (Direct Sequence Spread Spectrum) modulation)
++ O - OFDM (Orthogonal Frequency Division Multiplexing)
++Optional:
++ o - OFDM
++ d - CCK-OFDM (also known as DSSS-OFDM)
++ p - PBCC (Packet Binary Convolutional Coding)
++
++The term CCK-OFDM may be used interchangeably with DSSS-OFDM
++(the IEEE 802.11g-2003 standard uses the latter terminology).
++In the CCK-OFDM, the PLCP header of the frame uses the CCK form of DSSS,
++while the PLCP payload (the MAC frame) is modulated using OFDM.
++
++Basically, you must use CCK-OFDM if you have mixed 11b/11g environment,
++or else (pure OFDM) 11b equipment may not realize that AP
++is sending a packet and start sending its own one.
++Sadly, looks like acx111 does not support CCK-OFDM, only pure OFDM.
++
++Re PBCC: avoid using it. It makes sense only if you have
++TI "11b+" hardware. You _must_ use PBCC in order to reach 22Mbps on it.
++
++Preambles:
++
++Long preamble (at 1Mbit rate, takes 144 us):
++ 16 bytes ones
++ 2 bytes 0xF3A0 (lsb sent first)
++PLCP header follows (at 1Mbit also):
++ 1 byte Signal: speed, in 0.1Mbit units, except for:
++ 33Mbit: 33 (instead of 330 - doesn't fit in octet)
++ all CCK-OFDM rates: 30
++ 1 byte Service
++ 0,1,4: reserved
++ 2: 1=locked clock
++ 3: 1=PBCC
++ 5: Length Extension (PBCC 22,33Mbit (11g only)) <-
++ 6: Length Extension (PBCC 22,33Mbit (11g only)) <- BLACK MAGIC HERE
++ 7: Length Extension <-
++ 2 bytes Length (time needed to tx this frame)
++ a) 5.5 Mbit/s CCK
++ Length = octets*8/5.5, rounded up to integer
++ b) 11 Mbit/s CCK
++ Length = octets*8/11, rounded up to integer
++ Service bit 7:
++ 0 = rounding took less than 8/11
++ 1 = rounding took more than or equal to 8/11
++ c) 5.5 Mbit/s PBCC
++ Length = (octets+1)*8/5.5, rounded up to integer
++ d) 11 Mbit/s PBCC
++ Length = (octets+1)*8/11, rounded up to integer
++ Service bit 7:
++ 0 = rounding took less than 8/11
++ 1 = rounding took more than or equal to 8/11
++ e) 22 Mbit/s PBCC
++ Length = (octets+1)*8/22, rounded up to integer
++ Service bits 6,7:
++ 00 = rounding took less than 8/22ths
++ 01 = rounding took 8/22...15/22ths
++ 10 = rounding took 16/22ths or more.
++ f) 33 Mbit/s PBCC
++ Length = (octets+1)*8/33, rounded up to integer
++ Service bits 5,6,7:
++ 000 rounding took less than 8/33
++ 001 rounding took 8/33...15/33
++ 010 rounding took 16/33...23/33
++ 011 rounding took 24/33...31/33
++ 100 rounding took 32/33 or more
++ 2 bytes CRC
++
++PSDU follows (up to 2346 bytes at selected rate)
++
++While Signal value alone is not enough to determine rate and modulation,
++Signal+Service is always sufficient.
++
++Short preamble (at 1Mbit rate, takes 72 us):
++ 7 bytes zeroes
++ 2 bytes 0x05CF (lsb sent first)
++PLCP header follows *at 2Mbit/s*. Format is the same as in long preamble.
++PSDU follows (up to 2346 bytes at selected rate)
++
++OFDM preamble is completely different, uses OFDM
++modulation from the start and thus easily identifiable.
++Not shown here.
++*/
++
++
++/***********************************************************************
++** Constants
++*/
++
++#define WLAN_HDR_A3_LEN 24
++#define WLAN_HDR_A4_LEN 30
++/* IV structure:
++** 3 bytes: Initialization Vector (24 bits)
++** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3)
++*/
++#define WLAN_WEP_IV_LEN 4
++/* 802.11 says 2312 but looks like 2312 is a max size of _WEPed data_ */
++#define WLAN_DATA_MAXLEN 2304
++#define WLAN_WEP_ICV_LEN 4
++#define WLAN_FCS_LEN 4
++#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN)
++#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN)
++#define WLAN_A3FR_MAXLEN_FCS (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + 4)
++#define WLAN_A4FR_MAXLEN_FCS (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + 4)
++#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8)
++#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8)
++#define WLAN_A3FR_MAXLEN_WEP_FCS (WLAN_A3FR_MAXLEN_FCS + 8)
++#define WLAN_A4FR_MAXLEN_WEP_FCS (WLAN_A4FR_MAXLEN_FCS + 8)
++
++#define WLAN_BSS_TS_LEN 8
++#define WLAN_SSID_MAXLEN 32
++#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334)
++#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0)
++#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
++#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48)
++#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
++#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54)
++#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
++#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44)
++#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78)
++#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261)
++#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
++#define WLAN_CHALLENGE_IE_LEN 130
++#define WLAN_CHALLENGE_LEN 128
++#define WLAN_WEP_MAXKEYLEN 13
++#define WLAN_WEP_NKEYS 4
++
++/*--- Frame Control Field -------------------------------------*/
++/* Frame Types */
++#define WLAN_FTYPE_MGMT 0x00
++#define WLAN_FTYPE_CTL 0x01
++#define WLAN_FTYPE_DATA 0x02
++
++/* Frame subtypes */
++/* Management */
++#define WLAN_FSTYPE_ASSOCREQ 0x00
++#define WLAN_FSTYPE_ASSOCRESP 0x01
++#define WLAN_FSTYPE_REASSOCREQ 0x02
++#define WLAN_FSTYPE_REASSOCRESP 0x03
++#define WLAN_FSTYPE_PROBEREQ 0x04
++#define WLAN_FSTYPE_PROBERESP 0x05
++#define WLAN_FSTYPE_BEACON 0x08
++#define WLAN_FSTYPE_ATIM 0x09
++#define WLAN_FSTYPE_DISASSOC 0x0a
++#define WLAN_FSTYPE_AUTHEN 0x0b
++#define WLAN_FSTYPE_DEAUTHEN 0x0c
++
++/* Control */
++#define WLAN_FSTYPE_PSPOLL 0x0a
++#define WLAN_FSTYPE_RTS 0x0b
++#define WLAN_FSTYPE_CTS 0x0c
++#define WLAN_FSTYPE_ACK 0x0d
++#define WLAN_FSTYPE_CFEND 0x0e
++#define WLAN_FSTYPE_CFENDCFACK 0x0f
++
++/* Data */
++#define WLAN_FSTYPE_DATAONLY 0x00
++#define WLAN_FSTYPE_DATA_CFACK 0x01
++#define WLAN_FSTYPE_DATA_CFPOLL 0x02
++#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03
++#define WLAN_FSTYPE_NULL 0x04
++#define WLAN_FSTYPE_CFACK 0x05
++#define WLAN_FSTYPE_CFPOLL 0x06
++#define WLAN_FSTYPE_CFACK_CFPOLL 0x07
++
++/*--- FC Constants v. 2.0 ------------------------------------*/
++/* Each constant is defined twice: WF_CONST is in host */
++/* byteorder, WF_CONSTi is in ieee byteorder. */
++/* Usage: */
++/* printf("the frame subtype is %X", WF_FC_FTYPEi & rx.fc); */
++/* tx.fc = WF_FTYPE_CTLi | WF_FSTYPE_RTSi; */
++/*------------------------------------------------------------*/
++
++enum {
++/*--- Frame Control Field -------------------------------------*/
++/* Protocol version: always 0 for current 802.11 standards */
++IEEE16(WF_FC_PVER, 0x0003)
++IEEE16(WF_FC_FTYPE, 0x000c)
++IEEE16(WF_FC_FSTYPE, 0x00f0)
++IEEE16(WF_FC_TODS, 0x0100)
++IEEE16(WF_FC_FROMDS, 0x0200)
++IEEE16(WF_FC_FROMTODS, 0x0300)
++IEEE16(WF_FC_MOREFRAG, 0x0400)
++IEEE16(WF_FC_RETRY, 0x0800)
++/* Indicates PS mode in which STA will be after successful completion
++** of current frame exchange sequence. Always 0 for AP frames */
++IEEE16(WF_FC_PWRMGT, 0x1000)
++/* What MoreData=1 means:
++** From AP to STA in PS mode: don't sleep yet, I have more frames for you
++** From Contention-Free (CF) Pollable STA in response to a CF-Poll:
++** STA has buffered frames for transmission in response to next CF-Poll
++** Bcast/mcast frames transmitted from AP:
++** when additional bcast/mcast frames remain to be transmitted by AP
++** during this beacon interval
++** In all other cases MoreData=0 */
++IEEE16(WF_FC_MOREDATA, 0x2000)
++IEEE16(WF_FC_ISWEP, 0x4000)
++IEEE16(WF_FC_ORDER, 0x8000)
++
++/* Frame Types */
++IEEE16(WF_FTYPE_MGMT, 0x00)
++IEEE16(WF_FTYPE_CTL, 0x04)
++IEEE16(WF_FTYPE_DATA, 0x08)
++
++/* Frame subtypes */
++/* Management */
++IEEE16(WF_FSTYPE_ASSOCREQ, 0x00)
++IEEE16(WF_FSTYPE_ASSOCRESP, 0x10)
++IEEE16(WF_FSTYPE_REASSOCREQ, 0x20)
++IEEE16(WF_FSTYPE_REASSOCRESP, 0x30)
++IEEE16(WF_FSTYPE_PROBEREQ, 0x40)
++IEEE16(WF_FSTYPE_PROBERESP, 0x50)
++IEEE16(WF_FSTYPE_BEACON, 0x80)
++IEEE16(WF_FSTYPE_ATIM, 0x90)
++IEEE16(WF_FSTYPE_DISASSOC, 0xa0)
++IEEE16(WF_FSTYPE_AUTHEN, 0xb0)
++IEEE16(WF_FSTYPE_DEAUTHEN, 0xc0)
++
++/* Control */
++IEEE16(WF_FSTYPE_PSPOLL, 0xa0)
++IEEE16(WF_FSTYPE_RTS, 0xb0)
++IEEE16(WF_FSTYPE_CTS, 0xc0)
++IEEE16(WF_FSTYPE_ACK, 0xd0)
++IEEE16(WF_FSTYPE_CFEND, 0xe0)
++IEEE16(WF_FSTYPE_CFENDCFACK, 0xf0)
++
++/* Data */
++IEEE16(WF_FSTYPE_DATAONLY, 0x00)
++IEEE16(WF_FSTYPE_DATA_CFACK, 0x10)
++IEEE16(WF_FSTYPE_DATA_CFPOLL, 0x20)
++IEEE16(WF_FSTYPE_DATA_CFACK_CFPOLL, 0x30)
++IEEE16(WF_FSTYPE_NULL, 0x40)
++IEEE16(WF_FSTYPE_CFACK, 0x50)
++IEEE16(WF_FSTYPE_CFPOLL, 0x60)
++IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70)
++};
++
++
++/***********************************************************************
++** Macros
++*/
++
++/*--- Duration Macros ----------------------------------------*/
++/* Macros to get/set the bitfields of the Duration Field */
++/* - the duration value is only valid when bit15 is zero */
++/* - the firmware handles these values, so I'm not going */
++/* these macros right now. */
++/*------------------------------------------------------------*/
++
++/*--- Sequence Control Macros -------------------------------*/
++/* Macros to get/set the bitfields of the Sequence Control */
++/* Field. */
++/*------------------------------------------------------------*/
++#define WLAN_GET_SEQ_FRGNUM(n) ((u16)(n) & 0x000f)
++#define WLAN_GET_SEQ_SEQNUM(n) (((u16)(n) & 0xfff0) >> 4)
++
++/*--- Data ptr macro -----------------------------------------*/
++/* Creates a u8* to the data portion of a frame */
++/* Assumes you're passing in a ptr to the beginning of the hdr*/
++/*------------------------------------------------------------*/
++#define WLAN_HDR_A3_DATAP(p) (((u8*)(p)) + WLAN_HDR_A3_LEN)
++#define WLAN_HDR_A4_DATAP(p) (((u8*)(p)) + WLAN_HDR_A4_LEN)
++
++
++/***********************************************************************
++** Types
++*/
++
++/* 802.11 header type
++**
++** Note the following:
++** a1 *always* is receiver's mac or bcast/mcast
++** a2 *always* is transmitter's mac, if a2 exists
++** seq: [0:3] frag#, [4:15] seq# - used for dup detection
++** (dups from retries have same seq#) */
++typedef struct wlan_hdr {
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 a1[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 a2[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 a3[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++ u8 a4[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} wlan_hdr_t;
++
++/* Separate structs for use if frame type is known */
++typedef struct wlan_hdr_a3 {
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 a1[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 a2[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 a3[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++} wlan_hdr_a3_t;
++
++typedef struct wlan_hdr_mgmt {
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++} wlan_hdr_mgmt_t;
++
++#ifdef NOT_NEEDED_YET
++typedef struct { /* ad-hoc peer->peer (to/from DS = 0/0) */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++} ibss;
++typedef struct { /* ap->sta (to/from DS = 0/1) */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++} fromap;
++typedef struct { /* sta->ap (to/from DS = 1/0) */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++} toap;
++typedef struct { /* wds->wds (to/from DS = 1/1), the only 4addr pkt */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 ta[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++ u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} wds;
++typedef struct { /* all management packets */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u16 seq __WLAN_ATTRIB_PACK__;
++} mgmt;
++typedef struct { /* has no body, just a FCS */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 ta[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} rts;
++typedef struct { /* has no body, just a FCS */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} cts;
++typedef struct { /* has no body, just a FCS */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} ack;
++typedef struct { /* has no body, just a FCS */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ /* NB: this one holds Assoc ID in dur field: */
++ u16 aid __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 ta[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} pspoll;
++typedef struct { /* has no body, just a FCS */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} cfend;
++typedef struct { /* has no body, just a FCS */
++ u16 fc __WLAN_ATTRIB_PACK__;
++ u16 dur __WLAN_ATTRIB_PACK__;
++ u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++} cfendcfack;
++#endif
++
++/* Prism header emulation (monitor mode) */
++typedef struct wlanitem_u32 {
++ u32 did __WLAN_ATTRIB_PACK__;
++ u16 status __WLAN_ATTRIB_PACK__;
++ u16 len __WLAN_ATTRIB_PACK__;
++ u32 data __WLAN_ATTRIB_PACK__;
++} wlanitem_u32_t;
++#define WLANITEM_STATUS_data_ok 0
++#define WLANITEM_STATUS_no_value 1
++#define WLANITEM_STATUS_invalid_itemname 2
++#define WLANITEM_STATUS_invalid_itemdata 3
++#define WLANITEM_STATUS_missing_itemdata 4
++#define WLANITEM_STATUS_incomplete_itemdata 5
++#define WLANITEM_STATUS_invalid_msg_did 6
++#define WLANITEM_STATUS_invalid_mib_did 7
++#define WLANITEM_STATUS_missing_conv_func 8
++#define WLANITEM_STATUS_string_too_long 9
++#define WLANITEM_STATUS_data_out_of_range 10
++#define WLANITEM_STATUS_string_too_short 11
++#define WLANITEM_STATUS_missing_valid_func 12
++#define WLANITEM_STATUS_unknown 13
++#define WLANITEM_STATUS_invalid_did 14
++#define WLANITEM_STATUS_missing_print_func 15
++
++#define WLAN_DEVNAMELEN_MAX 16
++typedef struct wlansniffrm {
++ u32 msgcode __WLAN_ATTRIB_PACK__;
++ u32 msglen __WLAN_ATTRIB_PACK__;
++ u8 devname[WLAN_DEVNAMELEN_MAX] __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t hosttime __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t mactime __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t channel __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t rssi __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t sq __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t signal __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t noise __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t rate __WLAN_ATTRIB_PACK__;
++ wlanitem_u32_t istx __WLAN_ATTRIB_PACK__; /* tx? 0:no 1:yes */
++ wlanitem_u32_t frmlen __WLAN_ATTRIB_PACK__;
++} wlansniffrm_t;
++#define WLANSNIFFFRM 0x0041
++#define WLANSNIFFFRM_hosttime 0x1041
++#define WLANSNIFFFRM_mactime 0x2041
++#define WLANSNIFFFRM_channel 0x3041
++#define WLANSNIFFFRM_rssi 0x4041
++#define WLANSNIFFFRM_sq 0x5041
++#define WLANSNIFFFRM_signal 0x6041
++#define WLANSNIFFFRM_noise 0x7041
++#define WLANSNIFFFRM_rate 0x8041
++#define WLANSNIFFFRM_istx 0x9041
++#define WLANSNIFFFRM_frmlen 0xA041
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan_mgmt.h bt_kernel/drivers/net/wireless/tiacx/wlan_mgmt.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/net/wireless/tiacx/wlan_mgmt.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/net/wireless/tiacx/wlan_mgmt.h 2005-09-28 23:54:23.938287000 +0300
+@@ -0,0 +1,579 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++/***********************************************************************
++** Constants
++*/
++
++/*-- Information Element IDs --------------------*/
++#define WLAN_EID_SSID 0
++#define WLAN_EID_SUPP_RATES 1
++#define WLAN_EID_FH_PARMS 2
++#define WLAN_EID_DS_PARMS 3
++#define WLAN_EID_CF_PARMS 4
++#define WLAN_EID_TIM 5
++#define WLAN_EID_IBSS_PARMS 6
++#define WLAN_EID_COUNTRY 7 /* 802.11d */
++#define WLAN_EID_FH_HOP_PARMS 8 /* 802.11d */
++#define WLAN_EID_FH_TABLE 9 /* 802.11d */
++#define WLAN_EID_REQUEST 10 /* 802.11d */
++/*-- values 11-15 reserved --*/
++#define WLAN_EID_CHALLENGE 16
++/*-- values 17-31 reserved for challenge text extension --*/
++#define WLAN_EID_ERP_INFO 42
++#define WLAN_EID_NONERP 47 /* was seen from WRT54GS with OpenWrt */
++#define WLAN_EID_RSN 48
++#define WLAN_EID_EXT_RATES 50
++#define WLAN_EID_GENERIC 221
++
++#if 0
++#define WLAN_EID_PWR_CONSTRAINT 32 /* 11H PowerConstraint */
++#define WLAN_EID_PWR_CAP 33 /* 11H PowerCapability */
++#define WLAN_EID_TPC_REQUEST 34 /* 11H TPC Request */
++#define WLAN_EID_TPC_REPORT 35 /* 11H TPC Report */
++#define WLAN_EID_SUPP_CHANNELS 36 /* 11H Supported Channels */
++#define WLAN_EID_CHANNEL_SWITCH 37 /* 11H ChannelSwitch */
++#define WLAN_EID_MEASURE_REQUEST 38 /* 11H MeasurementRequest */
++#define WLAN_EID_MEASURE_REPORT 39 /* 11H MeasurementReport */
++#define WLAN_EID_QUIET_ID 40 /* 11H Quiet */
++#define WLAN_EID_IBSS_DFS_ID 41 /* 11H IBSS_DFS */
++#endif
++
++/*-- Reason Codes -------------------------------*/
++#define WLAN_MGMT_REASON_RSVD 0
++#define WLAN_MGMT_REASON_UNSPEC 1
++#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2
++#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3
++#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4
++#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5
++#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6
++#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7
++#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8
++#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9
++
++/*-- Status Codes -------------------------------*/
++#define WLAN_MGMT_STATUS_SUCCESS 0
++#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1
++#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10
++#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12
++#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13
++#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14
++#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15
++#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18
++/* p80211b additions */
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21
++
++/*-- Auth Algorithm Field ---------------------------*/
++#define WLAN_AUTH_ALG_OPENSYSTEM 0
++#define WLAN_AUTH_ALG_SHAREDKEY 1
++
++/*-- Management Frame Field Offsets -------------*/
++/* Note: Not all fields are listed because of variable lengths */
++/* Note: These offsets are from the start of the frame data */
++
++#define WLAN_BEACON_OFF_TS 0
++#define WLAN_BEACON_OFF_BCN_INT 8
++#define WLAN_BEACON_OFF_CAPINFO 10
++#define WLAN_BEACON_OFF_SSID 12
++
++#define WLAN_DISASSOC_OFF_REASON 0
++
++#define WLAN_ASSOCREQ_OFF_CAP_INFO 0
++#define WLAN_ASSOCREQ_OFF_LISTEN_INT 2
++#define WLAN_ASSOCREQ_OFF_SSID 4
++
++#define WLAN_ASSOCRESP_OFF_CAP_INFO 0
++#define WLAN_ASSOCRESP_OFF_STATUS 2
++#define WLAN_ASSOCRESP_OFF_AID 4
++#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6
++
++#define WLAN_REASSOCREQ_OFF_CAP_INFO 0
++#define WLAN_REASSOCREQ_OFF_LISTEN_INT 2
++#define WLAN_REASSOCREQ_OFF_CURR_AP 4
++#define WLAN_REASSOCREQ_OFF_SSID 10
++
++#define WLAN_REASSOCRESP_OFF_CAP_INFO 0
++#define WLAN_REASSOCRESP_OFF_STATUS 2
++#define WLAN_REASSOCRESP_OFF_AID 4
++#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6
++
++#define WLAN_PROBEREQ_OFF_SSID 0
++
++#define WLAN_PROBERESP_OFF_TS 0
++#define WLAN_PROBERESP_OFF_BCN_INT 8
++#define WLAN_PROBERESP_OFF_CAP_INFO 10
++#define WLAN_PROBERESP_OFF_SSID 12
++
++#define WLAN_AUTHEN_OFF_AUTH_ALG 0
++#define WLAN_AUTHEN_OFF_AUTH_SEQ 2
++#define WLAN_AUTHEN_OFF_STATUS 4
++#define WLAN_AUTHEN_OFF_CHALLENGE 6
++
++#define WLAN_DEAUTHEN_OFF_REASON 0
++
++enum {
++IEEE16(WF_MGMT_CAP_ESS, 0x0001)
++IEEE16(WF_MGMT_CAP_IBSS, 0x0002)
++/* In (re)assoc request frames by STA:
++** Pollable=0, PollReq=0: STA is not CF-Pollable
++** 0 1: STA is CF-Pollable, not requesting to be placed on the CF-Polling list
++** 1 0: STA is CF-Pollable, requesting to be placed on the CF-Polling list
++** 1 1: STA is CF-Pollable, requesting never to be polled
++** In beacon, proberesp, (re)assoc resp frames by AP:
++** 0 0: No point coordinator at AP
++** 0 1: Point coordinator at AP for delivery only (no polling)
++** 1 0: Point coordinator at AP for delivery and polling
++** 1 1: Reserved */
++IEEE16(WF_MGMT_CAP_CFPOLLABLE, 0x0004)
++IEEE16(WF_MGMT_CAP_CFPOLLREQ, 0x0008)
++/* 1=non-WEP data frames are disallowed */
++IEEE16(WF_MGMT_CAP_PRIVACY, 0x0010)
++/* In beacon, proberesp, (re)assocresp by AP/AdHoc:
++** 1=use of shortpre is allowed ("I can receive shortpre") */
++IEEE16(WF_MGMT_CAP_SHORT, 0x0020)
++IEEE16(WF_MGMT_CAP_PBCC, 0x0040)
++IEEE16(WF_MGMT_CAP_AGILITY, 0x0080)
++/* In (re)assoc request frames by STA:
++** 1=short slot time implemented and enabled
++** NB: AP shall use long slot time beginning at the next Beacon after assoc
++** of STA with this bit set to 0
++** In beacon, proberesp, (re)assoc resp frames by AP:
++** currently used slot time value: 0/1 - long/short */
++IEEE16(WF_MGMT_CAP_SHORTSLOT, 0x0400)
++/* In (re)assoc request frames by STA: 1=CCK-OFDM is implemented and enabled
++** In beacon, proberesp, (re)assoc resp frames by AP/AdHoc:
++** 1=CCK-OFDM is allowed */
++IEEE16(WF_MGMT_CAP_CCKOFDM, 0x2000)
++};
++
++
++/***********************************************************************
++** Types
++*/
++
++/* Information Element types */
++
++/* prototype structure, all IEs start with these members */
++typedef struct wlan_ie {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++} wlan_ie_t;
++
++/*-- Service Set Identity (SSID) -----------------*/
++typedef struct wlan_ie_ssid {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u8 ssid[1] __WLAN_ATTRIB_PACK__; /* may be zero */
++} wlan_ie_ssid_t;
++
++/*-- Supported Rates -----------------------------*/
++typedef struct wlan_ie_supp_rates {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u8 rates[1] __WLAN_ATTRIB_PACK__; /* had better be at LEAST one! */
++} wlan_ie_supp_rates_t;
++
++/*-- FH Parameter Set ----------------------------*/
++typedef struct wlan_ie_fh_parms {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u16 dwell __WLAN_ATTRIB_PACK__;
++ u8 hopset __WLAN_ATTRIB_PACK__;
++ u8 hoppattern __WLAN_ATTRIB_PACK__;
++ u8 hopindex __WLAN_ATTRIB_PACK__;
++} wlan_ie_fh_parms_t;
++
++/*-- DS Parameter Set ----------------------------*/
++typedef struct wlan_ie_ds_parms {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u8 curr_ch __WLAN_ATTRIB_PACK__;
++} wlan_ie_ds_parms_t;
++
++/*-- CF Parameter Set ----------------------------*/
++typedef struct wlan_ie_cf_parms {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u8 cfp_cnt __WLAN_ATTRIB_PACK__;
++ u8 cfp_period __WLAN_ATTRIB_PACK__;
++ u16 cfp_maxdur __WLAN_ATTRIB_PACK__;
++ u16 cfp_durremaining __WLAN_ATTRIB_PACK__;
++} wlan_ie_cf_parms_t;
++
++/*-- TIM ------------------------------------------*/
++typedef struct wlan_ie_tim {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u8 dtim_cnt __WLAN_ATTRIB_PACK__;
++ u8 dtim_period __WLAN_ATTRIB_PACK__;
++ u8 bitmap_ctl __WLAN_ATTRIB_PACK__;
++ u8 virt_bm[1] __WLAN_ATTRIB_PACK__;
++} wlan_ie_tim_t;
++
++/*-- IBSS Parameter Set ---------------------------*/
++typedef struct wlan_ie_ibss_parms {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u16 atim_win __WLAN_ATTRIB_PACK__;
++} wlan_ie_ibss_parms_t;
++
++/*-- Challenge Text ------------------------------*/
++typedef struct wlan_ie_challenge {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ u8 challenge[1] __WLAN_ATTRIB_PACK__;
++} wlan_ie_challenge_t;
++
++/*-- ERP (42) -------------------------------------*/
++typedef struct wlan_ie_erp {
++ u8 eid __WLAN_ATTRIB_PACK__;
++ u8 len __WLAN_ATTRIB_PACK__;
++ /* bit 0:Non ERP present
++ ** 1:Use Protection
++ ** 2:Barker Preamble mode
++ ** 3-7:reserved */
++ u8 erp __WLAN_ATTRIB_PACK__;
++} wlan_ie_erp_t;
++
++/* Types for parsing mgmt frames */
++
++/* prototype structure, all mgmt frame types will start with these members */
++typedef struct wlan_fr_mgmt {
++ u16 type;
++ u16 len; /* DOES NOT include FCS */
++ wlan_hdr_t *hdr;
++ /* used for target specific data, skb in Linux */
++ /*-- fixed fields -----------*/
++ /*-- info elements ----------*/
++} wlan_fr_mgmt_t;
++
++/*-- Beacon ---------------------------------------*/
++typedef struct wlan_fr_beacon {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u64 *ts;
++ u16 *bcn_int;
++ u16 *cap_info;
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++ wlan_ie_fh_parms_t *fh_parms;
++ wlan_ie_ds_parms_t *ds_parms;
++ wlan_ie_cf_parms_t *cf_parms;
++ wlan_ie_ibss_parms_t *ibss_parms;
++ wlan_ie_tim_t *tim; /* in beacon only, not proberesp */
++ wlan_ie_erp_t *erp; /* in beacon only, not proberesp */
++} wlan_fr_beacon_t;
++#define wlan_fr_proberesp wlan_fr_beacon
++#define wlan_fr_proberesp_t wlan_fr_beacon_t
++
++/*-- IBSS ATIM ------------------------------------*/
++typedef struct wlan_fr_ibssatim {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ /*-- info elements ----------*/
++ /* this frame type has a null body */
++} wlan_fr_ibssatim_t;
++
++/*-- Disassociation -------------------------------*/
++typedef struct wlan_fr_disassoc {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *reason;
++ /*-- info elements ----------*/
++} wlan_fr_disassoc_t;
++
++/*-- Association Request --------------------------*/
++typedef struct wlan_fr_assocreq {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *listen_int;
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_assocreq_t;
++
++/*-- Association Response -------------------------*/
++typedef struct wlan_fr_assocresp {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *status;
++ u16 *aid;
++ /*-- info elements ----------*/
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_assocresp_t;
++
++/*-- Reassociation Request ------------------------*/
++typedef struct wlan_fr_reassocreq {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *listen_int;
++ u8 *curr_ap;
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_reassocreq_t;
++
++/*-- Reassociation Response -----------------------*/
++typedef struct wlan_fr_reassocresp {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *status;
++ u16 *aid;
++ /*-- info elements ----------*/
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_reassocresp_t;
++
++/*-- Probe Request --------------------------------*/
++typedef struct wlan_fr_probereq {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_probereq_t;
++
++/*-- Authentication -------------------------------*/
++typedef struct wlan_fr_authen {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *auth_alg;
++ u16 *auth_seq;
++ u16 *status;
++ /*-- info elements ----------*/
++ wlan_ie_challenge_t *challenge;
++} wlan_fr_authen_t;
++
++/*-- Deauthenication -----------------------------*/
++typedef struct wlan_fr_deauthen {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *reason;
++ /*-- info elements ----------*/
++} wlan_fr_deauthen_t;
++
++/* Types for building mgmt frames */
++
++/* Warning. Several types used in below structs are
++** in fact variable length. Use structs with such fields with caution */
++typedef struct auth_frame_body {
++ u16 auth_alg __WLAN_ATTRIB_PACK__;
++ u16 auth_seq __WLAN_ATTRIB_PACK__;
++ u16 status __WLAN_ATTRIB_PACK__;
++ wlan_ie_challenge_t challenge __WLAN_ATTRIB_PACK__;
++} auth_frame_body_t;
++
++typedef struct assocresp_frame_body {
++ u16 cap_info __WLAN_ATTRIB_PACK__;
++ u16 status __WLAN_ATTRIB_PACK__;
++ u16 aid __WLAN_ATTRIB_PACK__;
++ wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__;
++} assocresp_frame_body_t;
++
++typedef struct reassocreq_frame_body {
++ u16 cap_info __WLAN_ATTRIB_PACK__;
++ u16 listen_int __WLAN_ATTRIB_PACK__;
++ u8 current_ap[ETH_ALEN] __WLAN_ATTRIB_PACK__;
++ wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__;
++/* access to this one is disabled since ssid_t is variable length: */
++ /* wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; */
++} reassocreq_frame_body_t;
++
++typedef struct reassocresp_frame_body {
++ u16 cap_info __WLAN_ATTRIB_PACK__;
++ u16 status __WLAN_ATTRIB_PACK__;
++ u16 aid __WLAN_ATTRIB_PACK__;
++ wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__;
++} reassocresp_frame_body_t;
++
++typedef struct deauthen_frame_body {
++ u16 reason __WLAN_ATTRIB_PACK__;
++} deauthen_frame_body_t;
++
++typedef struct disassoc_frame_body {
++ u16 reason __WLAN_ATTRIB_PACK__;
++} disassoc_frame_body_t;
++
++typedef struct probereq_frame_body {
++ wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__;
++ wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__;
++} probereq_frame_body_t;
++
++typedef struct proberesp_frame_body {
++ u8 timestamp[8] __WLAN_ATTRIB_PACK__;
++ u16 beacon_int __WLAN_ATTRIB_PACK__;
++ u16 cap_info __WLAN_ATTRIB_PACK__;
++ wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__;
++/* access to these is disabled since ssid_t is variable length: */
++ /* wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; */
++ /* fhps_t fhps __WLAN_ATTRIB_PACK__; */
++ /* dsps_t dsps __WLAN_ATTRIB_PACK__; */
++ /* cfps_t cfps __WLAN_ATTRIB_PACK__; */
++} proberesp_frame_body_t;
++
++
++/***********************************************************************
++** Functions
++*/
++
++/* Helpers for parsing mgmt frames */
++void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t *f);
++void wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t *f);
++void wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t *f);
++void wlan_mgmt_decode_authen(wlan_fr_authen_t *f);
++void wlan_mgmt_decode_beacon(wlan_fr_beacon_t *f);
++void wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t *f);
++void wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t *f);
++void wlan_mgmt_decode_probereq(wlan_fr_probereq_t *f);
++void wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t *f);
++void wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t *f);
++void wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t *f);
++
++/* Helpers for building mgmt frames */
++static inline u8*
++wlan_fill_ie_ssid(u8 *p, int len, const char *ssid)
++{
++ struct wlan_ie_ssid *ie = (void*)p;
++ ie->eid = WLAN_EID_SSID;
++ ie->len = len;
++ memcpy(ie->ssid, ssid, len);
++ return p + len + 2;
++}
++/* This controls whether we create 802.11g 'ext supported rates' IEs
++** or just create overlong 'supported rates' IEs instead
++** (non-11g compliant) */
++#define WE_OBEY_802_11G 1
++static inline u8*
++wlan_fill_ie_rates(u8 *p, int len, const u8 *rates)
++{
++ struct wlan_ie_supp_rates *ie = (void*)p;
++#if WE_OBEY_802_11G
++ if (len > 8 ) len = 8;
++#endif
++ /* supported rates (1 to 8 octets) */
++ ie->eid = WLAN_EID_SUPP_RATES;
++ ie->len = len;
++ memcpy(ie->rates, rates, len);
++ return p + len + 2;
++}
++/* This one wouldn't create an IE at all if not needed */
++static inline u8*
++wlan_fill_ie_rates_ext(u8 *p, int len, const u8 *rates)
++{
++ struct wlan_ie_supp_rates *ie = (void*)p;
++#if !WE_OBEY_802_11G
++ return p;
++#endif
++ len -= 8;
++ if (len < 0) return p;
++ /* ext supported rates */
++ ie->eid = WLAN_EID_EXT_RATES;
++ ie->len = len;
++ memcpy(ie->rates, rates+8, len);
++ return p + len + 2;
++}
++static inline u8*
++wlan_fill_ie_ds_parms(u8 *p, int channel)
++{
++ struct wlan_ie_ds_parms *ie = (void*)p;
++ ie->eid = WLAN_EID_DS_PARMS;
++ ie->len = 1;
++ ie->curr_ch = channel;
++ return p + sizeof(*ie);
++}
++static inline u8*
++wlan_fill_ie_ibss_parms(u8 *p, int atim_win)
++{
++ struct wlan_ie_ibss_parms *ie = (void*)p;
++ ie->eid = WLAN_EID_IBSS_PARMS;
++ ie->len = 2;
++ ie->atim_win = atim_win;
++ return p + sizeof(*ie);
++}
++static inline u8*
++wlan_fill_ie_tim(u8 *p, int rem, int period, int bcast,
++ int ofs, int len, const u8 *vbm)
++{
++ struct wlan_ie_tim *ie = (void*)p;
++ ie->eid = WLAN_EID_TIM;
++ ie->len = len + 3;
++ ie->dtim_cnt = rem;
++ ie->dtim_period = period;
++ ie->bitmap_ctl = ofs | (bcast!=0);
++ if (vbm)
++ memcpy(ie->virt_bm, vbm, len); /* min 1 byte */
++ else
++ ie->virt_bm[0] = 0;
++ return p + len + 3 + 2;
++}
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/pci/.gitignore bt_kernel/drivers/pci/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/pci/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/pci/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,4 +0,0 @@
+-classlist.h
+-devlist.h
+-gen-devlist
+-
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/ssi/omap-tsc2101.c bt_kernel/drivers/ssi/omap-tsc2101.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/ssi/omap-tsc2101.c 2005-10-30 17:44:18.415932786 +0200
++++ bt_kernel/drivers/ssi/omap-tsc2101.c 2005-10-22 03:52:45.687256000 +0300
+@@ -36,10 +36,11 @@
+ #include <asm/arch/hardware.h>
+ #include <asm/hardware/tsc2101.h>
+ #include <asm/arch/gpioexpander.h>
++#include <asm/arch/gpio.h>
+
+ #include "omap-tsc2101.h"
+
+-#if CONFIG_ARCH_OMAP16XX
++#if CONFIG_ARCH_OMAP1
+ #include <../drivers/ssi/omap-uwire.h>
+ #else
+ #error "Unsupported configuration"
+@@ -66,27 +67,28 @@
+ if (count++ == 0) {
+ int ret = 0;
+ /* set the Mux to provide MCLK to TSC2101 */
+- if (machine_is_omap_h3()) {
++ if (machine_is_omap_h3())
+ ret = omap_cfg_reg(V5_1710_MCLK_ON);
+- } else {
+- if (machine_is_omap_h2()) {
+- ret = omap_cfg_reg(R10_1610_MCLK_ON);
++ else if (machine_is_omap_h2())
++ ret = omap_cfg_reg(R10_1610_MCLK_ON);
++ else if (machine_is_h6300 ())
++ ret = omap_cfg_reg(R10_1510_MCLK_ON);
++
++ if (!cpu_is_omap1510 ()) {
++ /* Get the MCLK */
++ tsc2101_mclk_ck = clk_get(NULL, "mclk");
++ if (NULL == tsc2101_mclk_ck) {
++ printk(KERN_ERR "Unable to get the clock MCLK!!!\n");;
++ ret = -EPERM;
++ goto done;
+ }
+- }
+-
+- /* Get the MCLK */
+- tsc2101_mclk_ck = clk_get(NULL, "mclk");
+- if (NULL == tsc2101_mclk_ck) {
+- printk(KERN_ERR "Unable to get the clock MCLK!!!\n");;
+- ret = -EPERM;
+- goto done;
+- }
+- if (clk_set_rate(tsc2101_mclk_ck, 12000000)) {
+- printk(KERN_ERR "Unable to set rate to the MCLK!!!\n");;
+- ret = -EPERM;
+- goto done;
+- }
+- clk_enable(tsc2101_mclk_ck);
++ if (clk_set_rate(tsc2101_mclk_ck, 12000000)) {
++ printk(KERN_ERR "Unable to set rate to the MCLK!!!\n");;
++ ret = -EPERM;
++ goto done;
++ }
++ clk_enable(tsc2101_mclk_ck);
++ } /* if (!cpu_is_omap1510 ()) */
+
+ ret = omap_tsc2101_configure();
+
+@@ -116,10 +118,16 @@
+ }
+ }
+
+- /* Release the MCLK */
+- clk_disable(tsc2101_mclk_ck);
+- clk_put(tsc2101_mclk_ck);
+- tsc2101_mclk_ck = NULL;
++ if (!cpu_is_omap1510 ()) {
++ /* Release the MCLK */
++ clk_disable(tsc2101_mclk_ck);
++ clk_put(tsc2101_mclk_ck);
++ tsc2101_mclk_ck = NULL;
++ }
++
++#if defined(CONFIG_MACH_OMAP_H6300)
++ omap_free_gpio(8);
++#endif
+
+ module_put(THIS_MODULE);
+ }
+@@ -150,7 +158,10 @@
+ return;
+ }
+ }
+- if (machine_is_omap_h3()) {
++ if (machine_is_omap_h3() || machine_is_h6300 ()) {
++
++ if (machine_is_h6300 ())
++ omap_set_gpio_dataout (8, 0);
+
+ ret =
+ omap_uwire_data_transfer(0, ((page << 11) | (address << 5)),
+@@ -159,6 +170,8 @@
+ printk(KERN_ERR
+ "uwire-write returned error for address %x\n",
+ address);
++ if (machine_is_h6300 ())
++ omap_set_gpio_dataout (8, 1);
+ return;
+ }
+ ret = omap_uwire_data_transfer(0, data, 16, 0, NULL, 0);
+@@ -166,10 +179,14 @@
+ printk(KERN_ERR
+ "uwire-write returned error for address %x\n",
+ address);
++ if (machine_is_h6300 ())
++ omap_set_gpio_dataout (8, 1);
+ return;
+ }
+- }
+
++ if (machine_is_h6300 ())
++ omap_set_gpio_dataout (8, 1);
++ }
+ }
+
+ void omap_tsc2101_reads(int page, u8 startaddress, u16 * data, int numregs)
+@@ -178,9 +195,13 @@
+ if (machine_is_omap_h2()) {
+ cs = 1;
+ }
+- if (machine_is_omap_h3()) {
++ if (machine_is_omap_h3() || machine_is_h6300 ()) {
+ cs = 0;
+ }
++
++ if (machine_is_h6300 ())
++ omap_set_gpio_dataout(8, 0);
++
+ (void)omap_uwire_data_transfer(cs, (0x8000 | (page << 11)
+ | (startaddress << 5)),
+ 16, 0, NULL, 1);
+@@ -188,6 +209,9 @@
+ omap_uwire_data_transfer(cs, 0, 0, 16, data, 1);
+ }
+ omap_uwire_data_transfer(cs, 0, 0, 16, data, 0);
++
++ if (machine_is_h6300 ())
++ omap_set_gpio_dataout(8, 1);
+ }
+
+ u16 omap_tsc2101_read(int page, u8 address)
+@@ -228,9 +252,24 @@
+ omap_cfg_reg(N14_1610_UWIRE_CS0);
+ omap_uwire_configure_mode(0, uwire_flags);
+ }
++ if (machine_is_h6300()) {
++ uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE;
++ omap_cfg_reg(N14_1510_UWIRE_CS0);
++ omap_uwire_configure_mode(0, uwire_flags);
++
++ omap_request_gpio(8);
++ omap_set_gpio_dataout(8, 0);
++ omap_set_gpio_direction (8, 0);
++ }
+
+ /* Configure MCLK enable */
+- omap_writel(omap_readl(PU_PD_SEL_2) | (1 << 22), PU_PD_SEL_2);
++ if (cpu_is_omap16xx() || cpu_is_omap1710())
++ omap_writel(omap_readl(PU_PD_SEL_2) | (1 << 22), PU_PD_SEL_2);
++ if (machine_is_h6300()) {
++ omap_cfg_reg(V19_1510_UWIRE_SCLK);
++ omap_cfg_reg(W21_1510_UWIRE_SDO);
++ omap_cfg_reg(U18_1510_UWIRE_SDI);
++ }
+
+ return 0;
+ }
+@@ -243,5 +282,5 @@
+
+ MODULE_AUTHOR("Texas Instruments");
+ MODULE_DESCRIPTION
+- ("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec.");
++ ("Glue audio driver for the TI OMAP1510/1610/OMAP1710 TSC2101 codec.");
+ MODULE_LICENSE("GPL");
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/ssi/omap-uwire.c bt_kernel/drivers/ssi/omap-uwire.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/ssi/omap-uwire.c 2005-10-30 17:44:18.435929603 +0200
++++ bt_kernel/drivers/ssi/omap-uwire.c 2005-08-12 13:46:22.016114000 +0300
+@@ -212,6 +212,10 @@
+ omap_cfg_reg(N14_1610_UWIRE_CS0);
+ omap_cfg_reg(P15_1610_UWIRE_CS3);
+ }
++ if (machine_is_h6300 ()) {
++ omap_cfg_reg(N14_1510_UWIRE_CS0);
++ omap_cfg_reg(P15_1510_UWIRE_CS3);
++ }
+ if (machine_is_omap_perseus2()) {
+ /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */
+ int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000;
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/Kconfig bt_kernel/drivers/telephony/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/Kconfig 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/telephony/Kconfig 2005-10-06 02:34:39.057478000 +0300
+@@ -41,7 +41,18 @@
+ help
+ Say Y here to configure in PCMCIA service support for the Quicknet
+ cards manufactured by Quicknet Technologies, Inc. This changes the
+- card initialization code to work with the card manager daemon.
++ card initialization code to work with the card manager daemon.
++
++config GSM_H6300
++ tristate "H6300 P5186 GSM/GPRS DRIVER"
++ depends on PHONE && I2C && PCA9535
++ help
++ Bluetooth H6300 P5186 gsm/gprs driver.
++ This driver provides the firmware loading mechanism for the P5185
++ gsm/gprs hardware in iPAQ h6300.
++
++ Say Y here to compile support for P5186 gsm/gprs devices into the
++ kernel or say M to compile it as module (h6300_gsm).
+
+ endmenu
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/Makefile bt_kernel/drivers/telephony/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/Makefile 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/telephony/Makefile 2005-10-06 02:34:39.057478000 +0300
+@@ -2,6 +2,7 @@
+ # Makefile for drivers/telephony
+ #
+
+-obj-$(CONFIG_PHONE) += phonedev.o
+-obj-$(CONFIG_PHONE_IXJ) += ixj.o
+-obj-$(CONFIG_PHONE_IXJ_PCMCIA) += ixj_pcmcia.o
++obj-$(CONFIG_PHONE) += phonedev.o
++obj-$(CONFIG_PHONE_IXJ) += ixj.o
++obj-$(CONFIG_PHONE_IXJ_PCMCIA) += ixj_pcmcia.o
++obj-$(CONFIG_GSM_H6300) += omap/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/h6300_gsm_led.c bt_kernel/drivers/telephony/omap/h6300_gsm_led.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/h6300_gsm_led.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/telephony/omap/h6300_gsm_led.c 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,40 @@
++/*
++ * GSM interface driver helper for controlling bluetooth leds available in iPAQ h6300.
++ *
++ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi>
++ *
++ * 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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gpio.h>
++
++/*
++ * Low level access for disabling h6300 gsm led.
++ *
++ * TODO: implement for h6300
++ */
++void h6300_clear_gsm_led(int led_num)
++{
++ printk(KERN_NOTICE "h6300_gsm_led.c h6300_clear_gsm_led() done\n");
++ //hx4700_set_led(led_num, 0, 16);
++}
++EXPORT_SYMBOL(h6300_clear_gsm_led);
++
++/*
++ * Low level access for setting up the gsm led.
++ *
++ * TODO: implement for h6300
++ */
++void h6300_set_gsm_led(int led_num, int duty_time, int cycle_time)
++{
++ printk(KERN_NOTICE "h6300_gsm_led.c h6300_set_gsm_led() done\n");
++}
++EXPORT_SYMBOL(h6300_set_gsm_led);
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/h6300_gsm_led.h bt_kernel/drivers/telephony/omap/h6300_gsm_led.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/h6300_gsm_led.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/telephony/omap/h6300_gsm_led.h 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,10 @@
++#ifndef H6300_GSM_LED_H_
++#define H6300_GSM_LED_H_
++
++#define INDEX_GSM_LED 1
++
++void h6300_clear_gsm_led(int led_num);
++void h6300_set_gsm_led(int led_num, int duty_time, int cycle_time);
++
++
++#endif /*H6300_GSM_LED_H_*/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/h6300_gsm_p5186.c bt_kernel/drivers/telephony/omap/h6300_gsm_p5186.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/h6300_gsm_p5186.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/telephony/omap/h6300_gsm_p5186.c 2005-10-20 20:57:07.074687000 +0300
+@@ -0,0 +1,171 @@
++/*
++ * Wavecom P5186 GPRS and GSM module driver for iPAQ h6300.
++ *
++ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi>
++ *
++ * 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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gpio.h>
++
++#include <asm/arch/pca9535.h>
++#include <asm/arch/h6300_uart_info.h>
++#include "h6300_gsm_led.h"
++
++static void
++h6300_gsm_configure(struct uart_omap_port *up, int enable)
++{
++ printk(KERN_NOTICE "h6300_gsm_p5186.c h6300_gsm_configure() started, enable = %d\n", enable);
++
++ // printk( KERN_NOTICE "h6300 configure bluetooth: %d\n", enable );
++ if (enable == 0) {
++ pca9535_gpio_write(GPIO_I2C_GPRS_RESET, GPIO_VALUE_OFF); // turn off gpio
++ mdelay(5);
++ h6300_clear_gsm_led(INDEX_GSM_LED);
++ }
++ else if (enable == 1) {
++ pca9535_gpio_write(GPIO_I2C_GPRS_RESET, GPIO_VALUE_ON); // turn on gpio
++ mdelay(5);
++ }
++ else if (enable == 2) {
++ h6300_set_gsm_led(INDEX_GSM_LED, 16, 16);
++ }
++ printk(KERN_NOTICE "h6300_gsm_p5186.c h6300_gsm_configure() done\n");
++}
++
++static void
++h6300_gsm_set_txrx(struct uart_omap_port *up, int txrx)
++{
++ printk(KERN_NOTICE "h6300_gsm_p5186.c h6300_gsm_set_txrx(), txrx = %d done\n", txrx);
++ /* do nothing */
++}
++
++static int
++h6300_gsm_get_txrx(struct uart_omap_port *up)
++{
++ printk(KERN_NOTICE "h6300_gsm_p5186.c h6300_gsm_get_txrx() done\n");
++ /* do nothing */
++ return 0;
++}
++
++static int
++h6300_gsm_probe(struct device *dev)
++{
++ int ii;
++ int curVal;
++
++ struct h6300_uart_funcs *funcs = (struct h6300_uart_funcs *)dev->platform_data;
++/*
++ printk(KERN_NOTICE "h6300_gsm_p5186.c h6300_gsm_probe() started\n");
++ for (ii = 0; ii < 8; ii++)
++ {
++ curVal = pca9535_gpio_read(ii);
++ printk(KERN_NOTICE "I2C[%d] = %d ", ii, curVal);
++ }
++ for (ii = 10; ii < 18; ii++)
++ {
++ curVal = pca9535_gpio_read(ii);
++ printk(KERN_NOTICE "I2C[%d] = %d ", ii, curVal);
++ }
++ printk(KERN_NOTICE "\nfirst check done\n");
++*/
++ pca9535_gpio_direction(GPIO_I2C_GPRS_RESET, GPIO_DIR_OUTPUT); // set gpio direction to be output
++ pca9535_gpio_write(GPIO_I2C_GPRS_RESET, GPIO_VALUE_ON); // turn on gpio
++ mdelay(200);
++
++ pca9535_gpio_direction(GPIO_I2C_MIC_OP_EN, GPIO_DIR_OUTPUT); // set gpio direction to be output
++ pca9535_gpio_write(GPIO_I2C_MIC_OP_EN, GPIO_VALUE_ON); // turn on gpio
++ mdelay(200);
++
++ pca9535_gpio_direction(GPIO_I2C_SPK_OP_PD, GPIO_DIR_OUTPUT); // set gpio direction to be output
++ pca9535_gpio_write(GPIO_I2C_SPK_OP_PD, GPIO_VALUE_ON); // pd = pulldown?, normal off = on
++
++ mdelay(200);
++
++ //pca9535_gpio_direction(
++ /* configure bluetooth UART */
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_RXD_MD);
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_TXD_MD);
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_UART_CTS_MD);
++ //h6300_gpio_mode(GPIO_NR_H6300_BT_UART_RTS_MD);
++
++ funcs->configure = h6300_gsm_configure;
++ funcs->set_txrx = h6300_gsm_set_txrx;
++ funcs->get_txrx = h6300_gsm_get_txrx;
++
++ /* Make sure the LED is off */
++ h6300_clear_gsm_led(INDEX_GSM_LED);
++/*
++ for (ii = 0; ii < 8; ii++)
++ {
++ curVal = pca9535_gpio_read(ii);
++ printk(KERN_NOTICE "I2C[%d] = %d ", ii, curVal);
++ }
++ for (ii = 10; ii < 18; ii++)
++ {
++ curVal = pca9535_gpio_read(ii);
++ printk(KERN_NOTICE "I2C[%d] = %d ", ii, curVal);
++ }
++*/
++ printk(KERN_NOTICE "\nh6300_gsm_p5186.c h6300_gsm_probe() done\n");
++
++ return 0;
++}
++
++static int
++h6300_gsm_remove(struct device *dev)
++{
++ struct h6300_uart_funcs *funcs = (struct h6300_uart_funcs *)dev->platform_data;
++
++ printk(KERN_NOTICE "h6300_gsm_p5186.c h6300_gsm_remove() started\n");
++
++ pca9535_gpio_write(GPIO_I2C_GPRS_RESET, 0); // turn off gpio
++
++ funcs->configure = NULL;
++ funcs->set_txrx = NULL;
++ funcs->get_txrx = NULL;
++
++ /* Make sure the LED is off */
++ h6300_clear_gsm_led(INDEX_GSM_LED);
++
++ printk(KERN_NOTICE "h6300_gsm_p5186.c, h6300_gsm_remove() done\n");
++
++ return 0;
++}
++
++static struct device_driver gsm_driver = {
++ .name = "h6300_gsm",
++ .bus = &platform_bus_type,
++ .probe = h6300_gsm_probe,
++ .remove = h6300_gsm_remove,
++};
++
++static int __init
++h6300_gsm_init(void)
++{
++ printk(KERN_NOTICE "h6300 GSM Driver init()\n");
++ return driver_register(&gsm_driver);
++}
++
++static void __exit
++h6300_gsm_exit(void)
++{
++ printk(KERN_NOTICE "h6300 GSM Driver exit()\n");
++ driver_unregister(&gsm_driver);
++}
++
++module_init(h6300_gsm_init);
++module_exit(h6300_gsm_exit);
++
++MODULE_AUTHOR("Mika Laitio, <lamikr@cc.jyu.fi>");
++MODULE_DESCRIPTION("iPAQ h6300 Wavecom P5186 GPRS and GSM module driver.");
++MODULE_LICENSE("GPL");
++
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/Makefile bt_kernel/drivers/telephony/omap/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/telephony/omap/Makefile 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/telephony/omap/Makefile 2005-10-06 02:34:39.057478000 +0300
+@@ -0,0 +1,6 @@
++#
++# Makefile for the Linux iPAQ H6300 BRF6100 Bluetooth device drivers.
++#
++
++h6300_gsm-objs := h6300_gsm_led.o h6300_gsm_p5186.o
++obj-$(CONFIG_GSM_H6300) += h6300_gsm.o
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/usb/gadget/omap_udc.c bt_kernel/drivers/usb/gadget/omap_udc.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/usb/gadget/omap_udc.c 2005-10-30 17:44:18.495920054 +0200
++++ bt_kernel/drivers/usb/gadget/omap_udc.c 2005-10-30 16:32:39.609796000 +0200
+@@ -622,17 +622,24 @@
+ || (cpu_is_omap15xx() && length < ep->maxpacket)) {
+ txdma_ctrl = UDC_TXN_EOT | length;
+ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
+- length, 1, sync_mode);
++ length, 1, sync_mode, 0, 0);
+ } else {
+ length = min(length / ep->maxpacket,
+ (unsigned) UDC_TXN_TSC + 1);
+ txdma_ctrl = length;
+- omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+- ep->ep.maxpacket >> 1, length, sync_mode);
++ if (machine_is_h6300())
++ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
++ ep->ep.maxpacket, length, sync_mode,
++ 0, 0);
++ else
++ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
++ ep->ep.maxpacket >> 1, length, sync_mode,
++ 0, 0);
+ length *= ep->maxpacket;
+ }
+ omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF,
+- OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual);
++ OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
++ 0, 0);
+
+ omap_start_dma(ep->lch);
+ ep->dma_counter = dma_csac(ep->lch);
+@@ -675,11 +682,19 @@
+ packets = (req->req.length - req->req.actual) / ep->ep.maxpacket;
+ packets = min(packets, (unsigned)UDC_RXN_TC + 1);
+ req->dma_bytes = packets * ep->ep.maxpacket;
+- omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+- ep->ep.maxpacket >> 1, packets,
+- OMAP_DMA_SYNC_ELEMENT);
++ if (machine_is_h6300())
++ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
++ ep->ep.maxpacket, packets,
++ OMAP_DMA_SYNC_ELEMENT,
++ 0, 0);
++ else
++ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
++ ep->ep.maxpacket >> 1, packets,
++ OMAP_DMA_SYNC_ELEMENT,
++ 0, 0);
+ omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
+- OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual);
++ OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
++ 0, 0);
+ ep->dma_counter = DMA_DEST_LAST(ep->lch);
+
+ UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1);
+@@ -822,7 +837,8 @@
+ omap_set_dma_dest_params(ep->lch,
+ OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG));
++ (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG),
++ 0, 0);
+ }
+ } else {
+ status = omap_request_dma(OMAP_DMA_USB_W2FC_RX0 - 1 + channel,
+@@ -833,7 +849,8 @@
+ omap_set_dma_src_params(ep->lch,
+ OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG));
++ (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG),
++ 0, 0);
+ /* EMIFF */
+ omap_set_dma_dest_burst_mode(ep->lch,
+ OMAP_DMA_DATA_BURST_4);
+@@ -2103,7 +2120,7 @@
+ /* boards that don't have VBUS sensing can't autogate 48MHz;
+ * can't enter deep sleep while a gadget driver is active.
+ */
+- if (machine_is_omap_innovator() || machine_is_omap_osk())
++ if (machine_is_omap_innovator() || machine_is_omap_osk() || machine_is_h6300())
+ omap_vbus_session(&udc->gadget, 1);
+
+ done:
+@@ -2121,7 +2138,7 @@
+ if (!driver || driver != udc->driver)
+ return -EINVAL;
+
+- if (machine_is_omap_innovator() || machine_is_omap_osk())
++ if (machine_is_omap_innovator() || machine_is_omap_osk() || machine_is_h6300())
+ omap_vbus_session(&udc->gadget, 0);
+
+ if (udc->transceiver)
+@@ -2729,7 +2746,7 @@
+ hmc = HMC_1510;
+ type = "(unknown)";
+
+- if (machine_is_omap_innovator()) {
++ if (machine_is_omap_innovator() || machine_is_h6300()) {
+ /* just set up software VBUS detect, and then
+ * later rig it so we always report VBUS.
+ * FIXME without really sensing VBUS, we can't
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/logo/.gitignore bt_kernel/drivers/video/logo/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/logo/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/drivers/video/logo/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,7 +0,0 @@
+-#
+-# Generated files
+-#
+-*_mono.c
+-*_vga16.c
+-*_clut224.c
+-*_gray256.c
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/lcd_h6300.c bt_kernel/drivers/video/omap/lcd_h6300.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/lcd_h6300.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/video/omap/lcd_h6300.c 2005-10-30 16:32:39.609796000 +0200
+@@ -0,0 +1,107 @@
++/*
++ * File: drivers/video/omap_new/lcd-h6300.c
++ *
++ * LCD panel support for the TI OMAP1510 Innovator board
++ *
++ * Copyright (C) 2004 Nokia Corporation
++ * Author: Imre Deak <imre.deak@nokia.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.
++ */
++
++#include <linux/module.h>
++#include <asm/io.h>
++
++#include <asm/hardware/clock.h>
++#include <asm/arch/omapfb.h>
++
++/* #define OMAPFB_DBG 1 */
++
++#include "debug.h"
++
++//static struct clk *h6300_lcd_ck;
++
++static int h6300_panel_init(struct omapfb_device *fbdev)
++{
++ DBGENTER(1);
++/*
++ if ((h6300_lcd_ck = clk_get (NULL, "lcd_ck")) == NULL) {
++ printk(KERN_ERR "Unable to get the clock LCD_CK!!!\n");
++ return -EPERM;
++ } clk_enable (h6300_lcd_ck);
++*/
++ DBGLEAVE(1);
++ printk(KERN_INFO "lcd_h6300.c: h6300_panel_init() done\n");
++ return 0;
++}
++
++static void h6300_panel_cleanup(void)
++{
++ DBGENTER(1);
++/*
++ if (h6300_lcd_ck) {
++ clk_disable (h6300_lcd_ck);
++ clk_put (h6300_lcd_ck);
++ h6300_lcd_ck = NULL;
++ }
++*/
++ DBGLEAVE(1);
++ printk(KERN_INFO "lcd_h6300.c: h6300_panel_cleanup() done\n");
++}
++
++static int h6300_panel_enable(void)
++{
++ DBGENTER(1);
++ DBGLEAVE(1);
++ printk(KERN_INFO "lcd_h6300.c: h6300_panel_enable() done\n");
++ return 0;
++}
++
++static void h6300_panel_disable(void)
++{
++ DBGENTER(1);
++ DBGLEAVE(1);
++ printk(KERN_INFO "lcd_h6300.c: h6300_panel_disable() done\n");
++}
++
++static unsigned long h6300_panel_get_caps(void)
++{
++ printk(KERN_INFO "lcd_h6300.c: h6300_panel_get_caps() called\n");
++ return 0;
++}
++
++struct lcd_panel h6300_panel = {
++ .name = "h6300",
++ .config = OMAP_LCDC_PANEL_TFT,
++
++ .bpp = 16,
++ .data_lines = 16,
++ .x_res = 240,
++ .y_res = 320,
++ .pixel_clock = 21000,
++ .hsw = 12,
++ .hfp = 10,
++ .hbp = 10,
++ .vsw = 3,
++ .vfp = 10,
++ .vbp = 3,
++ .pcd = 0,
++
++ .init = h6300_panel_init,
++ .cleanup = h6300_panel_cleanup,
++ .enable = h6300_panel_enable,
++ .disable = h6300_panel_disable,
++ .get_caps = h6300_panel_get_caps,
++};
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/Makefile bt_kernel/drivers/video/omap/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/Makefile 2005-10-30 17:44:18.730882654 +0200
++++ bt_kernel/drivers/video/omap/Makefile 2005-10-22 03:52:45.687256000 +0300
+@@ -21,6 +21,7 @@
+ objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
+ objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
+ objs-y$(CONFIG_MACH_OMAP_PERSEUS2) += lcd_p2.o
++objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_H6300) += lcd_h6300.o
+
+ omapfb-objs := $(objs-yy)
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/omapfb.h bt_kernel/drivers/video/omap/omapfb.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/omapfb.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/drivers/video/omap/omapfb.h 2005-08-12 13:46:22.016114000 +0300
+@@ -0,0 +1,325 @@
++/*
++ * File: drivers/video/omap_new/omapfb.c
++ *
++ * Framebuffer driver for TI OMAP boards
++ *
++ * Copyright (C) 2004 Nokia Corporation
++ * Author: Imre Deak <imre.deak@nokia.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.
++ */
++
++#ifndef __OMAPFB_H
++#define __OMAPFB_H
++
++/* IOCTL commands. */
++
++#define OMAP_IOW(num, dtype) _IOW('O', num, dtype)
++#define OMAP_IOR(num, dtype) _IOR('O', num, dtype)
++#define OMAP_IOWR(num, dtype) _IOWR('O', num, dtype)
++#define OMAP_IO(num) _IO('O', num)
++
++#define OMAPFB_FILLRECT OMAP_IOW(0, struct fb_fillrect)
++#define OMAPFB_COPYAREA OMAP_IOW(1, struct fb_copyarea)
++#define OMAPFB_IMAGEBLIT OMAP_IOW(2, struct fb_image)
++
++#define OMAPFB_TRANSPARENT_BLIT OMAP_IOW(30, struct fb_image)
++#define OMAPFB_MIRROR OMAP_IOW(31, int)
++#define OMAPFB_SCALE OMAP_IOW(32, struct fb_scale)
++#define OMAPFB_SELECT_VIS_FRAME OMAP_IOW(33, int)
++#define OMAPFB_SELECT_SRC_FRAME OMAP_IOW(34, int)
++#define OMAPFB_SELECT_DST_FRAME OMAP_IOW(35, int)
++#define OMAPFB_GET_FRAME_OFFSET OMAP_IOWR(36, struct fb_frame_offset)
++#define OMAPFB_SYNC_GFX OMAP_IO(37)
++#define OMAPFB_VSYNC OMAP_IO(38)
++#define OMAPFB_LATE_ACTIVATE OMAP_IO(39)
++#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, enum fb_update_mode)
++#define OMAPFB_UPDATE_WINDOW OMAP_IOW(41, struct fb_update_window)
++#define OMAPFB_GET_CAPS OMAP_IOR(42, unsigned long)
++#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, enum fb_update_mode)
++#define OMAPFB_GET_GFX_STATUS OMAP_IOR(44, unsigned long)
++
++#define FBCAPS_GENERIC_MASK 0x00000fff
++#define FBCAPS_LCDC_MASK 0x00fff000
++#define FBCAPS_PANEL_MASK 0xff000000
++
++#define FBCAPS_MANUAL_UPDATE 0x00001000
++#define FBCAPS_SET_BACKLIGHT 0x01000000
++
++enum omapfb_gfx_status {
++ OMAPFB_GFX_STATUS_OK = 0,
++ OMAPFB_GFX_STATUS_CHANGED
++};
++
++#define OMAPFB_UPDATE_FAILED 0x01
++#define OMAPFB_FILLRECT_FAILED 0x02
++#define OMAPFB_COPYAREA_FAILED 0x04
++#define OMAPFB_IMGBLIT_FAILED 0x08
++
++struct fb_copyarea_ext {
++ __u32 dx;
++ __u32 dy;
++ __u32 width;
++ __u32 height;
++ __u32 sx;
++ __u32 sy;
++ __u32 trans_color;
++ __u32 rev_dir;
++};
++
++struct fb_scale {
++ unsigned int xscale, yscale;
++};
++
++struct fb_frame_offset {
++ unsigned int idx;
++ unsigned long offset;
++};
++
++struct fb_update_window {
++ unsigned int x, y;
++ unsigned int width, height;
++};
++
++enum fb_update_mode {
++ FB_UPDATE_DISABLED = 0,
++ FB_AUTO_UPDATE,
++ FB_MANUAL_UPDATE
++};
++
++#ifdef __KERNEL__
++
++#include <linux/completion.h>
++#include <linux/interrupt.h>
++#include <linux/fb.h>
++
++#define OMAPFB_DEVICE "omapfb"
++#define OMAPFB_DRIVER "omapfb"
++
++#define PRNERR(fmt, args...) printk(KERN_ERR OMAPFB_DRIVER ": " fmt, ## args)
++
++#define GFX_FIFO_SIZE 2
++
++#define LCD_PANEL_TFT 0x01
++
++#define OMAP_LCDC_INV_VSYNC 0x01
++#define OMAP_LCDC_INV_HSYNC 0x02
++#define OMAP_LCDC_INV_PIX_CLOCK 0x04
++#define OMAP_LCDC_INV_OUTPUT_EN 0x08
++#define OMAP_LCDC_HSVS_RISING_EDGE 0x10
++#define OMAP_LCDC_HSVS_OPPOSITE 0x20
++
++struct lcdc_video_mode {
++ u16 x_res, y_res;
++ u32 pixel_clock; /* In kHz */
++ int bpp;
++ u8 hsw; /* Horizontal synchronization pulse width */
++ u8 hfp; /* Horizontal front porch */
++ u8 hbp; /* Horizontal back porch */
++ u8 vsw; /* Vertical synchronization pulse width */
++ u8 vfp; /* Vertical front porch */
++ u8 vbp; /* Vertical back porch */
++ u8 acb; /* ac-bias pin frequency */
++ u8 pcd; /* Pixel clock divider (this will change) */
++ u8 flags;
++};
++
++struct lcd_panel {
++ const char *name;
++ int config;
++ int signals;
++ struct lcdc_video_mode *video_mode;
++
++ int (*init) (struct lcd_panel *panel);
++ void (*cleanup) (struct lcd_panel *panel);
++ int (*enable) (struct lcd_panel *panel);
++ void (*disable) (struct lcd_panel *panel);
++ unsigned long (*get_caps)(struct lcd_panel *panel);
++ int (*set_bklight_level)(struct lcd_panel *panel,
++ unsigned int level);
++ unsigned int (*get_bklight_level)(struct lcd_panel *panel);
++ unsigned int (*get_bklight_max) (struct lcd_panel *panel);
++};
++
++struct omapfb_device;
++
++struct lcd_ctrl {
++ const char *name;
++ void *data;
++ int (*init) (struct omapfb_device *fbdev);
++ void (*cleanup) (struct omapfb_device *fbdev);
++ void (*get_mem_layout) (struct omapfb_device *fbdev,
++ unsigned long *size,
++ unsigned long *fb_org);
++ unsigned long (*get_caps) (struct omapfb_device *fbdev);
++ int (*set_update_mode)(struct omapfb_device *fbdev,
++ enum fb_update_mode mode);
++ enum fb_update_mode (*get_update_mode)(struct omapfb_device *fbdev);
++ int (*update_window) (struct omapfb_device *fbdev,
++ struct fb_update_window *win);
++ void (*suspend) (struct omapfb_device *fbdev);
++ void (*resume) (struct omapfb_device *fbdev);
++ void (*change_mode) (struct omapfb_device *fbdev);
++};
++
++enum omapfb_state {
++ OMAPFB_DISABLED = 0,
++ OMAPFB_SUSPENDED= 99,
++ OMAPFB_ACTIVE = 100
++};
++
++struct gfx_lchannel {
++ int lch_num;
++ struct gfx_lchannel *next, *prev;
++};
++
++struct gfx_dma {
++ spinlock_t spinlock;
++
++ struct completion sync_complete; /* Signalled when the
++ fifo gets empty */
++ volatile int done; /* Indicates the
++ end of a DMA chain
++ transfer */
++ struct gfx_lchannel fifo[GFX_FIFO_SIZE];
++ struct gfx_lchannel *f_head, *f_tail; /* Process and insert
++ points on the
++ fifo */
++ struct gfx_lchannel *f_chain_end; /* Points to the new
++ chain end */
++ struct semaphore f_free; /* # of free lch-s */
++ int f_run; /* # of active lch-s */
++ int f_wait; /* # of lch-s
++ waiting */
++ struct tasklet_struct dequeue_tasklet; /* Processes new DMA
++ chain transfers on
++ the fifo */
++};
++
++#define OMAPFB_RQUEUE_SIZE 20
++
++struct omapfb_fillrect_params
++{
++ struct fb_info *fbi;
++ struct fb_fillrect rect;
++};
++
++struct omapfb_copyarea_params
++{
++ struct fb_info *fbi;
++ struct fb_copyarea_ext area;
++};
++
++struct omapfb_update_window_params
++{
++ struct fb_info *fbi;
++ struct fb_update_window win;
++};
++
++struct omapfb_imageblit_params
++{
++ struct fb_info *fbi;
++ struct fb_image image;
++ int flags;
++};
++
++union req_params
++{
++ /* All possible requests are to be listed here */
++ struct omapfb_fillrect_params fillrect;
++ struct omapfb_copyarea_params copyarea;
++ struct omapfb_update_window_params update_window;
++ struct omapfb_imageblit_params imageblit;
++};
++
++struct omapfb_request
++{
++ struct list_head entry;
++ int (*function)(void *par);
++ union req_params par;
++};
++
++struct omapfb_rqueue
++{
++ spinlock_t lock;
++ struct list_head free_list;
++ struct list_head pending_list;
++ struct completion rqueue_empty;
++ struct semaphore free_sema;
++ struct omapfb_request req_pool[OMAPFB_RQUEUE_SIZE];
++ struct work_struct work;
++ unsigned long status;
++};
++
++struct omapfb_device {
++ int state;
++ int ext_lcdc; /* Using external
++ LCD controller */
++ void *lcddma_base; /* MPU virtual
++ address */
++ dma_addr_t lcddma_handle; /* Bus physical
++ address */
++ unsigned long lcddma_mem_size;
++ unsigned long palette_org; /* Palette offset into
++ lcddma_base/handle */
++ unsigned long frame0_org, frame1_org; /* Frame offsets for
++ back and front
++ frame buffers into
++ lcddma_base/handle */
++ unsigned long vis_frame_org; /* Offset of visible
++ frame buffer.
++ = frame0/1_org */
++ unsigned long src_frame_org; /* Offset of source
++ frame for drawing
++ operations.
++ = frame0/1_org */
++ unsigned long dst_frame_org; /* Offset of dest
++ frame for drawing
++ operations.
++ = frame0/1_org */
++ unsigned long view_org; /* View offset into
++ lcddma_base/handle+
++ vis_frame_org.
++ Used for panning */
++ unsigned long palette_size;
++ int xscale, yscale, mirror; /* transformations.
++ rotate is stored in
++ fb_info->var */
++
++ u32 pseudo_palette[17];
++
++ struct gfx_dma gfx; /* Accelerator */
++ struct omapfb_rqueue rqueue;
++ struct lcd_panel *panel; /* LCD panel */
++ struct lcd_ctrl *ctrl; /* LCD controller */
++
++ struct fb_info *fb_info; /* Linux fbdev
++ framework data */
++ struct device *dev;
++};
++
++extern struct lcd_panel h3_panel;
++extern struct lcd_panel h2_panel;
++extern struct lcd_panel p2_panel;
++extern struct lcd_panel osk_panel;
++extern struct lcd_panel innovator1610_panel;
++extern struct lcd_panel innovator1510_panel;
++extern struct lcd_panel h6300_panel;
++
++extern struct lcd_ctrl omapfb_lcdc_ctrl;
++
++#endif /* __KERNEL__ */
++
++#endif /* __OMAPFB_H */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/omapfb_main.c bt_kernel/drivers/video/omap/omapfb_main.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/drivers/video/omap/omapfb_main.c 2005-10-30 17:44:18.967844936 +0200
++++ bt_kernel/drivers/video/omap/omapfb_main.c 2005-10-24 19:16:46.600530000 +0300
+@@ -89,6 +89,7 @@
+ extern struct lcd_panel innovator1610_panel;
+ extern struct lcd_panel innovator1510_panel;
+ extern struct lcd_panel lph8923_panel;
++extern struct lcd_panel h6300_panel;
+
+ static struct lcd_panel *panels[] = {
+ #ifdef CONFIG_MACH_OMAP_H2
+@@ -109,6 +110,9 @@
+ #ifdef CONFIG_MACH_OMAP_PALMTE
+ &palmte_panel,
+ #endif
++#ifdef CONFIG_MACH_OMAP_H6300
++ &h6300_panel,
++#endif
+
+ #ifdef CONFIG_MACH_OMAP_INNOVATOR
+
+@@ -260,18 +264,40 @@
+ u_int blue, u_int transp, int update_hw_pal)
+ {
+ struct omapfb_device *fbdev = (struct omapfb_device *)info->par;
+- u16 pal;
+ int r = 0;
+
+- if (regno < 16) {
+- pal = ((red >> 11) << 11) | ((green >> 10) << 5) | (blue >> 11);
+- ((u32 *)(info->pseudo_palette))[regno] = pal;
+- }
++ switch (fbdev->color_mode) {
++ case OMAPFB_COLOR_YUV422:
++ case OMAPFB_COLOR_YUV420:
++ r = -EINVAL;
++ break;
++ case OMAPFB_COLOR_CLUT_8BPP:
++ case OMAPFB_COLOR_CLUT_4BPP:
++ case OMAPFB_COLOR_CLUT_2BPP:
++ case OMAPFB_COLOR_CLUT_1BPP:
++ if (fbdev->ctrl->setcolreg)
++ r = fbdev->ctrl->setcolreg(regno, red, green, blue,
++ transp, update_hw_pal);
++ /* Fallthrough */
++ case OMAPFB_COLOR_RGB565:
++ if (r != 0)
++ break;
+
+- if (fbdev->ctrl->setcolreg)
+- r = fbdev->ctrl->setcolreg(regno, red, green, blue, transp,
+- update_hw_pal);
++ if (regno < 0) {
++ r = -EINVAL;
++ break;
++ }
+
++ if (regno < 16) {
++ u16 pal;
++ pal = ((red >> 11) << 11) | ((green >> 10) << 5) |
++ (blue >> 11);
++ ((u32 *)(info->pseudo_palette))[regno] = pal;
++ }
++ break;
++ default:
++ BUG();
++ }
+ return r;
+ }
+
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/.gitignore bt_kernel/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,30 +0,0 @@
+-#
+-# NOTE! Don't add files that are generated in specific
+-# subdirectories here. Add them in the ".gitignore" file
+-# in that subdirectory instead.
+-#
+-# Normal rules
+-#
+-.*
+-*.o
+-*.a
+-*.s
+-*.ko
+-*.mod.c
+-
+-#
+-# Top-level generic files
+-#
+-vmlinux*
+-System.map
+-Module.symvers
+-
+-#
+-# Generated include files
+-#
+-include/asm
+-include/config
+-include/linux/autoconf.h
+-include/linux/compile.h
+-include/linux/version.h
+-
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/board-h6300.h bt_kernel/include/asm-arm/arch-omap/board-h6300.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/board-h6300.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/include/asm-arm/arch-omap/board-h6300.h 2005-08-12 13:46:22.016114000 +0300
+@@ -0,0 +1,40 @@
++/*
++ * linux/include/asm-arm/arch-omap/board-innovator.h
++ *
++ * Copyright (C) 2001 RidgeRun, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++#ifndef __ASM_ARCH_H6300_H
++#define __ASM_ARCH_H6300_H
++
++#ifndef OMAP_SDRAM_DEVICE
++#define OMAP_SDRAM_DEVICE D256M_1X16_4B
++#endif
++
++#define OMAP1510P1_IMIF_PRI_VALUE 0x00
++#define OMAP1510P1_EMIFS_PRI_VALUE 0x00
++#define OMAP1510P1_EMIFF_PRI_VALUE 0x00
++
++#define NR_FPGA_IRQS 24
++#define NR_IRQS IH_BOARD_BASE + NR_FPGA_IRQS
++
++#endif /* __ASM_ARCH_H6300_H */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/cpu.h bt_kernel/include/asm-arm/arch-omap/cpu.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/cpu.h 2005-10-30 17:44:19.208806581 +0200
++++ bt_kernel/include/asm-arm/arch-omap/cpu.h 2005-10-30 16:32:39.609796000 +0200
+@@ -215,4 +215,9 @@
+ # define cpu_is_omap2420() 1
+ #endif
+
++/* Macros to detect if we have OMAP1 or OMAP2 */
++#define cpu_class_is_omap1() (cpu_is_omap730() || cpu_is_omap15xx() || \
++ cpu_is_omap16xx())
++#define cpu_class_is_omap2() cpu_is_omap24xx()
++
+ #endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/dma.h bt_kernel/include/asm-arm/arch-omap/dma.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/dma.h 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/include/asm-arm/arch-omap/dma.h 2005-10-30 16:32:39.609796000 +0200
+@@ -217,19 +217,22 @@
+ extern void omap_stop_dma(int lch);
+ extern void omap_set_dma_transfer_params(int lch, int data_type,
+ int elem_count, int frame_count,
+- int sync_mode);
++ int sync_mode,
++ int dma_trigger, int src_or_dst_synch);
+ extern void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode,
+ u32 color);
+
+ extern void omap_set_dma_src_params(int lch, int src_port, int src_amode,
+- unsigned long src_start);
++ unsigned long src_start,
++ int src_ei, int src_fi);
+ extern void omap_set_dma_src_index(int lch, int eidx, int fidx);
+ extern void omap_set_dma_src_data_pack(int lch, int enable);
+ extern void omap_set_dma_src_burst_mode(int lch,
+ enum omap_dma_burst_mode burst_mode);
+
+ extern void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
+- unsigned long dest_start);
++ unsigned long dest_start,
++ int dst_ei, int dst_fi);
+ extern void omap_set_dma_dest_index(int lch, int eidx, int fidx);
+ extern void omap_set_dma_dest_data_pack(int lch, int enable);
+ extern void omap_set_dma_dest_burst_mode(int lch,
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/hardware.h bt_kernel/include/asm-arm/arch-omap/hardware.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/hardware.h 2005-10-30 17:44:19.364781754 +0200
++++ bt_kernel/include/asm-arm/arch-omap/hardware.h 2005-10-22 03:52:45.687256000 +0300
+@@ -290,6 +290,10 @@
+ #include "board-innovator.h"
+ #endif
+
++#ifdef CONFIG_MACH_OMAP_H6300
++#include "board-h6300.h"
++#endif
++
+ #ifdef CONFIG_MACH_OMAP_H2
+ #include "board-h2.h"
+ #endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/h6300_uart_info.h bt_kernel/include/asm-arm/arch-omap/h6300_uart_info.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/h6300_uart_info.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/include/asm-arm/arch-omap/h6300_uart_info.h 2005-10-14 18:55:31.156317000 +0300
+@@ -0,0 +1,33 @@
++/*
++ * Support file for calling h6300 uart configuration functions.
++ * Used at least by h6300_bt driver.
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ * 2005-03-29 Todd Blumer Converted basic structure to support hx4700
++ * 2005-10-03 Mika Laitio (lamikr@cc.jyu.fi) Reorganized for the iPAQ h6300 bt driver.
++ */
++
++#ifndef _H6300_UART_INFO_H
++#define _H6300_UART_INFO_H
++
++#include "omap_serial.h"
++
++#define GPIO_BT_PWR_EN 3
++#define GPIO_N_BT_RST 9
++
++#define GPIO_I2C_GPRS_RESET 16
++#define GPIO_I2C_MIC_OP_EN 10
++#define GPIO_I2C_SPK_OP_PD 11
++
++#define GPIO_VALUE_OFF 0
++#define GPIO_VALUE_ON 1
++
++#define GPIO_DIR_OUTPUT 1
++
++struct h6300_uart_funcs {
++ void (*configure)( struct uart_omap_port *up, int state);
++ void (*set_txrx)( struct uart_omap_port *up, int txrx);
++ int (*get_txrx)( struct uart_omap_port *up);
++};
++
++#endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/mux.h bt_kernel/include/asm-arm/arch-omap/mux.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/mux.h 2005-10-30 17:44:19.465765680 +0200
++++ bt_kernel/include/asm-arm/arch-omap/mux.h 2005-10-14 18:55:31.156317000 +0300
+@@ -316,6 +316,13 @@
+ P15_1610_UWIRE_CS3,
+ N15_1610_UWIRE_CS1,
+
++ /* OMAP-1510 uWire */
++ P15_1510_UWIRE_CS3,
++ N14_1510_UWIRE_CS0,
++ V19_1510_UWIRE_SCLK,
++ W21_1510_UWIRE_SDO,
++ U18_1510_UWIRE_SDI,
++
+ /* OMAP-1610 Flash */
+ L3_1610_FLASH_CS2B_OE,
+ M8_1610_FLASH_CS2B_WE,
+@@ -380,6 +387,7 @@
+ T20_1610_LOW_PWR,
+
+ /* MCLK Settings */
++ R10_1510_MCLK_ON,
+ V5_1710_MCLK_ON,
+ V5_1710_MCLK_OFF,
+ R10_1610_MCLK_ON,
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/omapfb.h bt_kernel/include/asm-arm/arch-omap/omapfb.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/omapfb.h 2005-10-30 17:44:19.557751038 +0200
++++ bt_kernel/include/asm-arm/arch-omap/omapfb.h 2005-10-22 03:52:45.687256000 +0300
+@@ -267,6 +267,7 @@
+ extern struct lcd_panel osk_panel;
+ extern struct lcd_panel innovator1610_panel;
+ extern struct lcd_panel innovator1510_panel;
++extern struct lcd_panel h6300_panel;
+
+ #ifdef CONFIG_ARCH_OMAP1
+ extern struct lcd_ctrl omap1_lcd_ctrl;
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/omap_serial.h bt_kernel/include/asm-arm/arch-omap/omap_serial.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/omap_serial.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/include/asm-arm/arch-omap/omap_serial.h 2005-10-04 00:58:34.589442000 +0300
+@@ -0,0 +1,62 @@
++/*
++ * Omap/h6300 serial driver private interface.
++ * Code originates from the pxa-serial.h available in the handheld org drivers
++ * for iPAQ PXA4700.
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ * 2005-03-29 Todd Blumer Converted basic structure to support hx4700
++ * 2005-10-03 Mika Laitio (lamikr@cc.jyu.fi) Reorganized for the iPAQ h6300 bt driver.
++ */
++
++#ifndef _OMAP_SERIAL_H
++#define _OMAP_SERIAL_H
++
++#define OMAP_SERIAL_TX 1
++#define OMAP_SERIAL_RX 2
++
++#include <linux/tty.h>
++#include <linux/serial_core.h>
++
++struct platform_omap_serial_funcs;
++
++struct uart_omap_port {
++ struct uart_port port;
++ unsigned char ier;
++ unsigned char lcr;
++ unsigned char mcr;
++ unsigned int lsr_break_flag;
++ unsigned int cken;
++ char *name;
++ struct platform_omap_serial_funcs *pf;
++};
++
++/* A pointer to such a structure can be contained in the platform_data
++ * field of every PXA UART platform_device. If the field is NULL, the
++ * serial port works as usual.
++ *
++ * For the sake of simplicity/performance no one of the function pointers
++ * in the structure below can be NULL.
++ */
++struct platform_omap_serial_funcs {
++ /* Platform-specific function to initialize whatever is connected
++ to this serial port... enable=1 -> enable transceiver,
++ 0 -> disable transceiver. */
++ void (*configure) (struct uart_omap_port *up, int enable);
++ /* Platform-specific function to enable or disable the individual
++ transmitter/receiver submodules. On transceivers without echo
++ cancellation (e.g. SIR) transmitter always has priority, e.g.
++ if both bits are set, only the transmitter is enabled. */
++ void (*set_txrx) (struct uart_omap_port *up, int txrx);
++ /* Get the current state of tx/rx (see bitflags above) */
++ int (*get_txrx) (struct uart_omap_port *up);
++};
++
++/*
++ * The variables below are located in arch/arm/mach-omap/board_h6300.c
++ * Machine-specific code may want to put a pointer to a static
++ * platform_pxa_serial_funcs structure in the dev.platform_data
++ * field of the respective port device.
++ */
++extern struct platform_device btuart_device;
++
++#endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/pca9535.h bt_kernel/include/asm-arm/arch-omap/pca9535.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/include/asm-arm/arch-omap/pca9535.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/include/asm-arm/arch-omap/pca9535.h 2005-10-25 03:24:45.525766000 +0300
+@@ -0,0 +1,39 @@
++#ifndef _PCA9535_H
++#define _PCA9535_H
++
++enum pca9535_gpios {
++ GPIO0 = 0,
++ GPIO1 = 1,
++ GPIO2 = 2,
++ GPIO3 = 3,
++ GPIO4 = 4,
++ GPIO5 = 5,
++ GPIO6 = 6,
++ GPIO7 = 7,
++ GPIO8 = 8,
++ GPIO9 = 9,
++ GPIO10 = 10,
++ GPIO11 = 11,
++ GPIO12 = 12,
++ GPIO13 = 13,
++ GPIO14 = 14,
++ GPIO15 = 15,
++ GPIO16 = 16,
++ GPIO17 = 17
++};
++
++enum gpio_values {
++ HI = 0,
++ LOW = 1
++};
++
++enum gpio_direction {
++ GPIO_INPUT = 0,
++ GPIO_OUTPUT = 1
++};
++
++extern int pca9535_gpio_read(int gpio);
++extern int pca9535_gpio_write(int gpio, unsigned char val);
++extern int pca9535_gpio_direction(int gpio, unsigned char direction);
++
++#endif
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/lib/.gitignore bt_kernel/lib/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/lib/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/lib/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,6 +0,0 @@
+-#
+-# Generated files
+-#
+-gen_crc32table
+-crc32table.h
+-
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/Makefile bt_kernel/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/Makefile 2005-10-30 17:44:14.606539141 +0200
++++ bt_kernel/Makefile 2005-10-22 03:52:45.687256000 +0300
+@@ -11,7 +11,7 @@
+ # expect to learn how to build the kernel reading this file.
+
+ # Add custom flags here to avoid conflict with updates
+-EXTRAVERSION := $(EXTRAVERSION)-omap1
++EXTRAVERSION := $(EXTRAVERSION)-omap-h6300
+
+ # Do not print "Entering directory ..."
+ MAKEFLAGS += --no-print-directory
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/basic/.gitignore bt_kernel/scripts/basic/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/basic/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/scripts/basic/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,3 +0,0 @@
+-fixdep
+-split-include
+-docproc
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/.gitignore bt_kernel/scripts/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/scripts/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,4 +0,0 @@
+-conmakehash
+-kallsyms
+-pnmtologo
+-
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/kconfig/.gitignore bt_kernel/scripts/kconfig/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/kconfig/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/scripts/kconfig/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,16 +0,0 @@
+-#
+-# Generated files
+-#
+-config*
+-lex.*.c
+-*.tab.c
+-*.tab.h
+-
+-#
+-# configuration programs
+-#
+-conf
+-mconf
+-qconf
+-gconf
+-kxgettext
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/mod/.gitignore bt_kernel/scripts/mod/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/scripts/mod/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/scripts/mod/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,4 +0,0 @@
+-elfconfig.h
+-mk_elfconfig
+-modpost
+-
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/Kconfig bt_kernel/sound/arm/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/Kconfig 2005-10-30 17:44:20.184651252 +0200
++++ bt_kernel/sound/arm/Kconfig 2005-10-14 18:55:31.156317000 +0300
+@@ -15,6 +15,13 @@
+ To compile this driver as a module, choose M here: the module
+ will be called snd-sa11xx-uda1341.
+
++config SND_OMAP_TSC2101
++ tristate "OMAP TSC2101 driver (iPaq H63xx)"
++ depends ARCH_OMAP1
++ select SND_PCM
++ help
++ ALSA driver for TI TSC2101.
++
+ config SND_ARMAACI
+ tristate "ARM PrimeCell PL041 AC Link support"
+ depends on SND && ARM_AMBA
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/Makefile bt_kernel/sound/arm/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/Makefile 2005-10-30 17:44:20.234643295 +0200
++++ bt_kernel/sound/arm/Makefile 2005-10-14 18:55:31.156317000 +0300
+@@ -16,3 +16,6 @@
+
+ obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-aic23.o
+ snd-omap-aic23-objs := omap-aic23.o omap-alsa-dma.o omap-alsa-mixer.o
++
++obj-$(CONFIG_SND) += \
++ omap/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/Makefile bt_kernel/sound/arm/omap/Makefile
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/Makefile 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/sound/arm/omap/Makefile 2005-08-12 13:46:22.016114000 +0300
+@@ -0,0 +1,8 @@
++#
++# Makefile for ALSA
++#
++
++snd-omap-tsc2101-objs := tsc2101_main.o tsc2101_mix.o tsc2101_pcm.o
++
++# Toplevel Module Dependency
++obj-$(CONFIG_SND_OMAP_TSC2101) += snd-omap-tsc2101.o
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/omap-tsc2101.h bt_kernel/sound/arm/omap/omap-tsc2101.h
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/omap-tsc2101.h 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/sound/arm/omap/omap-tsc2101.h 2005-08-20 00:24:45.622662000 +0300
+@@ -0,0 +1,81 @@
++/*
++ * OMAP tsc2101 soundcard
++ * Copyright (c) by Everett Coleman II <gcc80x86@fuzzyneural.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++#ifndef __SOUND_OMAP_TSC2101_H
++# define __SOUND_OMAP_TSC2101_H 1
++
++# include <sound/driver.h>
++# include <linux/init.h>
++# include <sound/core.h>
++# include <sound/initval.h>
++# include <sound/control.h>
++# include <sound/pcm.h>
++# include <asm/arch/dma.h>
++# include <asm/hardware/tsc2101.h>
++# include <../drivers/ssi/omap-tsc2101.h>
++
++# define TSC2101_AUDIO_CODEC_REGISTERS 2
++# define OUTPUT_VOLUME_MIN 0
++# define OUTPUT_VOLUME_MAX 127
++# define AUDIO_RATE_DEFAULT 44100
++# define AUDIO_BPS_DEFAULT 16
++
++# define AUDIO_DMA_BUF_SIZE (8 * 1024)
++
++# define AUDIO_DMA_RX OMAP_DMA_MCBSP1_RX
++# define AUDIO_DMA_TX OMAP_DMA_MCBSP1_TX
++# define AUDIO_MCBSP OMAP_MCBSP1
++
++# define DEBUG 1
++# ifdef DEBUG
++# define dprintk(x, y...) printk (x, ##y);
++# else
++# define dprintk(x, y...) {}
++# endif /* DEBUG */
++
++//# define DUMP_TSC2101_REGISTERS
++
++typedef struct audio_stream {
++ char *id;
++ int sid;
++ int dev;
++ int channel;
++ spinlock_t lock;
++ unsigned int offset, period, position;
++ u8 active;
++ snd_pcm_substream_t *stream;
++} audio_stream_t;
++
++typedef struct snd_card_tsc2101 {
++ snd_card_t *card;
++ snd_pcm_t *pcm;
++
++ long samplerate;
++ audio_stream_t s[2];
++} snd_card_tsc2101_t;
++
++# ifdef DUMP_TSC2101_REGISTERS
++void dump_tsc2101_reg (void);
++# endif /* DUMP_TSC2101_REGISTERS */
++
++int snd_tsc2101_setup_mix (snd_card_tsc2101_t *);
++int snd_tsc2101_setup_pcm (snd_card_tsc2101_t *);
++void snd_tsc2101_disable_pcm (snd_card_tsc2101_t *);
++
++#endif /* __SOUND_OMAP_TSC2101_H */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/tsc2101_main.c bt_kernel/sound/arm/omap/tsc2101_main.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/tsc2101_main.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/sound/arm/omap/tsc2101_main.c 2005-08-12 13:46:22.016114000 +0300
+@@ -0,0 +1,109 @@
++/*
++ * OMAP tsc2101 soundcard
++ * Copyright (c) by Everett Coleman II <gcc80x86@fuzzyneural.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include "omap-tsc2101.h"
++
++#ifdef DUMP_TSC2101_REGISTERS
++void
++dump_tsc2101_reg (void) {
++ int i=0;
++ printk ("TSC2101 Register DUMP:\n");
++ for (i=0; i <= 0x27; i++) {
++ printk ("0x%02x: %04x ", i, omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, i));
++ if ((i % 4) == 3)
++ printk ("\n");
++ } printk ("\n");
++} /* dump_tsc2101_reg */
++#endif /* DUMP_TSC2101_REGISTERS */
++
++/* Globals
++ **********/
++static struct snd_card_tsc2101 *tsc2101=NULL;
++static char *id=NULL;
++
++/* Module init/exit Functions
++ ****************************/
++static void
++snd_tsc2101_free (snd_card_t *card) {
++ snd_card_tsc2101_t *chip=card->private_data;
++
++ tsc2101=0;
++ card->private_data=0;
++ kfree (chip);
++} /* snd_tsc2101_free */
++
++static int __init
++alsa_card_tsc2101_init (void) {
++ int err=0;
++ snd_card_t *card;
++
++ if (!machine_is_h6300 ())
++ return -ENODEV;
++
++ if ((card=snd_card_new (-1, id, THIS_MODULE, sizeof (snd_card_tsc2101_t))) == 0)
++ return -ENOMEM;
++ if ((tsc2101=kcalloc (1, sizeof (*tsc2101), GFP_KERNEL)) == 0)
++ return -ENOMEM;
++
++ card->private_data=(void *)tsc2101;
++ card->private_free=snd_tsc2101_free;
++
++ tsc2101->card=card;
++ tsc2101->samplerate=AUDIO_RATE_DEFAULT;
++
++ if ((err=snd_tsc2101_setup_pcm (tsc2101)) < 0)
++ goto nodev;
++ if ((err=snd_tsc2101_setup_mix (tsc2101)) < 0)
++ goto nodev;
++
++ // TODO: PM
++
++ strncpy (card->driver, "TSC2101", sizeof (card->driver));
++ strncpy (card->shortname, "OMAP TSC2101", sizeof (card->shortname));
++ strncpy (card->longname, "TI OMAP TSC2101 Audio Codec", sizeof (card->longname));
++
++ if ((err=snd_card_register (card)) == 0) {
++ printk (KERN_INFO "TSC2101 audio support initialized\n");
++ return 0;
++ }
++
++nodev:
++ snd_tsc2101_disable_pcm (tsc2101);
++ snd_card_free (card);
++ return err;
++} /* alsa_card_tsc2101_init */
++
++static void __exit
++alsa_card_tsc2101_exit (void) {
++ snd_tsc2101_disable_pcm (tsc2101);
++ snd_card_free(tsc2101->card);
++} /* alsa_card_tsc2101_exit */
++
++module_init(alsa_card_tsc2101_init)
++module_exit(alsa_card_tsc2101_exit)
++
++MODULE_AUTHOR("Everett Coleman II <gcc80x86@fuzzyneural.net>");
++MODULE_DESCRIPTION("TI TSC2101 Audio Driver");
++MODULE_LICENSE("GPL");
++
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for OMAP TSC2101 soundcard.");
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/tsc2101_mix.c bt_kernel/sound/arm/omap/tsc2101_mix.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/tsc2101_mix.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/sound/arm/omap/tsc2101_mix.c 2005-08-12 13:46:22.016114000 +0300
+@@ -0,0 +1,211 @@
++/*
++ * OMAP tsc2101 soundcard
++ * Copyright (c) by Everett Coleman II <gcc80x86@fuzzyneural.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include "omap-tsc2101.h"
++
++static int
++__pcm_playback_volume_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
++ uinfo->type =SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count=2;
++ uinfo->value.integer.min=OUTPUT_VOLUME_MIN;
++ uinfo->value.integer.max=OUTPUT_VOLUME_MAX;
++ return 0;
++} /* __pcm_playback_volume_info */
++
++# define dgc_dalvl_extract(x) ((x & 0x7f00) >> 8)
++# define dgc_darvl_extract(x) ((x & 0x007f))
++static int
++__pcm_playback_volume_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_DAC_GAIN_CTRL);
++ ucontrol->value.integer.value[0]=dgc_dalvl_extract(val); // L
++ ucontrol->value.integer.value[1]=dgc_darvl_extract(val); // R
++ return 0;
++} /* __pcm_playback_volume_get */
++
++static int
++__pcm_playback_volume_put (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_DAC_GAIN_CTRL);
++ int count=0;
++ if (dgc_dalvl_extract(val) != ucontrol->value.integer.value[0]) {
++ val=(val & ~DGC_DALVL(OUTPUT_VOLUME_MAX)) |
++ DGC_DALVL(ucontrol->value.integer.value[0]);
++ count++;
++ } /* L */
++
++ if (dgc_darvl_extract(val) != ucontrol->value.integer.value[1]) {
++ val=(val & ~DGC_DARVL(OUTPUT_VOLUME_MAX)) |
++ DGC_DARVL((u16)ucontrol->value.integer.value[1]);
++ count++;
++ } /* R */
++
++ if (count)
++ omap_tsc2101_write (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_DAC_GAIN_CTRL, val);
++
++ return count;
++} /* __pcm_playback_volume_put */
++
++static int
++__pcm_playback_switch_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
++ uinfo->type =SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count=2;
++ uinfo->value.integer.min=0;
++ uinfo->value.integer.max=1;
++ return 0;
++} /* __pcm_playback_switch_info */
++
++static int
++__pcm_playback_switch_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_DAC_GAIN_CTRL);
++ ucontrol->value.integer.value[0]=(val & DGC_DALMU) == DGC_DALMU; // L
++ ucontrol->value.integer.value[1]=(val & DGC_DARMU) == DGC_DARMU; // R
++ return 0;
++} /* __pcm_playback_switch_get */
++
++static int
++__pcm_playback_switch_put (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_DAC_GAIN_CTRL);
++ int count=0;
++
++ if (((val & DGC_DALMU) == DGC_DALMU) != ucontrol->value.integer.value[0]) {
++ val=(val & ~DGC_DALMU) |
++ (ucontrol->value.integer.value[0] ? DGC_DALMU : 0);
++ count++;
++ } /* L */
++ if (((val & DGC_DARMU) == DGC_DARMU) != ucontrol->value.integer.value[1]) {
++ val=(val & ~DGC_DARMU) |
++ (ucontrol->value.integer.value[1] ? DGC_DARMU : 0);
++ count++;
++ } /* R */
++
++ if (count)
++ omap_tsc2101_write (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_DAC_GAIN_CTRL, val);
++ return count;
++} /* __pcm_playback_switch_put */
++
++static int
++__line_playback_volume_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
++ uinfo->type =SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count=1;
++ uinfo->value.integer.min=OUTPUT_VOLUME_MIN;
++ uinfo->value.integer.max=OUTPUT_VOLUME_MAX;
++ return 0;
++} /* __line_playback_volume_info */
++
++#define hgc_adpga_hed_extract(x) ((x & 0x7f00) >> 8)
++static int
++__line_playback_volume_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_HEADSET_GAIN_CTRL);
++ ucontrol->value.integer.value[0]=hgc_adpga_hed_extract(val);
++ return 0;
++} /* __line_playback_volume_get */
++
++static int
++__line_playback_volume_put (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_HEADSET_GAIN_CTRL);
++ int count=0;
++ if (hgc_adpga_hed_extract(val) != ucontrol->value.integer.value[0]) {
++ val=(val & ~HGC_ADPGA_HED(OUTPUT_VOLUME_MAX)) |
++ HGC_ADPGA_HED((u16)ucontrol->value.integer.value[0]);
++ count++;
++ }
++ if (count)
++ omap_tsc2101_write (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_HEADSET_GAIN_CTRL, val);
++ return count;
++} /* __line_playback_volume_put */
++
++static int
++__line_playback_switch_info (snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
++ uinfo->type =SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count=1;
++ uinfo->value.integer.min=0;
++ uinfo->value.integer.max=1;
++ return 0;
++} /* __line_playback_switch_info */
++
++static int
++__line_playback_switch_get (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_HEADSET_GAIN_CTRL);
++ ucontrol->value.integer.value[0]=(val & HGC_ADMUT_HED) == HGC_ADMUT_HED;
++ return 0;
++} /* __line_playback_switch_get */
++
++static int
++__line_playback_switch_put (snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
++ u16 val=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_HEADSET_GAIN_CTRL);
++ int count=0;
++
++ if (((val & HGC_ADMUT_HED) == HGC_ADMUT_HED) != ucontrol->value.integer.value[0]) {
++ val=(val & ~HGC_ADMUT_HED) |
++ (ucontrol->value.integer.value[0] ? HGC_ADMUT_HED : 0);
++ count++;
++ }
++
++ if (count)
++ omap_tsc2101_write (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_HEADSET_GAIN_CTRL, val);
++ return count;
++} /* __line_playback_switch_put */
++
++
++static snd_kcontrol_new_t tsc2101_control[] __devinitdata={
++ { .name = "PCM Playback Volume",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 0,
++ .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __pcm_playback_volume_info,
++ .get = __pcm_playback_volume_get,
++ .put = __pcm_playback_volume_put,
++ }, {
++ .name = "PCM Playback Switch",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 0,
++ .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __pcm_playback_switch_info,
++ .get = __pcm_playback_switch_get,
++ .put = __pcm_playback_switch_put,
++ }, {
++ .name = "Line Playback Volume",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 1,
++ .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __line_playback_volume_info,
++ .get = __line_playback_volume_get,
++ .put = __line_playback_volume_put,
++ }, {
++ .name = "Line Playback Switch",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 1,
++ .access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __line_playback_switch_info,
++ .get = __line_playback_switch_get,
++ .put = __line_playback_switch_put,
++ }
++};
++
++int
++snd_tsc2101_setup_mix (snd_card_tsc2101_t *tsc2101) {
++ int i=0, err=0;
++
++ if (!tsc2101)
++ return -EINVAL;
++ for (i=0; i < ARRAY_SIZE(tsc2101_control); i++)
++ if ((err=snd_ctl_add (tsc2101->card, snd_ctl_new1 (&tsc2101_control[i], tsc2101->card))) < 0)
++ return err;
++ return 0;
++} /* snd_tsc2101_setup_mix */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/tsc2101_pcm.c bt_kernel/sound/arm/omap/tsc2101_pcm.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap/tsc2101_pcm.c 1970-01-01 02:00:00.000000000 +0200
++++ bt_kernel/sound/arm/omap/tsc2101_pcm.c 2005-08-31 01:22:50.289885000 +0300
+@@ -0,0 +1,555 @@
++/*
++ * OMAP tsc2101 soundcard
++ * Copyright (c) by Everett Coleman II <gcc80x86@fuzzyneural.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <asm/arch/mcbsp.h>
++#include <linux/delay.h>
++#include <asm/arch/gpio.h>
++#include "omap-tsc2101.h"
++
++static snd_pcm_hardware_t pcm_hardware[2] = {
++ {
++ .info =
++ SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE |
++ SNDRV_PCM_INFO_RESUME,
++ .formats = SNDRV_PCM_FMTBIT_U16_LE,
++ .rates =
++ SNDRV_PCM_RATE_8000_44100 |
++ SNDRV_PCM_RATE_KNOT,
++ .rate_min = 7350,
++ .rate_max = 48000,
++ .channels_min = 2,
++ .channels_max = 2,
++ .buffer_bytes_max = 128 * 1024,
++ .period_bytes_min = 64,
++ .period_bytes_max = AUDIO_DMA_BUF_SIZE,
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++ }, {
++ .info =
++ SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE |
++ SNDRV_PCM_INFO_RESUME,
++ .formats = SNDRV_PCM_FMTBIT_U16_LE,
++ .rates =
++ SNDRV_PCM_RATE_8000_44100 |
++ SNDRV_PCM_RATE_KNOT,
++ .rate_min = 7350,
++ .rate_max = 48000,
++ .channels_min = 2,
++ .channels_max = 2,
++ .buffer_bytes_max = 128 * 1024,
++ .period_bytes_min = 64,
++ .period_bytes_max = AUDIO_DMA_BUF_SIZE,
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++ }
++};
++
++static unsigned int rate_list[] = {
++ 7350, 8000, 8018, 8727,
++ 8820, 9600, 11025, 12000,
++ 14700, 16000, 22050, 24000,
++ 29400, 32000, 44100, 48000
++};
++
++static snd_pcm_hw_constraint_list_t rate_constraint = {
++ .count = ARRAY_SIZE (rate_list),
++ .list = rate_list,
++ .mask = 0,
++};
++
++
++static int
++__tsc2101_open (snd_pcm_substream_t *substream) {
++ snd_pcm_runtime_t *runtime = substream->runtime;
++ snd_card_tsc2101_t *chip = snd_pcm_substream_chip (substream);
++ int sid = substream->pstr->stream, err;
++
++ dprintk ("[%s]\n", __FUNCTION__);
++ chip->s[sid].stream = substream;
++ runtime->hw = pcm_hardware[sid];
++
++ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
++ return err;
++ if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &rate_constraint)) < 0)
++ return err;
++
++ dprintk ("[%s] pass\n", __FUNCTION__);
++ return 0;
++} /* __tsc2101_open */
++
++static int
++__tsc2101_close (snd_pcm_substream_t *substream) {
++ snd_card_tsc2101_t *chip = snd_pcm_substream_chip (substream);
++ dprintk ("[%s]\n", __FUNCTION__);
++ chip->s[substream->pstr->stream].stream = 0;
++ dprintk ("[%s] pass\n", __FUNCTION__);
++ return 0;
++} /* __tsc2101_close */
++
++static int
++__tsc2101_hw_params (snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) {
++ dprintk ("[%s> pass\n", __FUNCTION__);
++ return snd_pcm_lib_malloc_pages (substream, params_buffer_bytes (hw_params));
++} /* __tsc2101_hw_params */
++
++static int
++__tsc2101_hw_free (snd_pcm_substream_t *substream) {
++ dprintk ("[%s> pass\n", __FUNCTION__);
++ return snd_pcm_lib_free_pages (substream);
++} /* __tsc2101_hw_free */
++
++static struct omap_mcbsp_reg_cfg mcbsp_cfg = {
++// .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
++// .pcr0 = CLKXP | CLKRP,
++ .pcr0 = CLKRM | SCLKME | FSXP | FSRP | CLKXP | CLKRP,
++
++// .spcr2 = FREE | FRST | GRST | XRST | XINTM(3) | XEMPTY,
++// .spcr1 = RINTM(3) | RRST | RFULL,
++ .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
++ .spcr1 = RINTM(3) | RRST,
++
++ .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
++ RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1),
++ .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
++ .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
++ XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG,
++ .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
++ .srgr1 = FWID(15),
++ .srgr2 = GSYNC | CLKSP | FSGM | FPER(31),
++};
++
++static int
++__set_samplerate (snd_card_tsc2101_t *tsc2101, long sample_rate) {
++ static u8 div_list[] = {
++ 7, 7, 6, 6,
++ 5, 5, 4, 4,
++ 3, 3, 2, 2,
++ 1, 1, 0, 0
++ };
++ int i = 0, clkgdv = 0;
++ u8 khz = 0;
++ u16 data = 0;
++
++ dprintk ("[%s]\n", __FUNCTION__);
++ udelay (125);
++ for (i=rate_constraint.count-1; i >= 0 ; i--)
++ if (sample_rate >= rate_list[i])
++ break;
++ if (i == -1)
++ i=0;
++ khz=(i%2) ? 0 : 1;
++
++ dprintk ("rate=%u\n", rate_list[i]);
++
++ data=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_AUDIO_CTRL_1);
++ data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07));
++ data |= AC1_DACFS(div_list[i])
++ | AC1_ADCFS(div_list[i]);
++ omap_tsc2101_write (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_AUDIO_CTRL_1, data);
++
++ data=omap_tsc2101_read (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_AUDIO_CTRL_3);
++ data &= ~(AC3_REFFS | AC3_SLVMS);
++ data |= (khz) ? AC3_REFFS : 0;
++ data |= AC3_SLVMS;
++ omap_tsc2101_write (TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_AUDIO_CTRL_3, data);
++
++ if (khz) {
++ omap_tsc2101_write (
++ TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_PLL_PROG_1,
++ PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(7));
++ omap_tsc2101_write (
++ TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_PLL_PROG_2,
++ PLL2_D_VAL(0x1490));
++
++ } else {
++ omap_tsc2101_write (
++ TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_PLL_PROG_1,
++ PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(8));
++ omap_tsc2101_write (
++ TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_PLL_PROG_2,
++ PLL2_D_VAL(0x780));
++ }
++
++ tsc2101->samplerate=rate_list[i];
++
++ mcbsp_cfg.srgr1 =
++ (FWID(AUDIO_BPS_DEFAULT - 1) | CLKGDV(clkgdv));
++ mcbsp_cfg.srgr2 =
++ ((GSYNC | CLKSP | FSGM | FPER(AUDIO_BPS_DEFAULT * 2 - 1)));
++
++ omap_mcbsp_config(AUDIO_MCBSP, &mcbsp_cfg);
++ dprintk ("[%s] pass\n", __FUNCTION__);
++ return 0;
++} /* __set_samplerate */
++
++static int
++__tsc2101_prepare (snd_pcm_substream_t *substream) {
++ snd_pcm_runtime_t *runtime = substream->runtime;
++ snd_card_tsc2101_t *chip = snd_pcm_substream_chip (substream);
++ audio_stream_t *s = &chip->s[substream->pstr->stream];
++
++ dprintk ("[%s]\n", __FUNCTION__);
++ __set_samplerate (chip, runtime->rate);
++
++ // set FMT here ????
++ s->period =0;
++ s->offset =0;
++ s->position=0;
++ dprintk ("[%s] pass\n", __FUNCTION__);
++ return 0;
++} /* __tsc2101_prepare */
++
++# define DCSR_ERROR 0x3
++# define DCSR_END_BLOCK (1 << 5)
++# define DCCR_EN (1 << 7)
++static void
++tsc2101_playback_callback (int channel, u16 ch_status, void *data) {
++ audio_stream_t *s=(audio_stream_t *)data;
++
++ dprintk ("[%s]\n", __FUNCTION__);
++
++ if (ch_status & DCSR_ERROR) {
++ omap_writew (omap_readw (OMAP_DMA_CCR (channel)) &
++ ~DCCR_EN, OMAP_DMA_CCR (channel));
++ printk (KERN_ERR "[%s] DCSR_ERROR!\n", __FUNCTION__);
++ dprintk ("[%s] x-fail\n", __FUNCTION__);
++ return;
++ }
++
++ if (!(ch_status & DCSR_END_BLOCK)) {
++ dprintk ("[%s] y-fail\n", __FUNCTION__);
++ return;
++ }
++
++ if (s->active) {
++ unsigned int dma_size;
++ unsigned int offset;
++ snd_pcm_runtime_t *runtime;
++
++ snd_pcm_period_elapsed (s->stream);
++
++ spin_lock (&s->lock);
++ runtime=s->stream->runtime;
++
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ offset = dma_size * s->period;
++ snd_assert(dma_size <= AUDIO_DMA_BUF_SIZE,);
++
++ dprintk ("pb: offset=%08lx size=%u period=%u\n", (unsigned long)(runtime->dma_area + offset), dma_size, s->period);
++#if 0
++ omap_set_dma_transfer_params(channel, OMAP_DMA_DATA_TYPES16, 32, dma_size/(2*32), OMAP_DMA_SYNC_ELEMENT);
++ omap_set_dma_dest_params(channel, OMAP_DMA_PORT_MPUI, OMAP_DMA_AMODE_CONSTANT, (OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1));
++ omap_set_dma_src_params(channel, 0x00, 0x01, (unsigned long)(runtime->dma_area + offset));
++ omap_start_dma (channel);
++#else
++// omap_set_gpio_dataout (2, 1);
++ omap_set_dma_transfer_params (channel,
++ OMAP_DMA_DATA_TYPE_S16,
++ dma_size >> 1, 1,
++ OMAP_DMA_SYNC_ELEMENT
++ );
++ omap_set_dma_dest_params (channel,
++// OMAP_DMA_PORT_TIPB,
++ OMAP_DMA_PORT_MPUI,
++ OMAP_DMA_AMODE_CONSTANT,
++ (OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1)
++ );
++ omap_set_dma_src_params (channel,
++ OMAP_DMA_PORT_EMIFF,
++ OMAP_DMA_AMODE_POST_INC,
++ runtime->dma_area + offset
++ );
++ omap_start_dma (channel);
++// omap_set_gpio_dataout (2, 0);
++#endif
++
++ s->period++;
++ s->period %= runtime->periods;
++// s->periods++;
++
++ s->offset = offset;
++
++ spin_unlock (&s->lock);
++ }
++
++ dprintk ("[%s] pass\n", __FUNCTION__);
++} /* tsc2101_playback_callback */
++
++static void
++tsc2101_capture_callback (int channel, u16 ch_status, void *data) {
++ dprintk ("[%s]\n", __FUNCTION__);
++ omap_start_dma (channel);
++ dprintk ("[%s] pass\n", __FUNCTION__);
++} /* tsc2101_capture_callback */
++
++static int
++__tsc2101_trigger (snd_pcm_substream_t *substream, int command) {
++ snd_card_tsc2101_t *chip = snd_pcm_substream_chip (substream);
++ audio_stream_t *s = &chip->s[substream->pstr->stream];
++ int err=0;
++
++ dprintk ("[%s]\n", __FUNCTION__);
++
++ switch (command) {
++ case SNDRV_PCM_TRIGGER_START:
++ dprintk ("[%s] command=start(%d)\n", __FUNCTION__, command);
++ s->active++;
++ omap_clear_dma (s->channel);
++ if (s->sid == SNDRV_PCM_STREAM_PLAYBACK)
++ tsc2101_playback_callback (s->channel, 32, (void *)s);
++ else
++ tsc2101_capture_callback (s->channel, 32, (void *)s);
++ break;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ dprintk ("[%s] command=stop(%d)\n", __FUNCTION__, command);
++ // TODO: be sure that all data has been xfered
++ s->active--;
++ spin_lock (&s->lock);
++ omap_clear_dma (s->channel);
++ omap_stop_dma (s->channel);
++ spin_unlock (&s->lock);
++ break;
++
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ dprintk ("[%s] command=suspend(%d)\n", __FUNCTION__, command);
++ case SNDRV_PCM_TRIGGER_RESUME:
++ dprintk ("[%s] command=resume(%d)\n", __FUNCTION__, command);
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ dprintk ("[%s] command=pause.push(%d)\n", __FUNCTION__, command);
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ dprintk ("[%s] command=pause.release(%d)\n", __FUNCTION__, command);
++ default:
++ err = -EINVAL;
++ break;
++ } /* switch (command) */
++
++ dprintk ("[%s] exit (%d)\n", __FUNCTION__, err);
++ return err;
++} /* __tsc2101_trigger */
++
++static snd_pcm_uframes_t
++__tsc2101_pointer (snd_pcm_substream_t *substream) {
++ snd_card_tsc2101_t *chip = snd_pcm_substream_chip (substream);
++ snd_pcm_runtime_t *runtime = substream->runtime;
++ audio_stream_t *s = &chip->s[substream->pstr->stream];
++ unsigned long flags;
++ snd_pcm_uframes_t offset;
++ dma_addr_t ptr;
++
++ dprintk ("[%s]\n", __FUNCTION__);
++
++ spin_lock_irqsave (&s->lock, flags);
++ ptr=omap_get_dma_src_pos (s->channel);
++ dprintk ("xxxx: [%08lx,%08lx] ptr=%08lx:%08lx\n",
++ (unsigned long)runtime->dma_area,
++ (unsigned long)runtime->dma_addr,
++ (unsigned long)ptr,
++ (unsigned long)runtime->buffer_size);
++ spin_unlock_irqrestore (&s->lock, flags);
++
++ offset=bytes_to_frames (runtime, ptr - runtime->dma_addr);
++ if ((offset >= runtime->buffer_size) || (offset < 0))
++ offset=0;
++ dprintk ("[%s] pass:%lu\n", __FUNCTION__, offset);
++ return offset;
++} /* __tsc2101_pointer */
++
++static snd_pcm_ops_t playback_ops = {
++ .open = __tsc2101_open,
++ .close = __tsc2101_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = __tsc2101_hw_params,
++ .hw_free = __tsc2101_hw_free,
++ .prepare = __tsc2101_prepare,
++ .trigger = __tsc2101_trigger,
++ .pointer = __tsc2101_pointer,
++};
++
++static snd_pcm_ops_t capture_ops = {
++ .open = __tsc2101_open,
++ .close = __tsc2101_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = __tsc2101_hw_params,
++ .hw_free = __tsc2101_hw_free,
++ .prepare = __tsc2101_prepare,
++ .trigger = __tsc2101_trigger,
++ .pointer = __tsc2101_pointer,
++};
++
++static void
++tsc2101_audio_init (snd_card_tsc2101_t *tsc2101) {
++ dprintk ("[%s]\n", __FUNCTION__);
++ omap_set_gpio_dataout (2, 0);
++ omap_set_gpio_direction (2, 0);
++
++ /* initialize mcbsp */
++ omap_mcbsp_request (AUDIO_MCBSP);
++ omap_mcbsp_stop (AUDIO_MCBSP);
++
++ omap_mcbsp_config (AUDIO_MCBSP, &mcbsp_cfg);
++ omap_mcbsp_start (AUDIO_MCBSP);
++
++ omap_tsc2101_enable ();
++#if 0
++ omap_tsc2101_write (
++ TSC2101_AUDIO_CODEC_REGISTERS,
++ TSC2101_CODEC_POWER_CTRL,
++ 0x0000);
++ omap_tsc2101_write (
++ TSC2101_AUDIO_CODEC_REGISTERS,
++ TSC2101_AUDIO_CTRL_5,
++ AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
++ AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
++ AC5_HDSCPTC);
++#else
++#ifdef DUMP_TSC2101_REGISTERS
++ dump_tsc2101_reg ();
++#endif /* DUMP_TSC2101_REGISTERS */
++
++ omap_tsc2101_write (2, TSC2101_CODEC_POWER_CTRL, 0x0000);
++
++ /*Mute Analog Sidetone */
++ /*Select MIC_INHED input for headset */
++ /*Cell Phone In not connected */
++ omap_tsc2101_write (2, TSC2101_MIXER_PGA_CTRL,
++ MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC);
++
++ /* Set record source */
++ omap_tsc2101_write (2, TSC2101_MIXER_PGA_CTRL, MPC_MICSEL(0));
++
++ /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
++ /* 1dB AGC hysteresis */
++ /* MICes bias 2V */
++ omap_tsc2101_write (2, TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
++
++ /* Set codec output volume */
++ omap_tsc2101_write (2, TSC2101_DAC_GAIN_CTRL, 0x0000);
++
++ /* DAC left and right routed to SPK2 */
++ /* SPK1/2 unmuted */
++ omap_tsc2101_write (2, TSC2101_AUDIO_CTRL_5,
++ AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
++ AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
++ AC5_HDSCPTC);
++
++ /* OUT8P/N muted, CPOUT muted */
++
++ omap_tsc2101_write (2, TSC2101_AUDIO_CTRL_6,
++ AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
++ AC6_VGNDSCPTC);
++
++ /* Headset/Hook switch detect disabled */
++ omap_tsc2101_write (2, TSC2101_AUDIO_CTRL_7, 0x0000);
++
++ /* mic input volume control */
++ omap_tsc2101_write(2, TSC2101_HANDSET_GAIN_CTRL, HNGC_ADPGA_HND(25));
++#endif
++
++#ifdef DUMP_TSC2101_REGISTERS
++ dump_tsc2101_reg ();
++#endif /* DUMP_TSC2101_REGISTERS */
++
++ __set_samplerate (tsc2101, tsc2101->samplerate);
++ dprintk ("[%s] pass\n", __FUNCTION__);
++} /* tsc2101_audio_init */
++
++int
++snd_tsc2101_setup_pcm (snd_card_tsc2101_t *tsc2101) {
++ snd_pcm_t *pcm=0;
++ int err=0;
++
++ dprintk ("[%s]\n", __FUNCTION__);
++ if (!tsc2101)
++ return -EINVAL;
++ if ((err=snd_pcm_new (tsc2101->card, "TSC2101 PCM", 0, 1, 1, &pcm)) < 0)
++ return err;
++
++ if ((err=snd_pcm_lib_preallocate_pages_for_all (pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data (GFP_KERNEL), 128, 128*1024)) < 0)
++ return err;
++
++ snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
++ snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
++
++ pcm->private_data=tsc2101;
++ pcm->info_flags = 0;
++ strncpy (pcm->name, "TSC2101 PCM", sizeof (pcm->name));
++
++ spin_lock_init (&tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].lock);
++ tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].id = "TSC2101 out";
++ tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].sid = SNDRV_PCM_STREAM_PLAYBACK;
++ tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].dev = AUDIO_DMA_TX;
++ if ((err=omap_request_dma (
++ tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].dev,
++ tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].id,
++ tsc2101_playback_callback,
++ (void *)&tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK],
++ &tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].channel)) < 0)
++ return err;
++ dprintk ("TX dev=%u channel=%u\n", tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].dev, tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].channel);
++
++ spin_lock_init (&tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].lock);
++ tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].id = "TSC2101 in";
++ tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].sid = SNDRV_PCM_STREAM_CAPTURE;
++ tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].dev = AUDIO_DMA_RX;
++ if ((err=omap_request_dma (
++ tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].dev,
++ tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].id,
++ tsc2101_capture_callback,
++ (void *)&tsc2101->s[SNDRV_PCM_STREAM_CAPTURE],
++ &tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].channel)) < 0)
++ return err;
++ dprintk ("RX dev=%u channel=%u\n", tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].dev, tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].channel);
++
++ tsc2101_audio_init (tsc2101);
++
++ tsc2101->pcm=pcm;
++ dprintk ("[%s] pass\n", __FUNCTION__);
++ return 0;
++} /* snd_tsc2101_setup_pcm */
++
++void
++snd_tsc2101_disable_pcm (snd_card_tsc2101_t *tsc2101) {
++ dprintk ("[%s]\n", __FUNCTION__);
++ omap_stop_dma (tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].channel);
++ omap_free_dma (tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].channel);
++ omap_stop_dma (tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].channel);
++ omap_free_dma (tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].channel);
++
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(2);
++
++ omap_mcbsp_stop (AUDIO_MCBSP);
++ omap_mcbsp_free (AUDIO_MCBSP);
++
++ omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS, TSC2101_CODEC_POWER_CTRL, ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
++ omap_tsc2101_disable ();
++ dprintk ("[%s] pass\n", __FUNCTION__);
++} /* snd_tsc2101_disable_pcm */
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap-alsa-dma.c bt_kernel/sound/arm/omap-alsa-dma.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/arm/omap-alsa-dma.c 2005-10-30 17:44:20.392618150 +0200
++++ bt_kernel/sound/arm/omap-alsa-dma.c 2005-10-30 16:32:39.609796000 +0200
+@@ -325,9 +325,11 @@
+ int cfn = dma_size / (2 * cen);
+ FN_IN;
+ omap_set_dma_dest_params(channel, 0x05, 0x00,
+- (OMAP1610_MCBSP1_BASE + 0x806));
+- omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr);
+- omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00);
++ (OMAP1610_MCBSP1_BASE + 0x806),
++ 0, 0);
++ omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr,
++ 0, 0);
++ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
+ FN_OUT(0);
+ return 0;
+ }
+@@ -341,9 +343,10 @@
+ int cfn = dma_size / (2 * cen);
+ FN_IN;
+ omap_set_dma_src_params(channel, 0x05, 0x00,
+- (OMAP1610_MCBSP1_BASE + 0x802));
+- omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr);
+- omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00);
++ (OMAP1610_MCBSP1_BASE + 0x802),
++ 0, 0);
++ omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
++ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
+ FN_OUT(0);
+ return 0;
+ }
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/oss/Kconfig bt_kernel/sound/oss/Kconfig
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/oss/Kconfig 2005-10-30 17:44:20.459607487 +0200
++++ bt_kernel/sound/oss/Kconfig 2005-10-22 03:52:45.687256000 +0300
+@@ -12,7 +12,7 @@
+
+ config SOUND_OMAP_TSC2101
+ tristate "TSC2101 Stereo Codec"
+- depends on SOUND_OMAP && ( MACH_OMAP_H2 || MACH_OMAP_H3 )
++ depends on SOUND_OMAP && ( MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H6300)
+ select OMAP_TSC2101
+ select OMAP_UWIRE if ARCH_OMAP
+ ---help---
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/oss/omap-audio-dma-intfc.c bt_kernel/sound/oss/omap-audio-dma-intfc.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/oss/omap-audio-dma-intfc.c 2005-10-30 17:44:20.656576134 +0200
++++ bt_kernel/sound/oss/omap-audio-dma-intfc.c 2005-10-30 16:32:39.609796000 +0200
+@@ -701,9 +701,10 @@
+ int cfn = dma_size / (2 * cen);
+ FN_IN;
+ omap_set_dma_dest_params(channel, 0x05, 0x00,
+- (OMAP1610_MCBSP1_BASE + 0x806));
+- omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr);
+- omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00);
++ (OMAP1610_MCBSP1_BASE + 0x806),
++ 0, 0);
++ omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
++ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
+ FN_OUT(0);
+ return 0;
+ }
+@@ -716,9 +717,10 @@
+ int cfn = dma_size / (2 * cen);
+ FN_IN;
+ omap_set_dma_src_params(channel, 0x05, 0x00,
+- (OMAP1610_MCBSP1_BASE + 0x802));
+- omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr);
+- omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00);
++ (OMAP1610_MCBSP1_BASE + 0x802),
++ 0, 0);
++ omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
++ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
+ FN_OUT(0);
+ return 0;
+ }
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/oss/omap-audio-tsc2101.c bt_kernel/sound/oss/omap-audio-tsc2101.c
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/sound/oss/omap-audio-tsc2101.c 2005-10-30 17:44:20.697569609 +0200
++++ bt_kernel/sound/oss/omap-audio-tsc2101.c 2005-10-22 03:52:45.687256000 +0300
+@@ -48,7 +48,7 @@
+ #include "omap-audio.h"
+ #include "omap-audio-dma-intfc.h"
+ #include <asm/arch/mcbsp.h>
+-#if CONFIG_ARCH_OMAP16XX
++#if defined (CONFIG_ARCH_OMAP16XX) || defined (CONFIG_MACH_OMAP_H6300)
+ #include <../drivers/ssi/omap-uwire.h>
+ #include <asm/arch/dsp_common.h>
+ #else
+@@ -72,6 +72,8 @@
+
+ #if CONFIG_ARCH_OMAP16XX
+ #define PLATFORM_NAME "OMAP16XX"
++#elif CONFIG_MACH_OMAP_H6300
++#define PLATFORM_NAME "OMAP15XX"
+ #endif
+
+ #if CONFIG_ARCH_OMAP16XX
+@@ -90,7 +92,7 @@
+ #define LEAVE_CS 0x80
+
+ /* Select the McBSP For Audio */
+-#if CONFIG_ARCH_OMAP16XX
++#if defined (CONFIG_ARCH_OMAP16XX) || defined(CONFIG_MACH_OMAP_H6300)
+ #define AUDIO_MCBSP OMAP_MCBSP1
+ #else
+ #error "UnSupported Configuration"
+@@ -124,7 +126,7 @@
+ /*********** Debug Macros ********/
+ /* To Generate a rather shrill tone -test the entire path */
+ //#define TONE_GEN
+-/* To Generate a tone for each keyclick - test the tsc,spi paths*/
++///* To Generate a tone for each keyclick - test the tsc,spi paths*/
+ //#define TEST_KEYCLICK
+ /* To dump the tsc registers for debug */
+ //#define TSC_DUMP_REGISTERS
+@@ -215,6 +217,17 @@
+ };
+
+ static struct omap_mcbsp_reg_cfg initial_config = {
++#ifdef CONFIG_MACH_OMAP_H6300
++ .spcr2 = 0x0005,
++ .spcr1 = 0x0005,
++ .rcr2 = 0x8041,
++ .rcr1 = 0x8041,
++ .xcr2 = 0x00a1,
++ .xcr1 = 0x00a1,
++ .srgr2 = 0xb000,
++ .srgr1 = 0xb000,
++ .pcr0 = 0x0081,
++#else
+ .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+ .spcr1 = RINTM(3) | RRST,
+ .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+@@ -238,6 +251,7 @@
+ #endif /* tsc Master defs */
+
+ #endif /* platform specific inits */
++#endif /* CONFIG_MACH_OMAP_H6300 */
+ };
+
+ /***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/
+diff -Naur /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/usr/.gitignore bt_kernel/usr/.gitignore
+--- /home/lamikr/own/h6340/kernel/linux-2.6.14-rc5/usr/.gitignore 2005-10-20 09:23:05.000000000 +0300
++++ bt_kernel/usr/.gitignore 1970-01-01 02:00:00.000000000 +0200
+@@ -1,7 +0,0 @@
+-#
+-# Generated files
+-#
+-gen_init_cpio
+-initramfs_data.cpio
+-initramfs_data.cpio.gz
+-initramfs_list
diff --git a/packages/linux/linux-h6300-omap1_2.6.14-rc5.bb b/packages/linux/linux-h6300-omap1_2.6.14-rc5.bb
new file mode 100644
index 0000000000..5053f95c27
--- /dev/null
+++ b/packages/linux/linux-h6300-omap1_2.6.14-rc5.bb
@@ -0,0 +1,18 @@
+DESCRIPTION = "Linux kernel for h6300 devices."
+MAINTAINER = "Mika Laitio <lamikr@cc.jyu.fi>"
+SECTION = "kernel"
+LICENSE = "GPL"
+
+SRC_URI = "http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.14-rc5.tar.bz2 \
+ http://www.muru.com/linux/omap/patches/patch-2.6.14-rc5-omap1.bz2;patch=1 \
+ file://h6300_omap1_2614rc5.patch;patch=1 \
+ file://defconfig"
+
+S = "${WORKDIR}/linux-2.6.14-rc5"
+
+inherit kernel
+
+do_configure_prepend() {
+ install -m 0644 ${WORKDIR}/defconfig ${S}/.config
+ oe_runmake oldconfig
+}