diff options
Diffstat (limited to 'recipes/linux/linux-h6300-omap1-2.6.14.3/patch-linux-2614-omap2-to-2614_3-omap1-h6300')
-rw-r--r-- | recipes/linux/linux-h6300-omap1-2.6.14.3/patch-linux-2614-omap2-to-2614_3-omap1-h6300 | 32978 |
1 files changed, 32978 insertions, 0 deletions
diff --git a/recipes/linux/linux-h6300-omap1-2.6.14.3/patch-linux-2614-omap2-to-2614_3-omap1-h6300 b/recipes/linux/linux-h6300-omap1-2.6.14.3/patch-linux-2614-omap2-to-2614_3-omap1-h6300 new file mode 100644 index 0000000000..e7e442e7c2 --- /dev/null +++ b/recipes/linux/linux-h6300-omap1-2.6.14.3/patch-linux-2614-omap2-to-2614_3-omap1-h6300 @@ -0,0 +1,32978 @@ +diff -Naur linux-2.6.14-omap2/arch/arm/configs/omap_h6300_defconfig linux-h6300-omap2-2.6.14.3/arch/arm/configs/omap_h6300_defconfig +--- linux-2.6.14-omap2/arch/arm/configs/omap_h6300_defconfig 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/configs/omap_h6300_defconfig 2005-11-11 04:13:42.000000000 +0200 +@@ -0,0 +1,1538 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.14-omap1-h6300 ++# Thu Nov 10 05:21:43 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 -Naur linux-2.6.14-omap2/arch/arm/Kconfig linux-h6300-omap2-2.6.14.3/arch/arm/Kconfig +--- linux-2.6.14-omap2/arch/arm/Kconfig 2005-12-02 01:53:31.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/Kconfig 2005-10-14 18:55:31.000000000 +0300 +@@ -725,6 +725,8 @@ + + source "drivers/video/Kconfig" + ++source "drivers/telephony/Kconfig" ++ + source "sound/Kconfig" + + source "drivers/usb/Kconfig" +diff -Naur linux-2.6.14-omap2/arch/arm/mach-omap1/board-h6300.c linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/board-h6300.c +--- linux-2.6.14-omap2/arch/arm/mach-omap1/board-h6300.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/board-h6300.c 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/arch/arm/mach-omap1/Kconfig linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/Kconfig +--- linux-2.6.14-omap2/arch/arm/mach-omap1/Kconfig 2005-12-02 01:53:31.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/Kconfig 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/arch/arm/mach-omap1/Makefile linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/Makefile +--- linux-2.6.14-omap2/arch/arm/mach-omap1/Makefile 2005-12-02 01:53:31.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/Makefile 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/arch/arm/mach-omap1/mux.c linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/mux.c +--- linux-2.6.14-omap2/arch/arm/mach-omap1/mux.c 2005-12-02 01:53:32.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/mach-omap1/mux.c 2005-11-11 04:13:42.000000000 +0200 +@@ -196,6 +196,13 @@ + MUX_CFG("P15_1610_UWIRE_CS3", 8, 12, 1, 1, 22, 0, 1, 1, 1) + MUX_CFG("N15_1610_UWIRE_CS1", 7, 18, 2, 1, 14, 0, NA, 0, 1) + ++/* OMAP-1510 uWire */ ++MUX_CFG("P15_1510_UWIRE_CS3", 8, 12, 1, NA, 0, 0, NA, 0, 1) ++MUX_CFG("N14_1510_UWIRE_CS0", 8, 9, 1, NA, 0, 0, NA, 0, 1) ++MUX_CFG("V19_1510_UWIRE_SCLK", 8, 6, 0, NA, 0, 0, NA, 0, 1) ++MUX_CFG("W21_1510_UWIRE_SDO", 8, 3, 0, NA, 0, 0, NA, 0, 1) ++MUX_CFG("U18_1510_UWIRE_SDI", 8, 0, 0, 1, 18, 0, NA, 0, 1) ++ + /* OMAP-1610 Flash */ + MUX_CFG("L3_1610_FLASH_CS2B_OE",10, 6, 1, NA, 0, 0, NA, 0, 1) + MUX_CFG("M8_1610_FLASH_CS2B_WE",10, 3, 1, NA, 0, 0, NA, 0, 1) +@@ -258,6 +265,7 @@ + MUX_CFG("T20_1610_LOW_PWR", 7, 12, 1, NA, 0, 0, NA, 0, 0) + + /* MCLK Settings */ ++MUX_CFG("R10_1510_MCLK_ON", B, 18, 0, 2, 22, 1, NA, 1, 1) + MUX_CFG("V5_1710_MCLK_ON", B, 15, 0, NA, 0, 0, NA, 0, 0) + MUX_CFG("V5_1710_MCLK_OFF", B, 15, 6, NA, 0, 0, NA, 0, 0) + MUX_CFG("R10_1610_MCLK_ON", B, 18, 0, NA, 22, 0, NA, 1, 0) +diff -Naur linux-2.6.14-omap2/arch/arm/plat-omap/dma.c linux-h6300-omap2-2.6.14.3/arch/arm/plat-omap/dma.c +--- linux-2.6.14-omap2/arch/arm/plat-omap/dma.c 2005-12-02 01:53:32.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/plat-omap/dma.c 2005-11-11 04:13:42.000000000 +0200 +@@ -30,6 +30,7 @@ + #include <asm/hardware.h> + #include <asm/dma.h> + #include <asm/io.h> ++#include <asm/mach-types.h> + + #include <asm/arch/tc.h> + +@@ -1086,6 +1087,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 linux-2.6.14-omap2/arch/arm/tools/mach-types linux-h6300-omap2-2.6.14.3/arch/arm/tools/mach-types +--- linux-2.6.14-omap2/arch/arm/tools/mach-types 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/arch/arm/tools/mach-types 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/arch/i386/kernel/cpu/mtrr/main.c linux-h6300-omap2-2.6.14.3/arch/i386/kernel/cpu/mtrr/main.c +--- linux-2.6.14-omap2/arch/i386/kernel/cpu/mtrr/main.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/arch/i386/kernel/cpu/mtrr/main.c 2005-12-02 01:34:34.000000000 +0200 +@@ -626,6 +626,14 @@ + if (cpuid_eax(0x80000000) >= 0x80000008) { + u32 phys_addr; + phys_addr = cpuid_eax(0x80000008) & 0xff; ++ /* CPUID workaround for Intel 0F33/0F34 CPU */ ++ if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && ++ boot_cpu_data.x86 == 0xF && ++ boot_cpu_data.x86_model == 0x3 && ++ (boot_cpu_data.x86_mask == 0x3 || ++ boot_cpu_data.x86_mask == 0x4)) ++ phys_addr = 36; ++ + size_or_mask = ~((1 << (phys_addr - PAGE_SHIFT)) - 1); + size_and_mask = ~size_or_mask & 0xfff00000; + } else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR && +diff -Naur linux-2.6.14-omap2/arch/ppc64/Kconfig linux-h6300-omap2-2.6.14.3/arch/ppc64/Kconfig +--- linux-2.6.14-omap2/arch/ppc64/Kconfig 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/arch/ppc64/Kconfig 2005-12-02 01:34:35.000000000 +0200 +@@ -234,6 +234,10 @@ + This option enables hardware multithreading on RS64 cpus. + pSeries systems p620 and p660 have such a cpu type. + ++config NUMA ++ bool "NUMA support" ++ default y if DISCONTIGMEM || SPARSEMEM ++ + config ARCH_SELECT_MEMORY_MODEL + def_bool y + +@@ -249,9 +253,6 @@ + def_bool y + depends on ARCH_DISCONTIGMEM_ENABLE + +-config ARCH_FLATMEM_ENABLE +- def_bool y +- + config ARCH_SPARSEMEM_ENABLE + def_bool y + depends on ARCH_DISCONTIGMEM_ENABLE +@@ -274,10 +275,6 @@ + def_bool y + depends on NEED_MULTIPLE_NODES + +-config NUMA +- bool "NUMA support" +- default y if DISCONTIGMEM || SPARSEMEM +- + config SCHED_SMT + bool "SMT (Hyperthreading) scheduler support" + depends on SMP +diff -Naur linux-2.6.14-omap2/arch/s390/appldata/appldata_base.c linux-h6300-omap2-2.6.14.3/arch/s390/appldata/appldata_base.c +--- linux-2.6.14-omap2/arch/s390/appldata/appldata_base.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/arch/s390/appldata/appldata_base.c 2005-11-23 01:44:02.000000000 +0200 +@@ -592,12 +592,15 @@ + */ + void appldata_unregister_ops(struct appldata_ops *ops) + { ++ void *table; + spin_lock(&appldata_ops_lock); +- unregister_sysctl_table(ops->sysctl_header); + list_del(&ops->list); +- kfree(ops->ctl_table); ++ /* at that point any incoming access will fail */ ++ table = ops->ctl_table; + ops->ctl_table = NULL; + spin_unlock(&appldata_ops_lock); ++ unregister_sysctl_table(ops->sysctl_header); ++ kfree(table); + P_INFO("%s-ops unregistered!\n", ops->name); + } + /********************** module-ops management <END> **************************/ +diff -Naur linux-2.6.14-omap2/arch/x86_64/kernel/setup.c linux-h6300-omap2-2.6.14.3/arch/x86_64/kernel/setup.c +--- linux-2.6.14-omap2/arch/x86_64/kernel/setup.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/arch/x86_64/kernel/setup.c 2005-12-02 01:34:35.000000000 +0200 +@@ -993,6 +993,11 @@ + unsigned eax = cpuid_eax(0x80000008); + c->x86_virt_bits = (eax >> 8) & 0xff; + c->x86_phys_bits = eax & 0xff; ++ /* CPUID workaround for Intel 0F34 CPU */ ++ if (c->x86_vendor == X86_VENDOR_INTEL && ++ c->x86 == 0xF && c->x86_model == 0x3 && ++ c->x86_mask == 0x4) ++ c->x86_phys_bits = 36; + } + + if (c->x86 == 15) +diff -Naur linux-2.6.14-omap2/drivers/block/cfq-iosched.c linux-h6300-omap2-2.6.14.3/drivers/block/cfq-iosched.c +--- linux-2.6.14-omap2/drivers/block/cfq-iosched.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/block/cfq-iosched.c 2005-11-23 01:44:02.000000000 +0200 +@@ -2260,10 +2260,8 @@ + if (!atomic_dec_and_test(&cfqd->ref)) + return; + +- blk_put_queue(q); +- + cfq_shutdown_timer_wq(cfqd); +- q->elevator->elevator_data = NULL; ++ blk_put_queue(q); + + mempool_destroy(cfqd->crq_pool); + kfree(cfqd->crq_hash); +diff -Naur linux-2.6.14-omap2/drivers/block/pktcdvd.c linux-h6300-omap2-2.6.14.3/drivers/block/pktcdvd.c +--- linux-2.6.14-omap2/drivers/block/pktcdvd.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/block/pktcdvd.c 2005-12-02 01:34:35.000000000 +0200 +@@ -1191,7 +1191,7 @@ + struct packet_data *pkt; + int i; + +- for (i = 0; i <= PACKET_NUM_STATES; i++) ++ for (i = 0; i < PACKET_NUM_STATES; i++) + states[i] = 0; + + spin_lock(&pd->cdrw.active_list_lock); +diff -Naur linux-2.6.14-omap2/drivers/bluetooth/Kconfig linux-h6300-omap2-2.6.14.3/drivers/bluetooth/Kconfig +--- linux-2.6.14-omap2/drivers/bluetooth/Kconfig 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/bluetooth/Kconfig 2005-10-04 00:58:34.000000000 +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 linux-2.6.14-omap2/drivers/bluetooth/Makefile linux-h6300-omap2-2.6.14.3/drivers/bluetooth/Makefile +--- linux-2.6.14-omap2/drivers/bluetooth/Makefile 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/bluetooth/Makefile 2005-09-28 01:04:13.000000000 +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 linux-2.6.14-omap2/drivers/bluetooth/omap/h6300_bt_brf6100.c linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/h6300_bt_brf6100.c +--- linux-2.6.14-omap2/drivers/bluetooth/omap/h6300_bt_brf6100.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/h6300_bt_brf6100.c 2005-12-02 00:45:30.000000000 +0200 +@@ -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, 1); // turn off gpio, note 1 == off for negative gpios ++ mdelay(5); ++ h6300_clear_led(INDEX_BT_LED); ++ } ++ else if (enable == 1) { ++ omap_set_gpio_dataout(GPIO_N_BT_RST, 1); // turn on gpio, note 0 == on for negative gpios ++ 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, 0); // turn on gpio, note 0 == on for negative gpios ++ ++ /* 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 linux-2.6.14-omap2/drivers/bluetooth/omap/h6300_bt_led.c linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/h6300_bt_led.c +--- linux-2.6.14-omap2/drivers/bluetooth/omap/h6300_bt_led.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/h6300_bt_led.c 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/bluetooth/omap/h6300_bt_led.h linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/h6300_bt_led.h +--- linux-2.6.14-omap2/drivers/bluetooth/omap/h6300_bt_led.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/h6300_bt_led.h 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/bluetooth/omap/Makefile linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/Makefile +--- linux-2.6.14-omap2/drivers/bluetooth/omap/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/bluetooth/omap/Makefile 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/char/rtc.c linux-h6300-omap2-2.6.14.3/drivers/char/rtc.c +--- linux-2.6.14-omap2/drivers/char/rtc.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/char/rtc.c 2005-12-02 01:34:35.000000000 +0200 +@@ -149,8 +149,22 @@ + #ifdef RTC_IRQ + static void rtc_dropped_irq(unsigned long data); + +-static void set_rtc_irq_bit(unsigned char bit); +-static void mask_rtc_irq_bit(unsigned char bit); ++static void set_rtc_irq_bit_locked(unsigned char bit); ++static void mask_rtc_irq_bit_locked(unsigned char bit); ++ ++static inline void set_rtc_irq_bit(unsigned char bit) ++{ ++ spin_lock_irq(&rtc_lock); ++ set_rtc_irq_bit_locked(bit); ++ spin_unlock_irq(&rtc_lock); ++} ++ ++static void mask_rtc_irq_bit(unsigned char bit) ++{ ++ spin_lock_irq(&rtc_lock); ++ mask_rtc_irq_bit_locked(bit); ++ spin_unlock_irq(&rtc_lock); ++} + #endif + + static int rtc_proc_open(struct inode *inode, struct file *file); +@@ -401,18 +415,19 @@ + } + case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ + { +- mask_rtc_irq_bit(RTC_PIE); ++ unsigned long flags; /* can be called from isr via rtc_control() */ ++ spin_lock_irqsave (&rtc_lock, flags); ++ mask_rtc_irq_bit_locked(RTC_PIE); + if (rtc_status & RTC_TIMER_ON) { +- spin_lock_irq (&rtc_lock); + rtc_status &= ~RTC_TIMER_ON; + del_timer(&rtc_irq_timer); +- spin_unlock_irq (&rtc_lock); + } ++ spin_unlock_irqrestore (&rtc_lock, flags); + return 0; + } + case RTC_PIE_ON: /* Allow periodic ints */ + { +- ++ unsigned long flags; /* can be called from isr via rtc_control() */ + /* + * We don't really want Joe User enabling more + * than 64Hz of interrupts on a multi-user machine. +@@ -421,14 +436,14 @@ + (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + ++ spin_lock_irqsave (&rtc_lock, flags); + if (!(rtc_status & RTC_TIMER_ON)) { +- spin_lock_irq (&rtc_lock); + rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; + add_timer(&rtc_irq_timer); + rtc_status |= RTC_TIMER_ON; +- spin_unlock_irq (&rtc_lock); + } +- set_rtc_irq_bit(RTC_PIE); ++ set_rtc_irq_bit_locked(RTC_PIE); ++ spin_unlock_irqrestore (&rtc_lock, flags); + return 0; + } + case RTC_UIE_OFF: /* Mask ints from RTC updates. */ +@@ -609,6 +624,7 @@ + { + int tmp = 0; + unsigned char val; ++ unsigned long flags; /* can be called from isr via rtc_control() */ + + /* + * The max we can do is 8192Hz. +@@ -631,9 +647,9 @@ + if (arg != (1<<tmp)) + return -EINVAL; + +- spin_lock_irq(&rtc_lock); ++ spin_lock_irqsave(&rtc_lock, flags); + if (hpet_set_periodic_freq(arg)) { +- spin_unlock_irq(&rtc_lock); ++ spin_unlock_irqrestore(&rtc_lock, flags); + return 0; + } + rtc_freq = arg; +@@ -641,7 +657,7 @@ + val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0; + val |= (16 - tmp); + CMOS_WRITE(val, RTC_FREQ_SELECT); +- spin_unlock_irq(&rtc_lock); ++ spin_unlock_irqrestore(&rtc_lock, flags); + return 0; + } + #endif +@@ -844,12 +860,15 @@ + #ifndef RTC_IRQ + return -EIO; + #else +- spin_lock_irq(&rtc_task_lock); ++ unsigned long flags; ++ if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET) ++ return -EINVAL; ++ spin_lock_irqsave(&rtc_task_lock, flags); + if (rtc_callback != task) { +- spin_unlock_irq(&rtc_task_lock); ++ spin_unlock_irqrestore(&rtc_task_lock, flags); + return -ENXIO; + } +- spin_unlock_irq(&rtc_task_lock); ++ spin_unlock_irqrestore(&rtc_task_lock, flags); + return rtc_do_ioctl(cmd, arg, 1); + #endif + } +@@ -1306,40 +1325,32 @@ + * meddles with the interrupt enable/disable bits. + */ + +-static void mask_rtc_irq_bit(unsigned char bit) ++static void mask_rtc_irq_bit_locked(unsigned char bit) + { + unsigned char val; + +- spin_lock_irq(&rtc_lock); +- if (hpet_mask_rtc_irq_bit(bit)) { +- spin_unlock_irq(&rtc_lock); ++ if (hpet_mask_rtc_irq_bit(bit)) + return; +- } + val = CMOS_READ(RTC_CONTROL); + val &= ~bit; + CMOS_WRITE(val, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + + rtc_irq_data = 0; +- spin_unlock_irq(&rtc_lock); + } + +-static void set_rtc_irq_bit(unsigned char bit) ++static void set_rtc_irq_bit_locked(unsigned char bit) + { + unsigned char val; + +- spin_lock_irq(&rtc_lock); +- if (hpet_set_rtc_irq_bit(bit)) { +- spin_unlock_irq(&rtc_lock); ++ if (hpet_set_rtc_irq_bit(bit)) + return; +- } + val = CMOS_READ(RTC_CONTROL); + val |= bit; + CMOS_WRITE(val, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + + rtc_irq_data = 0; +- spin_unlock_irq(&rtc_lock); + } + #endif + +diff -Naur linux-2.6.14-omap2/drivers/hwmon/it87.c linux-h6300-omap2-2.6.14.3/drivers/hwmon/it87.c +--- linux-2.6.14-omap2/drivers/hwmon/it87.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/hwmon/it87.c 2005-12-02 01:34:35.000000000 +0200 +@@ -522,8 +522,15 @@ + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); ++ u8 reg = it87_read_value(client, IT87_REG_FAN_DIV); + + down(&data->update_lock); ++ switch (nr) { ++ case 0: data->fan_div[nr] = reg & 0x07; break; ++ case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break; ++ case 2: data->fan_div[nr] = (reg & 0x40) ? 3 : 1; break; ++ } ++ + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); +diff -Naur linux-2.6.14-omap2/drivers/hwmon/lm78.c linux-h6300-omap2-2.6.14.3/drivers/hwmon/lm78.c +--- linux-2.6.14-omap2/drivers/hwmon/lm78.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/hwmon/lm78.c 2005-12-02 01:34:35.000000000 +0200 +@@ -451,7 +451,7 @@ + static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) + { + struct lm78_data *data = lm78_update_device(dev); +- return sprintf(buf, "%d\n", vid_from_reg(82, data->vid)); ++ return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82)); + } + static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +diff -Naur linux-2.6.14-omap2/drivers/hwmon/w83627hf.c linux-h6300-omap2-2.6.14.3/drivers/hwmon/w83627hf.c +--- linux-2.6.14-omap2/drivers/hwmon/w83627hf.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/hwmon/w83627hf.c 2005-12-02 01:34:35.000000000 +0200 +@@ -454,7 +454,9 @@ + (w83627thf == data->type || w83637hf == data->type)) + + /* use VRM9 calculation */ +- data->in_min[0] = (u8)(((val * 100) - 70000 + 244) / 488); ++ data->in_min[0] = ++ SENSORS_LIMIT(((val * 100) - 70000 + 244) / 488, 0, ++ 255); + else + /* use VRM8 (standard) calculation */ + data->in_min[0] = IN_TO_REG(val); +@@ -479,7 +481,9 @@ + (w83627thf == data->type || w83637hf == data->type)) + + /* use VRM9 calculation */ +- data->in_max[0] = (u8)(((val * 100) - 70000 + 244) / 488); ++ data->in_max[0] = ++ SENSORS_LIMIT(((val * 100) - 70000 + 244) / 488, 0, ++ 255); + else + /* use VRM8 (standard) calculation */ + data->in_max[0] = IN_TO_REG(val); +diff -Naur linux-2.6.14-omap2/drivers/i2c/busses/i2c-omap.c linux-h6300-omap2-2.6.14.3/drivers/i2c/busses/i2c-omap.c +--- linux-2.6.14-omap2/drivers/i2c/busses/i2c-omap.c 2005-12-02 01:53:32.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/i2c/busses/i2c-omap.c 2005-10-20 20:53:33.000000000 +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 linux-2.6.14-omap2/drivers/i2c/chips/Kconfig linux-h6300-omap2-2.6.14.3/drivers/i2c/chips/Kconfig +--- linux-2.6.14-omap2/drivers/i2c/chips/Kconfig 2005-12-02 01:53:32.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/i2c/chips/Kconfig 2005-10-14 18:55:31.000000000 +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 linux-2.6.14-omap2/drivers/i2c/chips/Makefile linux-h6300-omap2-2.6.14.3/drivers/i2c/chips/Makefile +--- linux-2.6.14-omap2/drivers/i2c/chips/Makefile 2005-12-02 01:53:32.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/i2c/chips/Makefile 2005-10-14 18:55:31.000000000 +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 linux-2.6.14-omap2/drivers/i2c/chips/pca9535.c linux-h6300-omap2-2.6.14.3/drivers/i2c/chips/pca9535.c +--- linux-2.6.14-omap2/drivers/i2c/chips/pca9535.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/i2c/chips/pca9535.c 2005-10-25 22:23:21.000000000 +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 linux-2.6.14-omap2/drivers/input/keyboard/omap-keypad.c linux-h6300-omap2-2.6.14.3/drivers/input/keyboard/omap-keypad.c +--- linux-2.6.14-omap2/drivers/input/keyboard/omap-keypad.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/input/keyboard/omap-keypad.c 2005-11-04 03:06:04.000000000 +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,26 +260,34 @@ + 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]; + if (changed == 0) + continue; +- ++ + for (row = 0; row < 8; row++) { + int key; + if (!(changed & (1 << row))) +- continue; ++ continue; + #ifdef NEW_BOARD_LEARNING_MODE + printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col, row, (new_state[col] & (1 << row)) ? "pressed" : "released"); + #else +@@ -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; + } + +@@ -305,7 +528,7 @@ + omap_kp_dev.name = "omap-keypad"; + input_register_device(&omap_kp_dev); + +- if (machine_is_omap_h2() || machine_is_omap_h3()) { ++ if (machine_is_omap_h2() || machine_is_omap_h3() || machine_is_h6300()) { + omap_cfg_reg(F18_1610_KBC0); + omap_cfg_reg(D20_1610_KBC1); + omap_cfg_reg(D19_1610_KBC2); +@@ -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 linux-2.6.14-omap2/drivers/input/touchscreen/omap/Makefile linux-h6300-omap2-2.6.14.3/drivers/input/touchscreen/omap/Makefile +--- linux-2.6.14-omap2/drivers/input/touchscreen/omap/Makefile 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/input/touchscreen/omap/Makefile 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/drivers/input/touchscreen/omap/omap_ts.c linux-h6300-omap2-2.6.14.3/drivers/input/touchscreen/omap/omap_ts.c +--- linux-2.6.14-omap2/drivers/input/touchscreen/omap/omap_ts.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/input/touchscreen/omap/omap_ts.c 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/drivers/input/touchscreen/omap/ts_hx.c linux-h6300-omap2-2.6.14.3/drivers/input/touchscreen/omap/ts_hx.c +--- linux-2.6.14-omap2/drivers/input/touchscreen/omap/ts_hx.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/input/touchscreen/omap/ts_hx.c 2005-09-28 02:45:59.000000000 +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 linux-2.6.14-omap2/drivers/isdn/hardware/eicon/os_4bri.c linux-h6300-omap2-2.6.14.3/drivers/isdn/hardware/eicon/os_4bri.c +--- linux-2.6.14-omap2/drivers/isdn/hardware/eicon/os_4bri.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/isdn/hardware/eicon/os_4bri.c 2005-12-02 01:34:35.000000000 +0200 +@@ -16,6 +16,7 @@ + #include "diva_pci.h" + #include "mi_pc.h" + #include "dsrv4bri.h" ++#include "helpers.h" + + static void *diva_xdiLoadFileFile = NULL; + static dword diva_xdiLoadFileLength = 0; +@@ -815,7 +816,7 @@ + return (ret); + } + +-void *xdiLoadFile(char *FileName, unsigned long *FileLength, ++void *xdiLoadFile(char *FileName, dword *FileLength, + unsigned long lim) + { + void *ret = diva_xdiLoadFileFile; +diff -Naur linux-2.6.14-omap2/drivers/mmc/mmc.c linux-h6300-omap2-2.6.14.3/drivers/mmc/mmc.c +--- linux-2.6.14-omap2/drivers/mmc/mmc.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/mmc/mmc.c 2005-11-05 00:46:08.000000000 +0200 +@@ -495,6 +495,7 @@ + + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ ++ case 4: /* MMC v4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); +diff -Naur linux-2.6.14-omap2/drivers/net/wan/hdlc_cisco.c linux-h6300-omap2-2.6.14.3/drivers/net/wan/hdlc_cisco.c +--- linux-2.6.14-omap2/drivers/net/wan/hdlc_cisco.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wan/hdlc_cisco.c 2005-12-02 01:34:35.000000000 +0200 +@@ -192,7 +192,9 @@ + "uptime %ud%uh%um%us)\n", + dev->name, days, hrs, + min, sec); ++#if 0 + netif_carrier_on(dev); ++#endif + hdlc->state.cisco.up = 1; + } + } +@@ -225,7 +227,9 @@ + hdlc->state.cisco.settings.timeout * HZ)) { + hdlc->state.cisco.up = 0; + printk(KERN_INFO "%s: Link down\n", dev->name); ++#if 0 + netif_carrier_off(dev); ++#endif + } + + cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, +@@ -261,8 +265,10 @@ + { + hdlc_device *hdlc = dev_to_hdlc(dev); + del_timer_sync(&hdlc->state.cisco.timer); ++#if 0 + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); ++#endif + hdlc->state.cisco.up = 0; + hdlc->state.cisco.request_sent = 0; + } +diff -Naur linux-2.6.14-omap2/drivers/net/wan/hdlc_fr.c linux-h6300-omap2-2.6.14.3/drivers/net/wan/hdlc_fr.c +--- linux-2.6.14-omap2/drivers/net/wan/hdlc_fr.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wan/hdlc_fr.c 2005-12-02 01:34:35.000000000 +0200 +@@ -545,8 +545,10 @@ + + hdlc->state.fr.reliable = reliable; + if (reliable) { ++#if 0 + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); ++#endif + + hdlc->state.fr.n391cnt = 0; /* Request full status */ + hdlc->state.fr.dce_changed = 1; +@@ -560,8 +562,10 @@ + } + } + } else { ++#if 0 + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); ++#endif + + while (pvc) { /* Deactivate all PVCs */ + pvc_carrier(0, pvc); +diff -Naur linux-2.6.14-omap2/drivers/net/wan/hdlc_generic.c linux-h6300-omap2-2.6.14.3/drivers/net/wan/hdlc_generic.c +--- linux-2.6.14-omap2/drivers/net/wan/hdlc_generic.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wan/hdlc_generic.c 2005-12-02 01:34:35.000000000 +0200 +@@ -79,11 +79,13 @@ + hdlc_device *hdlc = dev_to_hdlc(dev); + if (hdlc->proto.start) + return hdlc->proto.start(dev); ++#if 0 + #ifdef DEBUG_LINK + if (netif_carrier_ok(dev)) + printk(KERN_ERR "hdlc_set_carrier_on(): already on\n"); + #endif + netif_carrier_on(dev); ++#endif + } + + +@@ -94,11 +96,13 @@ + if (hdlc->proto.stop) + return hdlc->proto.stop(dev); + ++#if 0 + #ifdef DEBUG_LINK + if (!netif_carrier_ok(dev)) + printk(KERN_ERR "hdlc_set_carrier_off(): already off\n"); + #endif + netif_carrier_off(dev); ++#endif + } + + +@@ -294,8 +298,10 @@ + if (result != 0) + return -EIO; + ++#if 0 + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); /* no carrier until DCD goes up */ ++#endif + + return 0; + } +diff -Naur linux-2.6.14-omap2/drivers/net/wireless/airo.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/airo.c +--- linux-2.6.14-omap2/drivers/net/wireless/airo.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/airo.c 2005-11-23 01:44:02.000000000 +0200 +@@ -46,6 +46,8 @@ + #include <linux/pci.h> + #include <asm/uaccess.h> + ++#include "airo.h" ++ + #ifdef CONFIG_PCI + static struct pci_device_id card_ids[] = { + { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, +diff -Naur linux-2.6.14-omap2/drivers/net/wireless/airo_cs.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/airo_cs.c +--- linux-2.6.14-omap2/drivers/net/wireless/airo_cs.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/airo_cs.c 2005-11-23 01:44:02.000000000 +0200 +@@ -42,6 +42,8 @@ + #include <asm/io.h> + #include <asm/system.h> + ++#include "airo.h" ++ + /* + All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + you do not define PCMCIA_DEBUG at all, all the debug code will be +@@ -78,10 +80,6 @@ + event handler. + */ + +-struct net_device *init_airo_card( int, int, int, struct device * ); +-void stop_airo_card( struct net_device *, int ); +-int reset_airo_card( struct net_device * ); +- + static void airo_config(dev_link_t *link); + static void airo_release(dev_link_t *link); + static int airo_event(event_t event, int priority, +diff -Naur linux-2.6.14-omap2/drivers/net/wireless/airo.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/airo.h +--- linux-2.6.14-omap2/drivers/net/wireless/airo.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/airo.h 2005-11-23 01:44:02.000000000 +0200 +@@ -0,0 +1,9 @@ ++#ifndef _AIRO_H_ ++#define _AIRO_H_ ++ ++struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, ++ struct device *dmdev); ++int reset_airo_card(struct net_device *dev); ++void stop_airo_card(struct net_device *dev, int freeres); ++ ++#endif /* _AIRO_H_ */ +diff -Naur linux-2.6.14-omap2/drivers/net/wireless/Kconfig linux-h6300-omap2-2.6.14.3/drivers/net/wireless/Kconfig +--- linux-2.6.14-omap2/drivers/net/wireless/Kconfig 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/Kconfig 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/Makefile linux-h6300-omap2-2.6.14.3/drivers/net/wireless/Makefile +--- linux-2.6.14-omap2/drivers/net/wireless/Makefile 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/Makefile 2005-10-14 18:55:31.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/prism54/islpci_eth.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/prism54/islpci_eth.c +--- linux-2.6.14-omap2/drivers/net/wireless/prism54/islpci_eth.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/prism54/islpci_eth.c 2005-11-23 01:44:02.000000000 +0200 +@@ -97,12 +97,6 @@ + /* lock the driver code */ + spin_lock_irqsave(&priv->slock, flags); + +- /* determine the amount of fragments needed to store the frame */ +- +- frame_size = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; +- if (init_wds) +- frame_size += 6; +- + /* check whether the destination queue has enough fragments for the frame */ + curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]); + if (unlikely(curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE)) { +@@ -213,6 +207,7 @@ + /* store the skb address for future freeing */ + priv->data_low_tx[index] = skb; + /* set the proper fragment start address and size information */ ++ frame_size = skb->len; + fragment->size = cpu_to_le16(frame_size); + fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */ + fragment->address = cpu_to_le32(pci_map_address); +diff -Naur linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx_config.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx_config.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx_config.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx_config.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx_func.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx_func.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx_func.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx_func.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx_struct.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx_struct.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/acx_struct.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/acx_struct.h 2005-10-29 22:02:44.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/cfi.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/cfi.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/cfi.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/cfi.c 2005-10-29 22:02:44.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/common.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/common.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/common.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/common.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/conv.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/conv.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/conv.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/conv.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/ioctl.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/ioctl.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/ioctl.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/ioctl.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/Kconfig linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/Kconfig +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/Kconfig 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/Kconfig 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/macros.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/macros.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/macros.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/macros.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/Makefile linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/Makefile +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/Makefile 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/pci.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/pci.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/pci.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/pci.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/setrate.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/setrate.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/setrate.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/setrate.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/usb.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/usb.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/usb.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/usb.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan.c linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan.c +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan.c 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan_compat.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan_compat.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan_compat.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan_compat.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan_hdr.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan_hdr.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan_hdr.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan_hdr.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan_mgmt.h linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan_mgmt.h +--- linux-2.6.14-omap2/drivers/net/wireless/tiacx/wlan_mgmt.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/net/wireless/tiacx/wlan_mgmt.h 2005-09-28 23:54:23.000000000 +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 linux-2.6.14-omap2/drivers/ssi/omap-tsc2101.c linux-h6300-omap2-2.6.14.3/drivers/ssi/omap-tsc2101.c +--- linux-2.6.14-omap2/drivers/ssi/omap-tsc2101.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/ssi/omap-tsc2101.c 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/drivers/ssi/omap-uwire.c linux-h6300-omap2-2.6.14.3/drivers/ssi/omap-uwire.c +--- linux-2.6.14-omap2/drivers/ssi/omap-uwire.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/ssi/omap-uwire.c 2005-08-12 13:46:22.000000000 +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 linux-2.6.14-omap2/drivers/telephony/Kconfig linux-h6300-omap2-2.6.14.3/drivers/telephony/Kconfig +--- linux-2.6.14-omap2/drivers/telephony/Kconfig 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/telephony/Kconfig 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/telephony/Makefile linux-h6300-omap2-2.6.14.3/drivers/telephony/Makefile +--- linux-2.6.14-omap2/drivers/telephony/Makefile 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/telephony/Makefile 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/telephony/omap/h6300_gsm_led.c linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/h6300_gsm_led.c +--- linux-2.6.14-omap2/drivers/telephony/omap/h6300_gsm_led.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/h6300_gsm_led.c 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/telephony/omap/h6300_gsm_led.h linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/h6300_gsm_led.h +--- linux-2.6.14-omap2/drivers/telephony/omap/h6300_gsm_led.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/h6300_gsm_led.h 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/telephony/omap/h6300_gsm_p5186.c linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/h6300_gsm_p5186.c +--- linux-2.6.14-omap2/drivers/telephony/omap/h6300_gsm_p5186.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/h6300_gsm_p5186.c 2005-10-20 20:57:07.000000000 +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 linux-2.6.14-omap2/drivers/telephony/omap/Makefile linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/Makefile +--- linux-2.6.14-omap2/drivers/telephony/omap/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/telephony/omap/Makefile 2005-10-06 02:34:39.000000000 +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 linux-2.6.14-omap2/drivers/usb/core/sysfs.c linux-h6300-omap2-2.6.14.3/drivers/usb/core/sysfs.c +--- linux-2.6.14-omap2/drivers/usb/core/sysfs.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/usb/core/sysfs.c 2005-11-23 01:44:02.000000000 +0200 +@@ -292,30 +292,23 @@ + { + struct usb_interface *intf; + struct usb_device *udev; +- int len; ++ struct usb_host_interface *alt; + + intf = to_usb_interface(dev); + udev = interface_to_usbdev(intf); ++ alt = intf->cur_altsetting; + +- len = sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic", +- le16_to_cpu(udev->descriptor.idVendor), +- le16_to_cpu(udev->descriptor.idProduct), +- le16_to_cpu(udev->descriptor.bcdDevice), +- udev->descriptor.bDeviceClass, +- udev->descriptor.bDeviceSubClass, +- udev->descriptor.bDeviceProtocol); +- buf += len; +- +- if (udev->descriptor.bDeviceClass == 0) { +- struct usb_host_interface *alt = intf->cur_altsetting; +- +- return len + sprintf(buf, "%02Xisc%02Xip%02X\n", +- alt->desc.bInterfaceClass, +- alt->desc.bInterfaceSubClass, +- alt->desc.bInterfaceProtocol); +- } else { +- return len + sprintf(buf, "*isc*ip*\n"); +- } ++ return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" ++ "ic%02Xisc%02Xip%02X\n", ++ le16_to_cpu(udev->descriptor.idVendor), ++ le16_to_cpu(udev->descriptor.idProduct), ++ le16_to_cpu(udev->descriptor.bcdDevice), ++ udev->descriptor.bDeviceClass, ++ udev->descriptor.bDeviceSubClass, ++ udev->descriptor.bDeviceProtocol, ++ alt->desc.bInterfaceClass, ++ alt->desc.bInterfaceSubClass, ++ alt->desc.bInterfaceProtocol); + } + static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +diff -Naur linux-2.6.14-omap2/drivers/usb/core/usb.c linux-h6300-omap2-2.6.14.3/drivers/usb/core/usb.c +--- linux-2.6.14-omap2/drivers/usb/core/usb.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/drivers/usb/core/usb.c 2005-11-23 01:44:02.000000000 +0200 +@@ -557,6 +557,7 @@ + { + struct usb_interface *intf; + struct usb_device *usb_dev; ++ struct usb_host_interface *alt; + int i = 0; + int length = 0; + +@@ -573,7 +574,8 @@ + + intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev (intf); +- ++ alt = intf->cur_altsetting; ++ + if (usb_dev->devnum < 0) { + pr_debug ("usb %s: already deleted?\n", dev->bus_id); + return -ENODEV; +@@ -615,46 +617,27 @@ + usb_dev->descriptor.bDeviceProtocol)) + return -ENOMEM; + +- if (usb_dev->descriptor.bDeviceClass == 0) { +- struct usb_host_interface *alt = intf->cur_altsetting; ++ if (add_hotplug_env_var(envp, num_envp, &i, ++ buffer, buffer_size, &length, ++ "INTERFACE=%d/%d/%d", ++ alt->desc.bInterfaceClass, ++ alt->desc.bInterfaceSubClass, ++ alt->desc.bInterfaceProtocol)) ++ return -ENOMEM; + +- /* 2.4 only exposed interface zero. in 2.5, hotplug +- * agents are called for all interfaces, and can use +- * $DEVPATH/bInterfaceNumber if necessary. +- */ +- if (add_hotplug_env_var(envp, num_envp, &i, +- buffer, buffer_size, &length, +- "INTERFACE=%d/%d/%d", +- alt->desc.bInterfaceClass, +- alt->desc.bInterfaceSubClass, +- alt->desc.bInterfaceProtocol)) +- return -ENOMEM; +- +- if (add_hotplug_env_var(envp, num_envp, &i, +- buffer, buffer_size, &length, +- "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", +- le16_to_cpu(usb_dev->descriptor.idVendor), +- le16_to_cpu(usb_dev->descriptor.idProduct), +- le16_to_cpu(usb_dev->descriptor.bcdDevice), +- usb_dev->descriptor.bDeviceClass, +- usb_dev->descriptor.bDeviceSubClass, +- usb_dev->descriptor.bDeviceProtocol, +- alt->desc.bInterfaceClass, +- alt->desc.bInterfaceSubClass, +- alt->desc.bInterfaceProtocol)) +- return -ENOMEM; +- } else { +- if (add_hotplug_env_var(envp, num_envp, &i, +- buffer, buffer_size, &length, +- "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic*isc*ip*", +- le16_to_cpu(usb_dev->descriptor.idVendor), +- le16_to_cpu(usb_dev->descriptor.idProduct), +- le16_to_cpu(usb_dev->descriptor.bcdDevice), +- usb_dev->descriptor.bDeviceClass, +- usb_dev->descriptor.bDeviceSubClass, +- usb_dev->descriptor.bDeviceProtocol)) +- return -ENOMEM; +- } ++ if (add_hotplug_env_var(envp, num_envp, &i, ++ buffer, buffer_size, &length, ++ "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", ++ le16_to_cpu(usb_dev->descriptor.idVendor), ++ le16_to_cpu(usb_dev->descriptor.idProduct), ++ le16_to_cpu(usb_dev->descriptor.bcdDevice), ++ usb_dev->descriptor.bDeviceClass, ++ usb_dev->descriptor.bDeviceSubClass, ++ usb_dev->descriptor.bDeviceProtocol, ++ alt->desc.bInterfaceClass, ++ alt->desc.bInterfaceSubClass, ++ alt->desc.bInterfaceProtocol)) ++ return -ENOMEM; + + envp[i] = NULL; + +diff -Naur linux-2.6.14-omap2/drivers/usb/gadget/omap_udc.c linux-h6300-omap2-2.6.14.3/drivers/usb/gadget/omap_udc.c +--- linux-2.6.14-omap2/drivers/usb/gadget/omap_udc.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/usb/gadget/omap_udc.c 2005-11-11 04:13:42.000000000 +0200 +@@ -59,7 +59,8 @@ + #undef USB_TRACE + + /* bulk DMA seems to be behaving for both IN and OUT */ +-#define USE_DMA ++//#define USE_DMA ++#undef USE_DMA + + /* ISO too */ + #define USE_ISO +@@ -2109,7 +2110,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: +@@ -2127,7 +2128,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) +@@ -2735,7 +2736,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 linux-2.6.14-omap2/drivers/video/omap/lcd_h6300.c linux-h6300-omap2-2.6.14.3/drivers/video/omap/lcd_h6300.c +--- linux-2.6.14-omap2/drivers/video/omap/lcd_h6300.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/video/omap/lcd_h6300.c 2005-11-11 04:13:42.000000000 +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 linux-2.6.14-omap2/drivers/video/omap/Makefile linux-h6300-omap2-2.6.14.3/drivers/video/omap/Makefile +--- linux-2.6.14-omap2/drivers/video/omap/Makefile 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/video/omap/Makefile 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/drivers/video/omap/omapfb_main.c linux-h6300-omap2-2.6.14.3/drivers/video/omap/omapfb_main.c +--- linux-2.6.14-omap2/drivers/video/omap/omapfb_main.c 2005-12-02 01:53:33.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/drivers/video/omap/omapfb_main.c 2005-10-24 19:16:46.000000000 +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 + +diff -Naur linux-2.6.14-omap2/fs/exec.c linux-h6300-omap2-2.6.14.3/fs/exec.c +--- linux-2.6.14-omap2/fs/exec.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/fs/exec.c 2005-11-23 01:44:02.000000000 +0200 +@@ -593,6 +593,7 @@ + struct signal_struct *sig = tsk->signal; + struct sighand_struct *newsighand, *oldsighand = tsk->sighand; + spinlock_t *lock = &oldsighand->siglock; ++ struct task_struct *leader = NULL; + int count; + + /* +@@ -668,7 +669,7 @@ + * and to assume its PID: + */ + if (!thread_group_leader(current)) { +- struct task_struct *leader = current->group_leader, *parent; ++ struct task_struct *parent; + struct dentry *proc_dentry1, *proc_dentry2; + unsigned long exit_state, ptrace; + +@@ -677,6 +678,7 @@ + * It should already be zombie at this point, most + * of the time. + */ ++ leader = current->group_leader; + while (leader->exit_state != EXIT_ZOMBIE) + yield(); + +@@ -736,7 +738,6 @@ + proc_pid_flush(proc_dentry2); + + BUG_ON(exit_state != EXIT_ZOMBIE); +- release_task(leader); + } + + /* +@@ -746,8 +747,11 @@ + sig->flags = 0; + + no_thread_group: +- BUG_ON(atomic_read(&sig->count) != 1); + exit_itimers(sig); ++ if (leader) ++ release_task(leader); ++ ++ BUG_ON(atomic_read(&sig->count) != 1); + + if (atomic_read(&oldsighand->count) == 1) { + /* +diff -Naur linux-2.6.14-omap2/fs/locks.c linux-h6300-omap2-2.6.14.3/fs/locks.c +--- linux-2.6.14-omap2/fs/locks.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/fs/locks.c 2005-12-02 01:34:35.000000000 +0200 +@@ -1418,7 +1418,7 @@ + lock_kernel(); + + error = __setlease(filp, arg, &flp); +- if (error) ++ if (error || arg == F_UNLCK) + goto out_unlock; + + error = fasync_helper(fd, filp, 1, &flp->fl_fasync); +diff -Naur linux-2.6.14-omap2/fs/xfs/Kconfig linux-h6300-omap2-2.6.14.3/fs/xfs/Kconfig +--- linux-2.6.14-omap2/fs/xfs/Kconfig 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/fs/xfs/Kconfig 2005-11-23 01:44:02.000000000 +0200 +@@ -24,7 +24,7 @@ + default y + + config XFS_QUOTA +- tristate "XFS Quota support" ++ bool "XFS Quota support" + depends on XFS_FS + help + If you say Y here, you will be able to set limits for disk usage on +diff -Naur linux-2.6.14-omap2/include/asm-alpha/barrier.h linux-h6300-omap2-2.6.14.3/include/asm-alpha/barrier.h +--- linux-2.6.14-omap2/include/asm-alpha/barrier.h 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/include/asm-alpha/barrier.h 2005-11-23 01:44:02.000000000 +0200 +@@ -1,6 +1,8 @@ + #ifndef __BARRIER_H + #define __BARRIER_H + ++#include <asm/compiler.h> ++ + #define mb() \ + __asm__ __volatile__("mb": : :"memory") + +diff -Naur linux-2.6.14-omap2/include/asm-arm/arch-omap/board-h6300.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/board-h6300.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/board-h6300.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/board-h6300.h 2005-08-12 13:46:22.000000000 +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 linux-2.6.14-omap2/include/asm-arm/arch-omap/h6300_uart_info.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/h6300_uart_info.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/h6300_uart_info.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/h6300_uart_info.h 2005-10-14 18:55:31.000000000 +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 linux-2.6.14-omap2/include/asm-arm/arch-omap/hardware.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/hardware.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/hardware.h 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/hardware.h 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/include/asm-arm/arch-omap/irqs.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/irqs.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/irqs.h 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/irqs.h 2005-11-11 04:13:42.000000000 +0200 +@@ -237,7 +237,7 @@ + #define INT_24XX_SDMA_IRQ1 13 + #define INT_24XX_SDMA_IRQ2 14 + #define INT_24XX_SDMA_IRQ3 15 +-#define INT_24XX_DSS_IRQ 25 ++#define INT_24XX_DSS_IRQ 25 + #define INT_24XX_GPIO_BANK1 29 + #define INT_24XX_GPIO_BANK2 30 + #define INT_24XX_GPIO_BANK3 31 +diff -Naur linux-2.6.14-omap2/include/asm-arm/arch-omap/mux.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/mux.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/mux.h 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/mux.h 2005-10-14 18:55:31.000000000 +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 linux-2.6.14-omap2/include/asm-arm/arch-omap/omapfb.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/omapfb.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/omapfb.h 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/omapfb.h 2005-11-11 04:13:42.000000000 +0200 +@@ -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 linux-2.6.14-omap2/include/asm-arm/arch-omap/omap_serial.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/omap_serial.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/omap_serial.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/omap_serial.h 2005-10-04 00:58:34.000000000 +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 linux-2.6.14-omap2/include/asm-arm/arch-omap/pca9535.h linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/pca9535.h +--- linux-2.6.14-omap2/include/asm-arm/arch-omap/pca9535.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/include/asm-arm/arch-omap/pca9535.h 2005-10-25 03:24:45.000000000 +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 linux-2.6.14-omap2/include/linux/proc_fs.h linux-h6300-omap2-2.6.14.3/include/linux/proc_fs.h +--- linux-2.6.14-omap2/include/linux/proc_fs.h 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/include/linux/proc_fs.h 2005-11-23 01:44:02.000000000 +0200 +@@ -66,6 +66,7 @@ + write_proc_t *write_proc; + atomic_t count; /* use count */ + int deleted; /* delete flag */ ++ void *set; + }; + + struct kcore_list { +diff -Naur linux-2.6.14-omap2/include/linux/sysctl.h linux-h6300-omap2-2.6.14.3/include/linux/sysctl.h +--- linux-2.6.14-omap2/include/linux/sysctl.h 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/include/linux/sysctl.h 2005-11-23 01:44:02.000000000 +0200 +@@ -24,6 +24,7 @@ + #include <linux/compiler.h> + + struct file; ++struct completion; + + #define CTL_MAXNAME 10 /* how many path components do we allow in a + call to sysctl? In other words, what is +@@ -925,6 +926,8 @@ + { + ctl_table *ctl_table; + struct list_head ctl_entry; ++ int used; ++ struct completion *unregistering; + }; + + struct ctl_table_header * register_sysctl_table(ctl_table * table, +diff -Naur linux-2.6.14-omap2/include/net/ipv6.h linux-h6300-omap2-2.6.14.3/include/net/ipv6.h +--- linux-2.6.14-omap2/include/net/ipv6.h 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/include/net/ipv6.h 2005-12-02 01:34:35.000000000 +0200 +@@ -237,6 +237,8 @@ + int newtype, + struct ipv6_opt_hdr __user *newopt, + int newoptlen); ++struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, ++ struct ipv6_txoptions *opt); + + extern int ip6_frag_nqueues; + extern atomic_t ip6_frag_mem; +diff -Naur linux-2.6.14-omap2/kernel/ptrace.c linux-h6300-omap2-2.6.14.3/kernel/ptrace.c +--- linux-2.6.14-omap2/kernel/ptrace.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/kernel/ptrace.c 2005-11-23 01:44:02.000000000 +0200 +@@ -152,7 +152,7 @@ + retval = -EPERM; + if (task->pid <= 1) + goto bad; +- if (task == current) ++ if (task->tgid == current->tgid) + goto bad; + /* the same process cannot be attached many times */ + if (task->ptrace & PT_PTRACED) +diff -Naur linux-2.6.14-omap2/kernel/signal.c linux-h6300-omap2-2.6.14.3/kernel/signal.c +--- linux-2.6.14-omap2/kernel/signal.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/kernel/signal.c 2005-12-02 01:34:35.000000000 +0200 +@@ -406,6 +406,8 @@ + + void exit_signal(struct task_struct *tsk) + { ++ atomic_dec(&tsk->signal->live); ++ + write_lock_irq(&tasklist_lock); + __exit_signal(tsk); + write_unlock_irq(&tasklist_lock); +@@ -1522,7 +1524,7 @@ + + psig = tsk->parent->sighand; + spin_lock_irqsave(&psig->siglock, flags); +- if (sig == SIGCHLD && ++ if (!tsk->ptrace && sig == SIGCHLD && + (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN || + (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) { + /* +diff -Naur linux-2.6.14-omap2/kernel/sysctl.c linux-h6300-omap2-2.6.14.3/kernel/sysctl.c +--- linux-2.6.14-omap2/kernel/sysctl.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/kernel/sysctl.c 2005-11-23 01:44:02.000000000 +0200 +@@ -169,7 +169,7 @@ + + extern struct proc_dir_entry *proc_sys_root; + +-static void register_proc_table(ctl_table *, struct proc_dir_entry *); ++static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *); + static void unregister_proc_table(ctl_table *, struct proc_dir_entry *); + #endif + +@@ -992,10 +992,51 @@ + + extern void init_irq_proc (void); + ++static DEFINE_SPINLOCK(sysctl_lock); ++ ++/* called under sysctl_lock */ ++static int use_table(struct ctl_table_header *p) ++{ ++ if (unlikely(p->unregistering)) ++ return 0; ++ p->used++; ++ return 1; ++} ++ ++/* called under sysctl_lock */ ++static void unuse_table(struct ctl_table_header *p) ++{ ++ if (!--p->used) ++ if (unlikely(p->unregistering)) ++ complete(p->unregistering); ++} ++ ++/* called under sysctl_lock, will reacquire if has to wait */ ++static void start_unregistering(struct ctl_table_header *p) ++{ ++ /* ++ * if p->used is 0, nobody will ever touch that entry again; ++ * we'll eliminate all paths to it before dropping sysctl_lock ++ */ ++ if (unlikely(p->used)) { ++ struct completion wait; ++ init_completion(&wait); ++ p->unregistering = &wait; ++ spin_unlock(&sysctl_lock); ++ wait_for_completion(&wait); ++ spin_lock(&sysctl_lock); ++ } ++ /* ++ * do not remove from the list until nobody holds it; walking the ++ * list in do_sysctl() relies on that. ++ */ ++ list_del_init(&p->ctl_entry); ++} ++ + void __init sysctl_init(void) + { + #ifdef CONFIG_PROC_FS +- register_proc_table(root_table, proc_sys_root); ++ register_proc_table(root_table, proc_sys_root, &root_table_header); + init_irq_proc(); + #endif + } +@@ -1004,6 +1045,7 @@ + void __user *newval, size_t newlen) + { + struct list_head *tmp; ++ int error = -ENOTDIR; + + if (nlen <= 0 || nlen >= CTL_MAXNAME) + return -ENOTDIR; +@@ -1012,20 +1054,30 @@ + if (!oldlenp || get_user(old_len, oldlenp)) + return -EFAULT; + } ++ spin_lock(&sysctl_lock); + tmp = &root_table_header.ctl_entry; + do { + struct ctl_table_header *head = + list_entry(tmp, struct ctl_table_header, ctl_entry); + void *context = NULL; +- int error = parse_table(name, nlen, oldval, oldlenp, ++ ++ if (!use_table(head)) ++ continue; ++ ++ spin_unlock(&sysctl_lock); ++ ++ error = parse_table(name, nlen, oldval, oldlenp, + newval, newlen, head->ctl_table, + &context); + kfree(context); ++ ++ spin_lock(&sysctl_lock); ++ unuse_table(head); + if (error != -ENOTDIR) +- return error; +- tmp = tmp->next; +- } while (tmp != &root_table_header.ctl_entry); +- return -ENOTDIR; ++ break; ++ } while ((tmp = tmp->next) != &root_table_header.ctl_entry); ++ spin_unlock(&sysctl_lock); ++ return error; + } + + asmlinkage long sys_sysctl(struct __sysctl_args __user *args) +@@ -1236,12 +1288,16 @@ + return NULL; + tmp->ctl_table = table; + INIT_LIST_HEAD(&tmp->ctl_entry); ++ tmp->used = 0; ++ tmp->unregistering = NULL; ++ spin_lock(&sysctl_lock); + if (insert_at_head) + list_add(&tmp->ctl_entry, &root_table_header.ctl_entry); + else + list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry); ++ spin_unlock(&sysctl_lock); + #ifdef CONFIG_PROC_FS +- register_proc_table(table, proc_sys_root); ++ register_proc_table(table, proc_sys_root, tmp); + #endif + return tmp; + } +@@ -1255,10 +1311,13 @@ + */ + void unregister_sysctl_table(struct ctl_table_header * header) + { +- list_del(&header->ctl_entry); ++ might_sleep(); ++ spin_lock(&sysctl_lock); ++ start_unregistering(header); + #ifdef CONFIG_PROC_FS + unregister_proc_table(header->ctl_table, proc_sys_root); + #endif ++ spin_unlock(&sysctl_lock); + kfree(header); + } + +@@ -1269,7 +1328,7 @@ + #ifdef CONFIG_PROC_FS + + /* Scan the sysctl entries in table and add them all into /proc */ +-static void register_proc_table(ctl_table * table, struct proc_dir_entry *root) ++static void register_proc_table(ctl_table * table, struct proc_dir_entry *root, void *set) + { + struct proc_dir_entry *de; + int len; +@@ -1305,13 +1364,14 @@ + de = create_proc_entry(table->procname, mode, root); + if (!de) + continue; ++ de->set = set; + de->data = (void *) table; + if (table->proc_handler) + de->proc_fops = &proc_sys_file_operations; + } + table->de = de; + if (de->mode & S_IFDIR) +- register_proc_table(table->child, de); ++ register_proc_table(table->child, de, set); + } + } + +@@ -1336,6 +1396,13 @@ + continue; + } + ++ /* ++ * In any case, mark the entry as goner; we'll keep it ++ * around if it's busy, but we'll know to do nothing with ++ * its fields. We are under sysctl_lock here. ++ */ ++ de->data = NULL; ++ + /* Don't unregister proc entries that are still being used.. */ + if (atomic_read(&de->count)) + continue; +@@ -1349,27 +1416,38 @@ + size_t count, loff_t *ppos) + { + int op; +- struct proc_dir_entry *de; ++ struct proc_dir_entry *de = PDE(file->f_dentry->d_inode); + struct ctl_table *table; + size_t res; +- ssize_t error; +- +- de = PDE(file->f_dentry->d_inode); +- if (!de || !de->data) +- return -ENOTDIR; +- table = (struct ctl_table *) de->data; +- if (!table || !table->proc_handler) +- return -ENOTDIR; +- op = (write ? 002 : 004); +- if (ctl_perm(table, op)) +- return -EPERM; ++ ssize_t error = -ENOTDIR; + +- res = count; +- +- error = (*table->proc_handler) (table, write, file, buf, &res, ppos); +- if (error) +- return error; +- return res; ++ spin_lock(&sysctl_lock); ++ if (de && de->data && use_table(de->set)) { ++ /* ++ * at that point we know that sysctl was not unregistered ++ * and won't be until we finish ++ */ ++ spin_unlock(&sysctl_lock); ++ table = (struct ctl_table *) de->data; ++ if (!table || !table->proc_handler) ++ goto out; ++ error = -EPERM; ++ op = (write ? 002 : 004); ++ if (ctl_perm(table, op)) ++ goto out; ++ ++ /* careful: calling conventions are nasty here */ ++ res = count; ++ error = (*table->proc_handler)(table, write, file, ++ buf, &res, ppos); ++ if (!error) ++ error = res; ++ out: ++ spin_lock(&sysctl_lock); ++ unuse_table(de->set); ++ } ++ spin_unlock(&sysctl_lock); ++ return error; + } + + static int proc_opensys(struct inode *inode, struct file *file) +diff -Naur linux-2.6.14-omap2/Makefile linux-h6300-omap2-2.6.14.3/Makefile +--- linux-2.6.14-omap2/Makefile 2005-12-02 01:53:31.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/Makefile 2005-12-02 01:34:34.000000000 +0200 +@@ -1,7 +1,7 @@ + VERSION = 2 + PATCHLEVEL = 6 + SUBLEVEL = 14 +-EXTRAVERSION = ++EXTRAVERSION = .3 + NAME=Affluent Albatross + + # *DOCUMENTATION* +@@ -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)-omap2 ++EXTRAVERSION := $(EXTRAVERSION)-omap1-h6300 + + # Do not print "Entering directory ..." + MAKEFLAGS += --no-print-directory +diff -Naur linux-2.6.14-omap2/net/core/datagram.c linux-h6300-omap2-2.6.14.3/net/core/datagram.c +--- linux-2.6.14-omap2/net/core/datagram.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/core/datagram.c 2005-11-23 01:44:02.000000000 +0200 +@@ -213,6 +213,10 @@ + { + int i, err, fraglen, end = 0; + struct sk_buff *next = skb_shinfo(skb)->frag_list; ++ ++ if (!len) ++ return 0; ++ + next_skb: + fraglen = skb_headlen(skb); + i = -1; +diff -Naur linux-2.6.14-omap2/net/ipv4/ipvs/ip_vs_core.c linux-h6300-omap2-2.6.14.3/net/ipv4/ipvs/ip_vs_core.c +--- linux-2.6.14-omap2/net/ipv4/ipvs/ip_vs_core.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/ipvs/ip_vs_core.c 2005-11-23 01:44:02.000000000 +0200 +@@ -1009,11 +1009,10 @@ + if (sysctl_ip_vs_expire_nodest_conn) { + /* try to expire the connection immediately */ + ip_vs_conn_expire_now(cp); +- } else { +- /* don't restart its timer, and silently +- drop the packet. */ +- __ip_vs_conn_put(cp); + } ++ /* don't restart its timer, and silently ++ drop the packet. */ ++ __ip_vs_conn_put(cp); + return NF_DROP; + } + +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_ftp.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_ftp.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_ftp.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_ftp.c 2005-12-02 01:34:35.000000000 +0200 +@@ -29,9 +29,9 @@ + static DEFINE_SPINLOCK(ip_ftp_lock); + + #define MAX_PORTS 8 +-static short ports[MAX_PORTS]; ++static unsigned short ports[MAX_PORTS]; + static int ports_c; +-module_param_array(ports, short, &ports_c, 0400); ++module_param_array(ports, ushort, &ports_c, 0400); + + static int loose; + module_param(loose, int, 0600); +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_irc.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_irc.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_irc.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_irc.c 2005-12-02 01:34:35.000000000 +0200 +@@ -34,7 +34,7 @@ + #include <linux/moduleparam.h> + + #define MAX_PORTS 8 +-static short ports[MAX_PORTS]; ++static unsigned short ports[MAX_PORTS]; + static int ports_c; + static int max_dcc_channels = 8; + static unsigned int dcc_timeout = 300; +@@ -52,7 +52,7 @@ + MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); + MODULE_DESCRIPTION("IRC (DCC) connection tracking helper"); + MODULE_LICENSE("GPL"); +-module_param_array(ports, short, &ports_c, 0400); ++module_param_array(ports, ushort, &ports_c, 0400); + MODULE_PARM_DESC(ports, "port numbers of IRC servers"); + module_param(max_dcc_channels, int, 0400); + MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session"); +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_netlink.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_netlink.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_netlink.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_netlink.c 2005-12-02 01:34:35.000000000 +0200 +@@ -58,14 +58,17 @@ + const struct ip_conntrack_tuple *tuple) + { + struct ip_conntrack_protocol *proto; ++ int ret = 0; + + NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum); + + proto = ip_conntrack_proto_find_get(tuple->dst.protonum); +- if (proto && proto->tuple_to_nfattr) +- return proto->tuple_to_nfattr(skb, tuple); ++ if (likely(proto && proto->tuple_to_nfattr)) { ++ ret = proto->tuple_to_nfattr(skb, tuple); ++ ip_conntrack_proto_put(proto); ++ } + +- return 0; ++ return ret; + + nfattr_failure: + return -1; +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_proto_icmp.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_proto_icmp.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2005-12-02 01:34:35.000000000 +0200 +@@ -296,7 +296,8 @@ + struct ip_conntrack_tuple *tuple) + { + if (!tb[CTA_PROTO_ICMP_TYPE-1] +- || !tb[CTA_PROTO_ICMP_CODE-1]) ++ || !tb[CTA_PROTO_ICMP_CODE-1] ++ || !tb[CTA_PROTO_ICMP_ID-1]) + return -1; + + tuple->dst.u.icmp.type = +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_proto_tcp.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2005-12-02 01:34:35.000000000 +0200 +@@ -362,6 +362,11 @@ + struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1]; + struct nfattr *tb[CTA_PROTOINFO_TCP_MAX]; + ++ /* updates could not contain anything about the private ++ * protocol info, in that case skip the parsing */ ++ if (!attr) ++ return 0; ++ + if (nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr) < 0) + goto nfattr_failure; + +@@ -813,6 +818,7 @@ + { + [TH_SYN] = 1, + [TH_SYN|TH_ACK] = 1, ++ [TH_SYN|TH_PUSH] = 1, + [TH_SYN|TH_ACK|TH_PUSH] = 1, + [TH_RST] = 1, + [TH_RST|TH_ACK] = 1, +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_tftp.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_tftp.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_conntrack_tftp.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_conntrack_tftp.c 2005-12-02 01:34:35.000000000 +0200 +@@ -26,9 +26,9 @@ + MODULE_LICENSE("GPL"); + + #define MAX_PORTS 8 +-static short ports[MAX_PORTS]; ++static unsigned short ports[MAX_PORTS]; + static int ports_c; +-module_param_array(ports, short, &ports_c, 0400); ++module_param_array(ports, ushort, &ports_c, 0400); + MODULE_PARM_DESC(ports, "port numbers of tftp servers"); + + #if 0 +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_core.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_core.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_core.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_core.c 2005-12-02 01:34:35.000000000 +0200 +@@ -66,10 +66,8 @@ + * removed until we've grabbed the reference */ + preempt_disable(); + p = __ip_nat_proto_find(protonum); +- if (p) { +- if (!try_module_get(p->me)) +- p = &ip_nat_unknown_protocol; +- } ++ if (!try_module_get(p->me)) ++ p = &ip_nat_unknown_protocol; + preempt_enable(); + + return p; +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_helper_pptp.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_helper_pptp.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_helper_pptp.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_helper_pptp.c 2005-12-02 01:34:35.000000000 +0200 +@@ -73,6 +73,7 @@ + struct ip_conntrack_tuple t; + struct ip_ct_pptp_master *ct_pptp_info; + struct ip_nat_pptp *nat_pptp_info; ++ struct ip_nat_range range; + + ct_pptp_info = &master->help.ct_pptp_info; + nat_pptp_info = &master->nat.help.nat_pptp_info; +@@ -110,7 +111,30 @@ + DEBUGP("not found!\n"); + } + +- ip_nat_follow_master(ct, exp); ++ /* This must be a fresh one. */ ++ BUG_ON(ct->status & IPS_NAT_DONE_MASK); ++ ++ /* Change src to where master sends to */ ++ range.flags = IP_NAT_RANGE_MAP_IPS; ++ range.min_ip = range.max_ip ++ = ct->master->tuplehash[!exp->dir].tuple.dst.ip; ++ if (exp->dir == IP_CT_DIR_ORIGINAL) { ++ range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; ++ range.min = range.max = exp->saved_proto; ++ } ++ /* hook doesn't matter, but it has to do source manip */ ++ ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); ++ ++ /* For DST manip, map port here to where it's expected. */ ++ range.flags = IP_NAT_RANGE_MAP_IPS; ++ range.min_ip = range.max_ip ++ = ct->master->tuplehash[!exp->dir].tuple.src.ip; ++ if (exp->dir == IP_CT_DIR_REPLY) { ++ range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; ++ range.min = range.max = exp->saved_proto; ++ } ++ /* hook doesn't matter, but it has to do destination manip */ ++ ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); + } + + /* outbound packets == from PNS to PAC */ +@@ -213,7 +237,7 @@ + + /* alter expectation for PNS->PAC direction */ + invert_tuplepr(&inv_t, &expect_orig->tuple); +- expect_orig->saved_proto.gre.key = htons(nat_pptp_info->pac_call_id); ++ expect_orig->saved_proto.gre.key = htons(ct_pptp_info->pns_call_id); + expect_orig->tuple.src.u.gre.key = htons(nat_pptp_info->pns_call_id); + expect_orig->tuple.dst.u.gre.key = htons(ct_pptp_info->pac_call_id); + inv_t.src.ip = reply_t->src.ip; +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_proto_gre.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_proto_gre.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_proto_gre.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_proto_gre.c 2005-12-02 01:34:35.000000000 +0200 +@@ -139,8 +139,8 @@ + break; + case GRE_VERSION_PPTP: + DEBUGP("call_id -> 0x%04x\n", +- ntohl(tuple->dst.u.gre.key)); +- pgreh->call_id = htons(ntohl(tuple->dst.u.gre.key)); ++ ntohs(tuple->dst.u.gre.key)); ++ pgreh->call_id = tuple->dst.u.gre.key; + break; + default: + DEBUGP("can't nat unknown GRE version\n"); +diff -Naur linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_proto_unknown.c linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_proto_unknown.c +--- linux-2.6.14-omap2/net/ipv4/netfilter/ip_nat_proto_unknown.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/netfilter/ip_nat_proto_unknown.c 2005-12-02 01:34:35.000000000 +0200 +@@ -62,7 +62,7 @@ + + struct ip_nat_protocol ip_nat_unknown_protocol = { + .name = "unknown", +- .me = THIS_MODULE, ++ /* .me isn't set: getting a ref to this cannot fail. */ + .manip_pkt = unknown_manip_pkt, + .in_range = unknown_in_range, + .unique_tuple = unknown_unique_tuple, +diff -Naur linux-2.6.14-omap2/net/ipv4/tcp_bic.c linux-h6300-omap2-2.6.14.3/net/ipv4/tcp_bic.c +--- linux-2.6.14-omap2/net/ipv4/tcp_bic.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv4/tcp_bic.c 2005-11-23 01:44:02.000000000 +0200 +@@ -27,7 +27,7 @@ + */ + + static int fast_convergence = 1; +-static int max_increment = 32; ++static int max_increment = 16; + static int low_window = 14; + static int beta = 819; /* = 819/1024 (BICTCP_BETA_SCALE) */ + static int low_utilization_threshold = 153; +diff -Naur linux-2.6.14-omap2/net/ipv6/datagram.c linux-h6300-omap2-2.6.14.3/net/ipv6/datagram.c +--- linux-2.6.14-omap2/net/ipv6/datagram.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv6/datagram.c 2005-12-02 01:34:35.000000000 +0200 +@@ -437,7 +437,7 @@ + break; + case IPPROTO_AH: + nexthdr = ptr[0]; +- len = (ptr[1] + 1) << 2; ++ len = (ptr[1] + 2) << 2; + break; + default: + nexthdr = ptr[0]; +diff -Naur linux-2.6.14-omap2/net/ipv6/exthdrs.c linux-h6300-omap2-2.6.14.3/net/ipv6/exthdrs.c +--- linux-2.6.14-omap2/net/ipv6/exthdrs.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv6/exthdrs.c 2005-12-02 01:34:35.000000000 +0200 +@@ -628,6 +628,7 @@ + if (!tot_len) + return NULL; + ++ tot_len += sizeof(*opt2); + opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); + if (!opt2) + return ERR_PTR(-ENOBUFS); +@@ -668,7 +669,26 @@ + + return opt2; + out: +- sock_kfree_s(sk, p, tot_len); ++ sock_kfree_s(sk, opt2, opt2->tot_len); + return ERR_PTR(err); + } + ++struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, ++ struct ipv6_txoptions *opt) ++{ ++ /* ++ * ignore the dest before srcrt unless srcrt is being included. ++ * --yoshfuji ++ */ ++ if (opt && opt->dst0opt && !opt->srcrt) { ++ if (opt_space != opt) { ++ memcpy(opt_space, opt, sizeof(*opt_space)); ++ opt = opt_space; ++ } ++ opt->opt_nflen -= ipv6_optlen(opt->dst0opt); ++ opt->dst0opt = NULL; ++ } ++ ++ return opt; ++} ++ +diff -Naur linux-2.6.14-omap2/net/ipv6/ip6_flowlabel.c linux-h6300-omap2-2.6.14.3/net/ipv6/ip6_flowlabel.c +--- linux-2.6.14-omap2/net/ipv6/ip6_flowlabel.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv6/ip6_flowlabel.c 2005-12-02 01:34:35.000000000 +0200 +@@ -225,20 +225,16 @@ + struct ip6_flowlabel * fl, + struct ipv6_txoptions * fopt) + { +- struct ipv6_txoptions * fl_opt = fl ? fl->opt : NULL; ++ struct ipv6_txoptions * fl_opt = fl->opt; + +- if (fopt == NULL || fopt->opt_flen == 0) { +- if (!fl_opt || !fl_opt->dst0opt || fl_opt->srcrt) +- return fl_opt; +- } ++ if (fopt == NULL || fopt->opt_flen == 0) ++ return fl_opt; + + if (fl_opt != NULL) { + opt_space->hopopt = fl_opt->hopopt; +- opt_space->dst0opt = fl_opt->srcrt ? fl_opt->dst0opt : NULL; ++ opt_space->dst0opt = fl_opt->dst0opt; + opt_space->srcrt = fl_opt->srcrt; + opt_space->opt_nflen = fl_opt->opt_nflen; +- if (fl_opt->dst0opt && !fl_opt->srcrt) +- opt_space->opt_nflen -= ipv6_optlen(fl_opt->dst0opt); + } else { + if (fopt->opt_nflen == 0) + return fopt; +diff -Naur linux-2.6.14-omap2/net/ipv6/raw.c linux-h6300-omap2-2.6.14.3/net/ipv6/raw.c +--- linux-2.6.14-omap2/net/ipv6/raw.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv6/raw.c 2005-12-02 01:34:35.000000000 +0200 +@@ -756,7 +756,9 @@ + } + if (opt == NULL) + opt = np->opt; +- opt = fl6_merge_options(&opt_space, flowlabel, opt); ++ if (flowlabel) ++ opt = fl6_merge_options(&opt_space, flowlabel, opt); ++ opt = ipv6_fixup_options(&opt_space, opt); + + fl.proto = proto; + rawv6_probe_proto_opt(&fl, msg); +diff -Naur linux-2.6.14-omap2/net/ipv6/udp.c linux-h6300-omap2-2.6.14.3/net/ipv6/udp.c +--- linux-2.6.14-omap2/net/ipv6/udp.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/ipv6/udp.c 2005-12-02 01:34:35.000000000 +0200 +@@ -778,7 +778,9 @@ + } + if (opt == NULL) + opt = np->opt; +- opt = fl6_merge_options(&opt_space, flowlabel, opt); ++ if (flowlabel) ++ opt = fl6_merge_options(&opt_space, flowlabel, opt); ++ opt = ipv6_fixup_options(&opt_space, opt); + + fl->proto = IPPROTO_UDP; + ipv6_addr_copy(&fl->fl6_dst, daddr); +diff -Naur linux-2.6.14-omap2/net/netfilter/nf_queue.c linux-h6300-omap2-2.6.14.3/net/netfilter/nf_queue.c +--- linux-2.6.14-omap2/net/netfilter/nf_queue.c 2005-10-28 03:02:08.000000000 +0300 ++++ linux-h6300-omap2-2.6.14.3/net/netfilter/nf_queue.c 2005-12-02 01:34:35.000000000 +0200 +@@ -117,7 +117,7 @@ + + /* QUEUE == DROP if noone is waiting, to be safe. */ + read_lock(&queue_handler_lock); +- if (!queue_handler[pf]->outfn) { ++ if (!queue_handler[pf] || !queue_handler[pf]->outfn) { + read_unlock(&queue_handler_lock); + kfree_skb(*skb); + return 1; +diff -Naur linux-2.6.14-omap2/sound/arm/Kconfig linux-h6300-omap2-2.6.14.3/sound/arm/Kconfig +--- linux-2.6.14-omap2/sound/arm/Kconfig 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/Kconfig 2005-11-13 02:15:10.000000000 +0200 +@@ -45,5 +45,15 @@ + + To compile this driver as a module, choose M here: the module + will be called snd-omap-aic23. ++ ++config SND_OMAP_TSC2101 ++ tristate "OMAP TSC2101 driver (iPaq H63xx)" ++ depends ARCH_OMAP && SND ++ select SND_PCM ++ help ++ ALSA driver for TI TSC2101. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-omap-tsc2101. + + endmenu +diff -Naur linux-2.6.14-omap2/sound/arm/Makefile linux-h6300-omap2-2.6.14.3/sound/arm/Makefile +--- linux-2.6.14-omap2/sound/arm/Makefile 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/Makefile 2005-11-13 02:15:10.000000000 +0200 +@@ -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_TSC2101) += snd-omap-tsc2101.o ++snd-omap-tsc2101-objs := omap-tsc2101.o omap-alsa-dma.o +diff -Naur linux-2.6.14-omap2/sound/arm/omap-aic23.c linux-h6300-omap2-2.6.14.3/sound/arm/omap-aic23.c +--- linux-2.6.14-omap2/sound/arm/omap-aic23.c 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/omap-aic23.c 2005-11-13 02:15:10.000000000 +0200 +@@ -262,144 +262,6 @@ + } + + /* +- * DMA functions +- * Depends on omap-aic23-dma.c functions and (omap) dma.c +- * +- */ +-#define DMA_BUF_SIZE 1024 * 8 +- +-static int audio_dma_request(struct audio_stream *s, +- void (*callback) (void *)) +-{ +- int err; +- +- err = omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch); +- if (err < 0) +- printk(KERN_ERR "unable to grab audio dma 0x%x\n", +- s->dma_dev); +- return err; +-} +- +-static int audio_dma_free(struct audio_stream *s) +-{ +- int err = 0; +- +- err = omap_free_sound_dma(s, &s->lch); +- if (err < 0) +- printk(KERN_ERR "Unable to free audio dma channels!\n"); +- return err; +-} +- +-/* +- * This function should calculate the current position of the dma in the +- * buffer. It will help alsa middle layer to continue update the buffer. +- * Its correctness is crucial for good functioning. +- */ +-static u_int audio_get_dma_pos(struct audio_stream *s) +-{ +- snd_pcm_substream_t *substream = s->stream; +- snd_pcm_runtime_t *runtime = substream->runtime; +- unsigned int offset; +- unsigned long flags; +- dma_addr_t count; +- ADEBUG(); +- +- /* this must be called w/ interrupts locked as requested in dma.c */ +- spin_lock_irqsave(&s->dma_lock, flags); +- +- /* For the current period let's see where we are */ +- count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]); +- +- spin_unlock_irqrestore(&s->dma_lock, flags); +- +- /* Now, the position related to the end of that period */ +- offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count); +- +- if (offset >= runtime->buffer_size || offset < 0) +- offset = 0; +- +- return offset; +-} +- +-/* +- * this stops the dma and clears the dma ptrs +- */ +-static void audio_stop_dma(struct audio_stream *s) +-{ +- unsigned long flags; +- ADEBUG(); +- +- spin_lock_irqsave(&s->dma_lock, flags); +- s->active = 0; +- s->period = 0; +- s->periods = 0; +- +- /* this stops the dma channel and clears the buffer ptrs */ +- omap_audio_stop_dma(s); +- +- omap_clear_sound_dma(s); +- +- spin_unlock_irqrestore(&s->dma_lock, flags); +-} +- +-/* +- * Main dma routine, requests dma according where you are in main alsa buffer +- */ +-static void audio_process_dma(struct audio_stream *s) +-{ +- snd_pcm_substream_t *substream = s->stream; +- snd_pcm_runtime_t *runtime; +- unsigned int dma_size; +- unsigned int offset; +- int ret; +- +- runtime = substream->runtime; +- if (s->active) { +- dma_size = frames_to_bytes(runtime, runtime->period_size); +- offset = dma_size * s->period; +- snd_assert(dma_size <= DMA_BUF_SIZE,); +- ret = +- omap_start_sound_dma(s, +- (dma_addr_t) runtime->dma_area + +- offset, dma_size); +- if (ret) { +- printk(KERN_ERR +- "audio_process_dma: cannot queue DMA buffer (%i)\n", +- ret); +- return; +- } +- +- s->period++; +- s->period %= runtime->periods; +- s->periods++; +- s->offset = offset; +- } +-} +- +-/* +- * This is called when dma IRQ occurs at the end of each transmited block +- */ +-void audio_dma_callback(void *data) +-{ +- struct audio_stream *s = data; +- +- /* +- * If we are getting a callback for an active stream then we inform +- * the PCM middle layer we've finished a period +- */ +- if (s->active) +- snd_pcm_period_elapsed(s->stream); +- +- spin_lock(&s->dma_lock); +- if (s->periods > 0) { +- s->periods--; +- } +- audio_process_dma(s); +- spin_unlock(&s->dma_lock); +-} +- +- +-/* + * Alsa section + * PCM settings and callbacks + */ +diff -Naur linux-2.6.14-omap2/sound/arm/omap-aic23.h linux-h6300-omap2-2.6.14.3/sound/arm/omap-aic23.h +--- linux-2.6.14-omap2/sound/arm/omap-aic23.h 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/omap-aic23.h 2005-11-13 02:15:10.000000000 +0200 +@@ -43,6 +43,7 @@ + #include <asm/arch/dma.h> + #include <sound/core.h> + #include <sound/pcm.h> ++#include "omap-alsa-dma.h" + + #define DEFAULT_OUTPUT_VOLUME 0x60 + #define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */ +@@ -67,27 +68,6 @@ + #define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB + + /* +- * Buffer management for alsa and dma +- */ +-struct audio_stream { +- char *id; /* identification string */ +- int stream_id; /* numeric identification */ +- int dma_dev; /* dma number of that device */ +- int *lch; /* Chain of channels this stream is linked to */ +- char started; /* to store if the chain was started or not */ +- int dma_q_head; /* DMA Channel Q Head */ +- int dma_q_tail; /* DMA Channel Q Tail */ +- char dma_q_count; /* DMA Channel Q Count */ +- int active:1; /* we are using this stream for transfer now */ +- int period; /* current transfer period */ +- int periods; /* current count of periods registerd in the DMA engine */ +- spinlock_t dma_lock; /* for locking in DMA operations */ +- snd_pcm_substream_t *stream; /* the pcm stream */ +- unsigned linked:1; /* dma channels linked */ +- int offset; /* store start position of the last period in the alsa buffer */ +-}; +- +-/* + * Alsa card structure for aic23 + */ + struct snd_card_omap_aic23 { +@@ -99,7 +79,6 @@ + + /*********** Function Prototypes *************************/ + +-void audio_dma_callback(void *); + int snd_omap_mixer(struct snd_card_omap_aic23 *); + void snd_omap_init_mixer(void); + /* Clock functions */ +diff -Naur linux-2.6.14-omap2/sound/arm/omap-alsa-dma.c linux-h6300-omap2-2.6.14.3/sound/arm/omap-alsa-dma.c +--- linux-2.6.14-omap2/sound/arm/omap-alsa-dma.c 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/omap-alsa-dma.c 2005-11-13 02:15:10.000000000 +0200 +@@ -440,6 +440,134 @@ + return; + } + ++int audio_dma_request(struct audio_stream *s, ++ void (*callback) (void *)) ++{ ++ int err; ++ ++ err = omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch); ++ if (err < 0) ++ printk(KERN_ERR "unable to grab audio dma 0x%x\n", ++ s->dma_dev); ++ return err; ++} ++ ++int audio_dma_free(struct audio_stream *s) ++{ ++ int err = 0; ++ ++ err = omap_free_sound_dma(s, &s->lch); ++ if (err < 0) ++ printk(KERN_ERR "Unable to free audio dma channels!\n"); ++ return err; ++} ++ ++/* ++ * This function should calculate the current position of the dma in the ++ * buffer. It will help alsa middle layer to continue update the buffer. ++ * Its correctness is crucial for good functioning. ++ */ ++u_int audio_get_dma_pos(struct audio_stream *s) ++{ ++ snd_pcm_substream_t *substream = s->stream; ++ snd_pcm_runtime_t *runtime = substream->runtime; ++ unsigned int offset; ++ unsigned long flags; ++ dma_addr_t count; ++ ++ /* this must be called w/ interrupts locked as requested in dma.c */ ++ spin_lock_irqsave(&s->dma_lock, flags); ++ ++ /* For the current period let's see where we are */ ++ count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]); ++ ++ spin_unlock_irqrestore(&s->dma_lock, flags); ++ ++ /* Now, the position related to the end of that period */ ++ offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count); ++ ++ if (offset >= runtime->buffer_size || offset < 0) ++ offset = 0; ++ ++ return offset; ++} ++ ++/* ++ * this stops the dma and clears the dma ptrs ++ */ ++void audio_stop_dma(struct audio_stream *s) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&s->dma_lock, flags); ++ s->active = 0; ++ s->period = 0; ++ s->periods = 0; ++ ++ /* this stops the dma channel and clears the buffer ptrs */ ++ omap_audio_stop_dma(s); ++ ++ omap_clear_sound_dma(s); ++ ++ spin_unlock_irqrestore(&s->dma_lock, flags); ++} ++ ++/* ++ * Main dma routine, requests dma according where you are in main alsa buffer ++ */ ++void audio_process_dma(struct audio_stream *s) ++{ ++ snd_pcm_substream_t *substream = s->stream; ++ snd_pcm_runtime_t *runtime; ++ unsigned int dma_size; ++ unsigned int offset; ++ int ret; ++ ++ runtime = substream->runtime; ++ if (s->active) { ++ dma_size = frames_to_bytes(runtime, runtime->period_size); ++ offset = dma_size * s->period; ++ snd_assert(dma_size <= DMA_BUF_SIZE,); ++ ret = ++ omap_start_sound_dma(s, ++ (dma_addr_t) runtime->dma_area + ++ offset, dma_size); ++ if (ret) { ++ printk(KERN_ERR ++ "audio_process_dma: cannot queue DMA buffer (%i)\n", ++ ret); ++ return; ++ } ++ ++ s->period++; ++ s->period %= runtime->periods; ++ s->periods++; ++ s->offset = offset; ++ } ++} ++ ++/* ++ * This is called when dma IRQ occurs at the end of each transmited block ++ */ ++void audio_dma_callback(void *data) ++{ ++ struct audio_stream *s = data; ++ ++ /* ++ * If we are getting a callback for an active stream then we inform ++ * the PCM middle layer we've finished a period ++ */ ++ if (s->active) ++ snd_pcm_period_elapsed(s->stream); ++ ++ spin_lock(&s->dma_lock); ++ if (s->periods > 0) { ++ s->periods--; ++ } ++ audio_process_dma(s); ++ spin_unlock(&s->dma_lock); ++} ++ + MODULE_AUTHOR("Texas Instruments"); + MODULE_DESCRIPTION + ("Common DMA handling for Audio driver on OMAP processors"); +diff -Naur linux-2.6.14-omap2/sound/arm/omap-alsa-dma.h linux-h6300-omap2-2.6.14.3/sound/arm/omap-alsa-dma.h +--- linux-2.6.14-omap2/sound/arm/omap-alsa-dma.h 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/omap-alsa-dma.h 2005-11-13 02:15:10.000000000 +0200 +@@ -30,7 +30,13 @@ + + /************************** INCLUDES *************************************/ + +-#include "omap-aic23.h" ++/* h6300 tsc2101 changes start */ ++//#include "omap-aic23.h" ++#include <sound/driver.h> ++#include <asm/arch/dma.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++/* h6300 tsc2101 changes end */ + + /************************** GLOBAL MACROS *************************************/ + +@@ -39,8 +45,33 @@ + #define DMA_FREE(s) omap_free_sound_dma(s, &s->lch) + #define DMA_CLEAR(s) omap_clear_sound_dma(s) + ++/* h6300 tsc2101 changes start */ ++#define DMA_BUF_SIZE 1024 * 8 ++ + /************************** GLOBAL DATA STRUCTURES *********************************/ + ++/* ++ * Buffer management for alsa and dma ++ */ ++struct audio_stream { ++ char *id; /* identification string */ ++ int stream_id; /* numeric identification */ ++ int dma_dev; /* dma number of that device */ ++ int *lch; /* Chain of channels this stream is linked to */ ++ char started; /* to store if the chain was started or not */ ++ int dma_q_head; /* DMA Channel Q Head */ ++ int dma_q_tail; /* DMA Channel Q Tail */ ++ char dma_q_count; /* DMA Channel Q Count */ ++ int active:1; /* we are using this stream for transfer now */ ++ int period; /* current transfer period */ ++ int periods; /* current count of periods registerd in the DMA engine */ ++ spinlock_t dma_lock; /* for locking in DMA operations */ ++ snd_pcm_substream_t *stream; /* the pcm stream */ ++ unsigned linked:1; /* dma channels linked */ ++ int offset; /* store start position of the last period in the alsa buffer */ ++}; ++/* h6300 tsc2101 changes end */ ++ + typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data); + + /**************** ARCH SPECIFIC FUNCIONS *******************************************/ +@@ -54,6 +85,14 @@ + int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr, + u_int dma_size); + ++/* h6300 tsc2101 changes start */ + void omap_audio_stop_dma(struct audio_stream *s); ++void audio_dma_callback(void *); ++void audio_process_dma(struct audio_stream *s); ++u_int audio_get_dma_pos(struct audio_stream *s); ++int audio_dma_request(struct audio_stream *s, void (*callback) (void *)); ++int audio_dma_free(struct audio_stream *s); ++void audio_stop_dma(struct audio_stream *s); ++/* h6300 tsc2101 changes end */ + + #endif +diff -Naur linux-2.6.14-omap2/sound/arm/omap-tsc2101.c linux-h6300-omap2-2.6.14.3/sound/arm/omap-tsc2101.c +--- linux-2.6.14-omap2/sound/arm/omap-tsc2101.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/omap-tsc2101.c 2005-11-13 02:15:10.000000000 +0200 +@@ -0,0 +1,665 @@ ++#include <linux/config.h> ++#include <sound/driver.h> ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/ioctl.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++ ++#ifdef CONFIG_PM ++#include <linux/pm.h> ++#endif ++ ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/arch/dma.h> ++#include <asm/hardware/tsc2101.h> ++#include <../drivers/ssi/omap-tsc2101.h> ++#include <asm/arch/mcbsp.h> ++ ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/initval.h> ++#include <sound/memalloc.h> ++ ++#include "omap-alsa-dma.h" ++#include "omap-tsc2101.h" ++ ++//#undef DEBUG ++#define DEBUG ++ ++#ifdef DEBUG ++#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__) ++#else ++#define ADEBUG() /* nop */ ++#endif ++ ++#define TSC2101_MASTER ++ ++#define DEFAULT_BITPERSAMPLE 16 ++#define AUDIO_RATE_DEFAULT 44100 ++#define AUDIO_MCBSP OMAP_MCBSP1 ++#define NUMBER_SAMPLE_RATES_SUPPORTED 16 ++#define PAGE2_AUDIO_CODEC_REGISTERS (2) ++ ++MODULE_AUTHOR("Everett Coleman, Daniel Petrini, David Cohen, Anderson Briglia - INdT"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("OMAP TSC2101 driver for ALSA"); ++MODULE_SUPPORTED_DEVICE("{{TSC2101,OMAP TSC2101}}"); ++MODULE_ALIAS("omap_mcbsp.1"); ++ ++static char *id = NULL; ++MODULE_PARM_DESC(id, "OMAP ALSA Driver for TSC2101 chip."); ++ ++static struct snd_card_omap_tsc2101 *omap_tsc2101 = NULL; ++ ++static struct omap_mcbsp_reg_cfg initial_config_mcbsp = { ++#ifdef CONFIG_MACH_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) | ++ 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), ++ ++ /* platform specific initialization */ ++# if defined(CONFIG_MACH_OMAP_H2) ++ .pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP, ++# elif defined(CONFIG_MACH_OMAP_H3) ++ ++# ifndef TSC2101_MASTER ++ .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP, ++# else ++ .pcr0 = CLKRM | SCLKME | FSXP | FSRP | CLKXP | CLKRP, ++# endif /* !TSC2101_MASTER */ ++# endif /* CONFIG_MACH_OMAP_H2 */ ++#endif /* CONFIG_MACH_H6300 */ ++}; ++ ++static unsigned int rates[] = { ++ 7350, 8000, 8018, 8727, ++ 8820, 9600, 11025, 12000, ++ 14700, 16000, 22050, 24000, ++ 29400, 32000, 44100, 48000 ++}; ++ ++static snd_pcm_hw_constraint_list_t hw_constraints_rates = { ++ .count = ARRAY_SIZE(rates), ++ .list = rates, ++ .mask = 0, ++}; ++ ++struct sample_rate_reg_info { ++ u16 sample_rate; ++ u8 divisor; ++ u8 fs_44kHz; ++}; ++ ++static const struct sample_rate_reg_info ++reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = { ++ {48000, 0, 0}, {44100, 0, 1}, {32000, 1, 0}, {29400, 1, 1}, ++ {24000, 2, 0}, {22050, 2, 1}, {16000, 3, 0}, {14700, 3, 1}, ++ {12000, 4, 0}, {11025, 4, 1}, {9600, 5, 0}, {8820, 5, 1}, ++ {8727, 6, 0}, {8018, 6, 1}, {8000, 7, 0}, {7350, 7, 1} ++}; ++ ++static snd_pcm_hardware_t snd_omap_tsc2101_capture = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE), ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | ++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | ++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | ++ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | ++ SNDRV_PCM_RATE_KNOT), ++ .rate_min = 7350, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .buffer_bytes_max = 128 * 1024, ++ .period_bytes_min = 32, ++ .period_bytes_max = 8 * 1024, ++ .periods_min = 16, ++ .periods_max = 255, ++ .fifo_size = 0, ++}; ++ ++static snd_pcm_hardware_t snd_omap_tsc2101_playback = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE), ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | ++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | ++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | ++ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | ++ SNDRV_PCM_RATE_KNOT), ++ .rate_min = 7350, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .buffer_bytes_max = 128 * 1024, ++ .period_bytes_min = 32, ++ .period_bytes_max = 8 * 1024, ++ .periods_min = 16, ++ .periods_max = 255, ++ .fifo_size = 0, ++}; ++ ++static __inline__ void ++audio_tsc2101_write (u8 address, u16 data) { ++ omap_tsc2101_write (PAGE2_AUDIO_CODEC_REGISTERS, address, data); ++} /* audio_tsc2101_write */ ++ ++static __inline__ u16 ++audio_tsc2101_read (u8 address) { ++ return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address)); ++} /* audio_tsc2101_read */ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++void ++snd_omap_tsc2101_free (snd_card_t * card) { ++ struct snd_card_omap_tsc2101 *chip = card->private_data; ++ ADEBUG(); ++ ++ /* TODO ++ * Turn off codec after it is done. ++ * Can't do it immediately, since it may still have ++ * buffered data. ++ */ ++ ++ ++ ++ ++ audio_dma_free (&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); ++ audio_dma_free (&chip->s[SNDRV_PCM_STREAM_CAPTURE]); ++} /* snd_omap_tsc2101_free */ ++ ++#ifdef CONFIG_PM ++static int ++snd_omap_tsc2101_suspend (snd_card_t * card, pm_message_t state) { ++ // TODO: function ++ return 0; ++} /* snd_omap_tsc2101_suspend */ ++ ++static int ++snd_omap_tsc2101_resume (snd_card_t * card) { ++ // TODO: function ++ return 0; ++} /* snd_omap_tsc2101_resume */ ++ ++static int ++omap_tsc2101_suspend (struct device *dev, pm_message_t state, u32 level) { ++ // TODO: function ++ return 0; ++} /* omap_tsc2101_suspend */ ++ ++static int ++omap_tsc2101_resume (struct device *dev, u32 level) { ++ // TODO: function ++ return 0; ++} /* omap_tsc2101_resume */ ++ ++#else ++# define snd_omap_tsc2101_suspend NULL ++# define snd_omap_tsc2101_resume NULL ++# define omap_tsc2101_suspend NULL ++# define omap_tsc2101_resume NULL ++#endif /* CONFIG_PM */ ++ ++static inline void ++tsc2101_configure (void) { ++ audio_tsc2101_write (TSC2101_CODEC_POWER_CTRL, 0x0000); ++ ++ /*Mute Analog Sidetone */ ++ /*Select MIC_INHED input for headset */ ++ /*Cell Phone In not connected */ ++ audio_tsc2101_write (TSC2101_MIXER_PGA_CTRL, ++ MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC); ++ ++ /* Set record source */ ++ // TODO:MIXER tsc2101_update (SET_RECSRC, tsc2101_local.recsrc); ++ ++ /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */ ++ /* 1dB AGC hysteresis */ ++ /* MICes bias 2V */ ++ audio_tsc2101_write (TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0)); ++ ++ /* Set codec output volume */ ++ audio_tsc2101_write (TSC2101_DAC_GAIN_CTRL, 0x0000); ++ ++ /* DAC left and right routed to SPK2 */ ++ /* SPK1/2 unmuted */ ++ audio_tsc2101_write (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 */ ++ ++ audio_tsc2101_write (TSC2101_AUDIO_CTRL_6, ++ AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC | ++ AC6_VGNDSCPTC); ++ ++ /* Headset/Hook switch detect disabled */ ++ audio_tsc2101_write (TSC2101_AUDIO_CTRL_7, 0x0000); ++ ++ /* Left line input volume control */ ++ // TODO:MIXER tsc2101_update (SET_LINE, tsc2101_local.line); ++ ++ /* mic input volume control */ ++ // TODO:MIXER tsc2101_update(SET_MIC, tsc2101_local.mic); ++ ++ /* Left/Right headphone channel volume control */ ++ /* Zero-cross detect on */ ++ // TODO:MIXER tsc2101_update (SET_VOLUME, tsc2101_local.volume); ++} /* tsc2101_configure */ ++ ++static int ++snd_card_omap_tsc2101_open (snd_pcm_substream_t * substream) { ++ struct snd_card_omap_tsc2101 *chip = snd_pcm_substream_chip(substream); ++ snd_pcm_runtime_t *runtime = substream->runtime; ++ int stream_id = substream->pstr->stream; ++ int err; ++ ADEBUG(); ++ ++ chip->s[stream_id].stream = substream; ++ ++// TODO: turn audio on, power on ++ ++ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ++ runtime->hw = snd_omap_tsc2101_playback; ++ else ++ runtime->hw = snd_omap_tsc2101_capture; ++ 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, &hw_constraints_rates)) < 0) ++ return err; ++ return 0; ++} /* snd_card_omap_tsc2101_open */ ++ ++static int ++snd_card_omap_tsc2101_close (snd_pcm_substream_t *substream) { ++ struct snd_card_omap_tsc2101 *chip = snd_pcm_substream_chip(substream); ++ ADEBUG(); ++ ++ // TODO: omap_tsc2101_clock_off(); ++ chip->s[substream->pstr->stream].stream = NULL; ++ return 0; ++} /* snd_card_omap_tsc2101_close */ ++ ++static int ++snd_omap_tsc2101_hw_params (snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) { ++ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); ++} /* snd_omap_tsc2101_hw_params */ ++ ++static int ++snd_omap_tsc2101_hw_free (snd_pcm_substream_t *substream) { ++ return snd_pcm_lib_free_pages(substream); ++} /* snd_omap_tsc2101_hw_free */ ++ ++static int ++omap_tsc2101_set_samplerate (struct snd_card_omap_tsc2101 *omap_tsc2101, long rate) { ++ u8 count = 0; ++ u16 data = 0; ++ int clkgdv = 0; ++ ++ if (rate >= 48000) ++ rate = 48000; ++ else if (rate >= 44100) ++ rate = 44100; ++ else if (rate >= 32000) ++ rate = 32000; ++ else if (rate >= 29400) ++ rate = 29400; ++ else if (rate >= 24000) ++ rate = 24000; ++ else if (rate >= 22050) ++ rate = 22050; ++ else if (rate >= 16000) ++ rate = 16000; ++ else if (rate >= 14700) ++ rate = 14700; ++ else if (rate >= 12000) ++ rate = 12000; ++ else if (rate >= 11025) ++ rate = 11025; ++ else if (rate >= 9600) ++ rate = 9600; ++ else if (rate >= 8820) ++ rate = 8820; ++ else if (rate >= 8727) ++ rate = 8727; ++ else if (rate >= 8018) ++ rate = 8018; ++ else if (rate >= 8000) ++ rate = 8000; ++ else ++ rate = 7350; ++ ++ /* wait for any frame to complete */ ++ udelay(125); ++ ++ /* Search for the right sample rate */ ++ while ((reg_info[count].sample_rate != rate) && ++ (count < NUMBER_SAMPLE_RATES_SUPPORTED)) { ++ count++; ++ } ++ if (count == NUMBER_SAMPLE_RATES_SUPPORTED) { ++ printk(KERN_ERR "Invalid Sample Rate %d requested\n", ++ (int)rate); ++ return -EPERM; ++ } ++ ++ /* Set AC1 */ ++ data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_1); ++ /*Clear prev settings */ ++ data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07)); ++ data |= ++ AC1_DACFS(reg_info[count].divisor) | AC1_ADCFS(reg_info[count]. ++ divisor); ++ audio_tsc2101_write(TSC2101_AUDIO_CTRL_1, data); ++ ++ /* Set the AC3 */ ++ data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_3); ++ /*Clear prev settings */ ++ data &= ~(AC3_REFFS | AC3_SLVMS); ++ data |= (reg_info[count].fs_44kHz) ? AC3_REFFS : 0; ++#ifdef TSC2101_MASTER ++ data |= AC3_SLVMS; ++#endif /* #ifdef TSC2101_MASTER */ ++ audio_tsc2101_write(TSC2101_AUDIO_CTRL_3, data); ++ ++ /* program the PLLs */ ++ if (reg_info[count].fs_44kHz) { ++ /* 44.1 khz - 12 MHz Mclk */ ++ audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(7)); /* PVAL 1; I_VAL 7 */ ++ audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490)); /* D_VAL 5264 */ ++ } else { ++ /* 48 khz - 12 Mhz Mclk */ ++ audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(8)); /* PVAL 1; I_VAL 8 */ ++ audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780)); /* D_VAL 1920 */ ++ } ++ ++ omap_tsc2101->samplerate = rate; ++ ++ /* Set the sample rate */ ++#ifndef TSC2101_MASTER ++ clkgdv = ++ DEFAULT_MCBSP_CLOCK / (rate * ++ (DEFAULT_BITPERSAMPLE * 2 - 1)); ++ if (clkgdv) ++ initial_config_mcbsp.srgr1 = ++ (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv)); ++ else ++ return (1); ++ ++ /* Stereo Mode */ ++ initial_config_mcbsp.srgr2 = ++ (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)); ++#else ++ initial_config_mcbsp.srgr1 = ++ (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv)); ++ initial_config_mcbsp.srgr2 = ++ ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1))); ++ ++#endif /* end of #ifdef TSC2101_MASTER */ ++ omap_mcbsp_config(AUDIO_MCBSP, &initial_config_mcbsp); ++ return 0; ++} /* omap_tsc2101_set_samplerate */ ++ ++static int ++snd_omap_tsc2101_prepare (snd_pcm_substream_t *substream) { ++ struct snd_card_omap_tsc2101 *chip = snd_pcm_substream_chip(substream); ++ snd_pcm_runtime_t *runtime = substream->runtime; ++ struct audio_stream *s = &chip->s[substream->pstr->stream]; ++ ++ /* set requested samplerate */ ++ omap_tsc2101_set_samplerate (chip, runtime->rate); ++ ++ s->period = 0; ++ s->periods = 0; ++ return 0; ++} /* snd_omap_tsc2101_prepare */ ++ ++static int ++snd_omap_tsc2101_trigger (snd_pcm_substream_t *substream, int cmd) { ++ struct snd_card_omap_tsc2101 *chip = snd_pcm_substream_chip(substream); ++ int stream_id = substream->pstr->stream; ++ struct audio_stream *s = &chip->s[stream_id]; ++ int err = 0; ++ ADEBUG(); ++ ++ /* note local interrupts are already disabled in the midlevel code */ ++ spin_lock(&s->dma_lock); ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ /* requested stream startup */ ++ s->active = 1; ++ audio_process_dma(s); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ /* requested stream shutdown */ ++ audio_stop_dma(s); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ spin_unlock(&s->dma_lock); ++ return err; ++} /* snd_omap_tsc2101_trigger */ ++ ++static snd_pcm_uframes_t ++snd_omap_tsc2101_pointer (snd_pcm_substream_t *substream) { ++ struct snd_card_omap_tsc2101 *chip = snd_pcm_substream_chip(substream); ++ return audio_get_dma_pos(&chip->s[substream->pstr->stream]); ++} /* snd_omap_tsc2101_pointer */ ++ ++static snd_pcm_ops_t snd_card_omap_tsc2101_playback_ops = { ++ .open = snd_card_omap_tsc2101_open, ++ .close = snd_card_omap_tsc2101_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_omap_tsc2101_hw_params, ++ .hw_free = snd_omap_tsc2101_hw_free, ++ .prepare = snd_omap_tsc2101_prepare, ++ .trigger = snd_omap_tsc2101_trigger, ++ .pointer = snd_omap_tsc2101_pointer, ++}; ++ ++static snd_pcm_ops_t snd_card_omap_tsc2101_capture_ops = { ++ .open = snd_card_omap_tsc2101_open, ++ .close = snd_card_omap_tsc2101_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_omap_tsc2101_hw_params, ++ .hw_free = snd_omap_tsc2101_hw_free, ++ .prepare = snd_omap_tsc2101_prepare, ++ .trigger = snd_omap_tsc2101_trigger, ++ .pointer = snd_omap_tsc2101_pointer, ++}; ++ ++static void ++omap_tsc2101_audio_init (struct snd_card_omap_tsc2101 *omap_tsc2101) { ++ /* Setup DMA stuff */ ++ omap_tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa TSC2101 out"; ++ omap_tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = ++ SNDRV_PCM_STREAM_PLAYBACK; ++ omap_tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = ++ OMAP_DMA_MCBSP1_TX; ++ ++ omap_tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa TSC2101 in"; ++ omap_tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = ++ SNDRV_PCM_STREAM_CAPTURE; ++ omap_tsc2101->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = ++ OMAP_DMA_MCBSP1_RX; ++ ++ /* configuring the McBSP */ ++ omap_mcbsp_request (AUDIO_MCBSP); ++ ++ /* if configured, then stop mcbsp */ ++ omap_mcbsp_stop (AUDIO_MCBSP); ++ ++ omap_mcbsp_config (AUDIO_MCBSP, &initial_config_mcbsp); ++ omap_mcbsp_start (AUDIO_MCBSP); ++ ++ tsc2101_configure (); ++} /* omap_tsc2101_audio_init */ ++ ++static int __init ++snd_card_omap_tsc2101_pcm (struct snd_card_omap_tsc2101 *omap_tsc2101, int device) { ++ snd_pcm_t *pcm; ++ int err; ++ ADEBUG(); ++ ++ if ((err = snd_pcm_new (omap_tsc2101->card, "TSC2101 PCM", device, 1, 1, &pcm)) < 0) ++ return err; ++ ++ snd_pcm_lib_preallocate_pages_for_all (pcm, ++ SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data (GFP_KERNEL), ++ 128 * 1024, 128 * 1024); ++ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ++ &snd_card_omap_tsc2101_playback_ops); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ++ &snd_card_omap_tsc2101_capture_ops); ++ ++ pcm->private_data = omap_tsc2101; ++ pcm->info_flags = 0; ++ strcpy (pcm->name, "omap tsc2101 pcm"); ++ ++ omap_tsc2101_audio_init (omap_tsc2101); ++ ++ audio_dma_request(&omap_tsc2101->s[SNDRV_PCM_STREAM_PLAYBACK], ++ audio_dma_callback); ++ audio_dma_request(&omap_tsc2101->s[SNDRV_PCM_STREAM_CAPTURE], ++ audio_dma_callback); ++ ++ omap_tsc2101->pcm = pcm; ++ return 0; ++} /* snd_card_omap_tsc2101_pcm */ ++ ++static int __init ++snd_omap_tsc2101_probe (struct device *dev) { ++ int err = 0; ++ snd_card_t *card; ++ ADEBUG(); ++ ++ if ((card = snd_card_new (-1, id, THIS_MODULE, sizeof (omap_tsc2101))) == NULL) ++ return -ENOMEM; ++ if ((omap_tsc2101 = kcalloc (1, sizeof (*omap_tsc2101), GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ ++ card->private_data = (void *)omap_tsc2101; ++ card->private_free = snd_omap_tsc2101_free; ++ omap_tsc2101->card = card; ++ omap_tsc2101->samplerate = AUDIO_RATE_DEFAULT; ++ ++ spin_lock_init(&omap_tsc2101->s[0].dma_lock); ++ spin_lock_init(&omap_tsc2101->s[1].dma_lock); ++ ++ // TODO: setup mixer (fail, goto nodev) ++ ++ if ((err = snd_card_omap_tsc2101_pcm (omap_tsc2101, 0)) < 0) ++ goto nodev; ++ ++ snd_card_set_pm_callback (card, snd_omap_tsc2101_suspend, snd_omap_tsc2101_resume, omap_tsc2101); ++ ++ strcpy (card->driver, "TSC2101"); ++ strcpy (card->shortname, "TI TSC2101"); ++ strcpy (card->longname, "TI OMAP TSC2101"); ++ ++ // TODO: init mixer ++ ++ if ((err = snd_card_register (card)) == 0) { ++ printk(KERN_INFO "TSC2101 audio support initialized\n"); ++ dev_set_drvdata(dev, card); ++ return 0; ++ } ++ ++nodev: ++ printk (KERN_ERR "failed to initialize TSC2101\n"); ++ snd_omap_tsc2101_free (card); ++ return err; ++} /* snd_omap_tsc2101_probe */ ++ ++static int ++snd_omap_tsc2101_remove (struct device *dev) { ++ snd_card_t *card = dev_get_drvdata(dev); ++ struct snd_card_omap_tsc2101 *chip = card->private_data; ++ ++ snd_card_free(card); ++ omap_tsc2101 = NULL; ++ card->private_data = NULL; ++ kfree (chip); ++ dev_set_drvdata (dev, NULL); ++ return 0; ++} /* snd_omap_tsc2101_remove */ ++ ++static struct device_driver omap_alsa_driver = { ++ .name = "omap_mcbsp", ++ .bus = &platform_bus_type, ++ .probe = snd_omap_tsc2101_probe, ++ .remove = snd_omap_tsc2101_remove, ++ .suspend = omap_tsc2101_suspend, ++ .resume = omap_tsc2101_resume, ++}; ++ ++static void ++omap_alsa_device_release (struct device *dev) { ++ /* Nothing */ ++} ++ ++static struct platform_device omap_alsa_device = { ++ .name = "omap_mcbsp", ++ .id = -1, ++ .dev = { ++ .release = omap_alsa_device_release, ++ }, ++}; ++ ++static int __init ++omap_tsc2101_init (void) { ++ int err; ++ ++ ADEBUG(); ++ if ((err = platform_device_register (&omap_alsa_device)) != 0) ++ return err; ++ if ((err = driver_register (&omap_alsa_driver)) != 0) ++ platform_device_unregister (&omap_alsa_device); ++ return err; ++} /* omap_tsc2101_init */ ++ ++static void __exit ++omap_tsc2101_exit (void) { ++ ADEBUG(); ++ driver_unregister (&omap_alsa_driver); ++ platform_device_unregister (&omap_alsa_device); ++} /* omap_tsc2101_exit */ ++ ++module_init (omap_tsc2101_init); ++module_exit (omap_tsc2101_exit); +diff -Naur linux-2.6.14-omap2/sound/arm/omap-tsc2101.h linux-h6300-omap2-2.6.14.3/sound/arm/omap-tsc2101.h +--- linux-2.6.14-omap2/sound/arm/omap-tsc2101.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/arm/omap-tsc2101.h 2005-11-13 02:15:10.000000000 +0200 +@@ -0,0 +1,13 @@ ++#ifndef __OMAP_AUDIO_TSC2101_H ++#define __OMAP_AUDIO_TSC2101_H ++ ++# include "omap-alsa-dma.h" ++ ++struct snd_card_omap_tsc2101 { ++ snd_card_t *card; ++ snd_pcm_t *pcm; ++ long samplerate; ++ struct audio_stream s[2]; /* playback & capture */ ++}; ++ ++#endif /* __OMAP_AUDIO_TSC2101_H */ +diff -Naur linux-2.6.14-omap2/sound/oss/Kconfig linux-h6300-omap2-2.6.14.3/sound/oss/Kconfig +--- linux-2.6.14-omap2/sound/oss/Kconfig 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/oss/Kconfig 2005-10-22 03:52:45.000000000 +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 linux-2.6.14-omap2/sound/oss/omap-audio-tsc2101.c linux-h6300-omap2-2.6.14.3/sound/oss/omap-audio-tsc2101.c +--- linux-2.6.14-omap2/sound/oss/omap-audio-tsc2101.c 2005-12-02 01:53:34.000000000 +0200 ++++ linux-h6300-omap2-2.6.14.3/sound/oss/omap-audio-tsc2101.c 2005-10-22 03:52:45.000000000 +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 ********************/ |