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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +/* + * 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 ") */ + .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 #include #include +#include #include @@ -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 **************************/ 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 + * 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 +#include +#include +#include + +#include +#include + +#include +#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, "); +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 + * 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 +#include +#include +#include + +#include +#include + +/* + * 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<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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +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 "); +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 + * 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 #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 #include #include +#include #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 #include +#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 #include +#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 ':' 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 : 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 +/* #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 +#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 /* request_firmware() */ +#include /* 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 +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#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 "); */ +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, "\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 +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#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 +#include +#include +#include +#include +#include /* required for 2.4.x kernels; verify_write() */ + +#include +#include +#if WIRELESS_EXT >= 13 +#include +#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 ap 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 +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif +#include +#include +#include +#include + +#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=DOT11_RATE_6) return DOT11_MOD_OFDM; */ + return default_modulation[r_enum]; + } + if(suffix=='c') { + if(r_enumDOT11_RATE_11) return -EINVAL; + return DOT11_MOD_CCK; + } + if(suffix=='p') { + if(r_enumDOT11_RATE_33) return -EINVAL; + return DOT11_MOD_PBCC; + } + if(suffix=='o') { + if(r_enumINT_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 +** Andreas Mohr +** +** 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 +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +#include +#endif +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#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 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 +#include + +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#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 #include #include +#include #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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include + +/* + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "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, "); +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 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include +#include + +/* #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 + #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 +#include + +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 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 #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 "); 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 #include #include +#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 +#include +#include +#include +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM +#include +#endif + +#include +#include +#include +#include +#include <../drivers/ssi/omap-tsc2101.h> +#include + +#include +#include +#include +#include + +#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 -#if CONFIG_ARCH_OMAP16XX +#if defined (CONFIG_ARCH_OMAP16XX) || defined (CONFIG_MACH_OMAP_H6300) #include <../drivers/ssi/omap-uwire.h> #include #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 ********************/