diff -Nur linux-2.6.9-intc1/arch/arm/boot/compressed/head.S linux-2.6.9-intc1-mih1/arch/arm/boot/compressed/head.S --- linux-2.6.9-intc1/arch/arm/boot/compressed/head.S 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/boot/compressed/head.S 2005-05-02 17:21:06.000000000 -0400 @@ -53,10 +53,18 @@ .macro writeb, rb strb \rb, [r3, #0] .endm -#elif defined(CONFIG_ARCH_PXA) /* Xscale-type */ +#elif defined(CONFIG_ARCH_PXA) /* Xscale-type output: 1=FFUART,2=BTUART,7=STUART */ .macro loadsp, rb mov \rb, #0x40000000 +#if defined(CONFIG_INITIAL_UART1) /* Output to FFUART */ orr \rb, \rb, #0x00100000 +#elif defined(CONFIG_INITIAL_UART2) /* Output to BTUART */ + orr \rb, \rb, #0x00200000 +#elif defined(CONFIG_INITIAL_UART3) /* Output to STUART */ + orr \rb, \rb, #0x00700000 +#else +#warning No serial ouput selected! +#endif .endm .macro writeb, rb strb \rb, [r3, #0] diff -Nur linux-2.6.9-intc1/arch/arm/configs/glencoe_defconfig linux-2.6.9-intc1-mih1/arch/arm/configs/glencoe_defconfig --- linux-2.6.9-intc1/arch/arm/configs/glencoe_defconfig 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/arch/arm/configs/glencoe_defconfig 2005-05-02 17:47:52.000000000 -0400 @@ -0,0 +1,1242 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-intc1-mih1 +# Mon May 2 17:47:43 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS 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=y +# 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 is not set +# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set + +# +# Intel PXA2xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_MAINSTONE is not set +CONFIG_MACH_GLENCOE=y +# CONFIG_ARCH_PXA_IDP is not set + +# +# Intel PXA27x Errata Fixes +# +CONFIG_PXA27x_E11=y +# CONFIG_PXA27x_E17 is not set +CONFIG_PXA27x_E20=y +CONFIG_PXA27x_E23=y +# CONFIG_PXA27x_E25 is not set +CONFIG_PXA27x_E28=y +CONFIG_PXA27x_E37=y +CONFIG_PXA27x_E38=y +CONFIG_PXA27x=y +CONFIG_IWMMXT=y + +# +# h720x Implementations +# +# CONFIG_INITIAL_UART1 is not set +# CONFIG_INITIAL_UART2 is not set +CONFIG_INITIAL_UART3=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_MINICACHE=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_XSCALE_PMU=y + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CPU_FREQ=y +CONFIG_DVFM=y +CONFIG_FAST_DVFM=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set +CONFIG_PCMCIA_PXA2XX=y + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set +CONFIG_PM=y +CONFIG_SRAM_ALLOCATE=y +# CONFIG_PREEMPT is not set +CONFIG_APM=y +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="root=/dev/mtdblock2 console=ttyS2,115200 noirqdebug mem=64M" +CONFIG_LEDS=y +CONFIG_LEDS_TIMER=y +CONFIG_LEDS_CPU=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +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=y +# 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=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +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=y +# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_2 is not set +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=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_XIP is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_MULTI_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_EDB7312 is not set +# CONFIG_MTD_SHARP_SL is not set +# CONFIG_MTD_PLATRAM is not set +CONFIG_MTD_PXA27x=y +CONFIG_MTD_PXA27x_WINDOWSIZE=32 + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_MULTIPLE_TABLES is not set +# CONFIG_IP_ROUTE_MULTIPATH is not set +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +# CONFIG_NET_IPGRE_BROADCAST is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +CONFIG_INET_TUNNEL=y + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=y +CONFIG_IP_NF_CT_ACCT=y +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +# CONFIG_IP_NF_FTP is not set +# CONFIG_IP_NF_IRC is not set +# CONFIG_IP_NF_TFTP is not set +# CONFIG_IP_NF_AMANDA is not set +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_LIMIT=y +CONFIG_IP_NF_MATCH_IPRANGE=y +CONFIG_IP_NF_MATCH_MAC=y +CONFIG_IP_NF_MATCH_PKTTYPE=y +# CONFIG_IP_NF_MATCH_MARK is not set +CONFIG_IP_NF_MATCH_MULTIPORT=y +# 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=y +# CONFIG_IP_NF_MATCH_TTL is not set +CONFIG_IP_NF_MATCH_TCPMSS=y +# CONFIG_IP_NF_MATCH_HELPER is not set +CONFIG_IP_NF_MATCH_STATE=y +CONFIG_IP_NF_MATCH_CONNTRACK=y +# CONFIG_IP_NF_MATCH_OWNER is not set +CONFIG_IP_NF_MATCH_PHYSDEV=y +# 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_COMMENT is not set +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +# CONFIG_IP_NF_TARGET_LOG is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_IP_NF_TARGET_TCPMSS=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_SAME is not set +CONFIG_IP_NF_NAT_LOCAL=y +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_ARPTABLES is not set + +# +# Bridge: Netfilter Configuration +# +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# 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_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=y +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +# CONFIG_BT_HCIBCM203X 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 is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_SMC91X 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 is not set +# 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_HERMES=m +# CONFIG_ATMEL is not set + +# +# Wireless 802.11b Pcmcia/Cardbus cards support +# +CONFIG_PCMCIA_HERMES=m +CONFIG_AIRO_CS=m +# CONFIG_PCMCIA_WL3501 is not set +CONFIG_HOSTAP=m +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_CS=m +CONFIG_NET_WIRELESS=y + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +CONFIG_PCMCIA_PCNET=m +# CONFIG_PCMCIA_NMCLAN is not set +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +# CONFIG_PCMCIA_AXNET 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=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=m +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=m +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_TSDEV=y +CONFIG_INPUT_TSDEV_SCREEN_X=240 +CONFIG_INPUT_TSDEV_SCREEN_Y=320 +# CONFIG_INPUT_TSLIBDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_RAW 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_FASTAPSERIAL=y +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC 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 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=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 is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_PXA_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set +CONFIG_I2C_ALGOPXA=y + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_ADAP_PXA=y + +# +# Hardware Sensors Chip support +# +# CONFIG_I2C_SENSOR is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 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_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_ADCM2650 is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS 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_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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 is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# 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 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=y +# CONFIG_SMB_NLS_DEFAULT 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 + +# +# 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=y +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=y +# CONFIG_LDM_DEBUG 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="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=y +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_PXA=y +CONFIG_FB_PXA_LCD_QVGA=y +# CONFIG_FB_PXA_LCD_VGA is not set +CONFIG_FB_PXA_OVERLAY=m +# CONFIG_FB_PXA_PARAMETERS is not set +# 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 is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_AC97_CODEC=m + +# +# ALSA ARM devices +# +CONFIG_SND_PXA2XX_AC97=m +CONFIG_SND_PXA2XX_PCM=m + +# +# ALSA USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set + +# +# PCMCIA devices +# + +# +# Open Sound System +# +CONFIG_SOUND_PRIME=m +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# 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 +CONFIG_SOUND_WM97XX=m +# CONFIG_SOUND_WM9713 is not set +CONFIG_SOUND_PXA=m +CONFIG_SOUND_PXA_AC97=m +CONFIG_SOUND_NO_SPEAKER_INIT=y +CONFIG_SOUND_PXA_WM97XX=m +# CONFIG_SOUND_PXA_WM97XX_BATT is not set +# CONFIG_SOUND_UCB1X00 is not set + +# +# Misc devices +# +CONFIG_XIRCOM_GEM=y +CONFIG_GLENCOE_PERIPH=y + +# +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set +# CONFIG_UCB1400_TS is not set +# CONFIG_MCP_WM97XX is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# 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_EHCI_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811HS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +# CONFIG_USB_MIDI is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL 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_PHIDGETSERVO is not set +# CONFIG_USB_TEST is not set + +# +# USB Gadget Support +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +CONFIG_USB_GADGET_PXA27X=y +CONFIG_USB_PXA27X=y +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_SA1100 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +# CONFIG_USB_ZERO is not set +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_PXA=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC_CCITT=y +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff -Nur linux-2.6.9-intc1/arch/arm/Kconfig linux-2.6.9-intc1-mih1/arch/arm/Kconfig --- linux-2.6.9-intc1/arch/arm/Kconfig 2005-05-02 15:16:10.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/Kconfig 2005-05-01 14:54:02.000000000 -0400 @@ -257,6 +257,20 @@ depends on SA1111 || ARCH_IXP4XX default y +##################################################################### +# PXA2xx serial output +if ARCH_PXA + choice + prompt "Initial UART output port (during uncompressing and printchar debugging)" + config INITIAL_UART1 + bool "UART1 (FFUART)" + config INITIAL_UART2 + bool "UART2 (BTUART)" + config INITIAL_UART3 + bool "UART3 (STUART)" + endchoice +endif + source arch/arm/mm/Kconfig # bool 'Use XScale PMU as timer source' CONFIG_XSCALE_PMU_TIMER @@ -357,7 +371,7 @@ config CPU_FREQ bool "Support CPU clock change (EXPERIMENTAL)" - depends on (ARCH_SA1100 || ARCH_INTEGRATOR || MACH_MAINSTONE) && EXPERIMENTAL + depends on (ARCH_SA1100 || ARCH_INTEGRATOR || MACH_MAINSTONE || MACH_GLENCOE) && EXPERIMENTAL help CPU clock scaling allows you to change the clock speed of the running CPU on the fly. This is a nice method to save battery power, @@ -589,7 +603,7 @@ config LEDS bool "Timer and CPU usage LEDs" - depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB || ARCH_IMX + depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || MACH_GLENCOE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB || ARCH_IMX help If you say Y here, the LEDs on your machine will be used to provide useful information about your current system status. @@ -602,8 +616,8 @@ system, but the driver will do nothing. config LEDS_TIMER - bool "Timer LED" if LEDS && (ARCH_NETWINDER || ARCH_EBSA285 || ARCH_SHARK || MACH_MAINSTONE || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_P720T || ARCH_VERSATILE_PB || ARCH_IMX) - depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB || ARCH_IMX + bool "Timer LED" if LEDS && (ARCH_NETWINDER || ARCH_EBSA285 || ARCH_SHARK || MACH_MAINSTONE || MACH_GLENCOE || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_P720T || ARCH_VERSATILE_PB || ARCH_IMX) + depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || MACH_GLENCOE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB || ARCH_IMX default y if ARCH_EBSA110 help If you say Y here, one of the system LEDs (the green one on the @@ -618,7 +632,7 @@ config LEDS_CPU bool "CPU usage LED" - depends on LEDS && (ARCH_NETWINDER || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_P720T || ARCH_VERSATILE_PB || ARCH_IMX) + depends on LEDS && (ARCH_NETWINDER || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || MACH_GLENCOE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_P720T || ARCH_VERSATILE_PB || ARCH_IMX) help If you say Y here, the red LED will be used to give a good real time indication of CPU usage, by lighting whenever the idle task diff -Nur linux-2.6.9-intc1/arch/arm/kernel/debug.S linux-2.6.9-intc1-mih1/arch/arm/kernel/debug.S --- linux-2.6.9-intc1/arch/arm/kernel/debug.S 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/kernel/debug.S 2005-05-02 17:21:56.000000000 -0400 @@ -239,13 +239,21 @@ .endm #elif defined(CONFIG_ARCH_PXA) - + /* Xscale-type output: 1=FFUART,2=BTUART,7=STUART */ .macro addruart,rx mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? moveq \rx, #0x40000000 @ physical movne \rx, #io_p2v(0x40000000) @ virtual - orr \rx, \rx, #0x00100000 +#if defined(CONFIG_INITIAL_UART1) /* Output to FFUART */ + orr \rb, \rb, #0x00100000 +#elif defined(CONFIG_INITIAL_UART2) /* Output to BTUART */ + orr \rb, \rb, #0x00200000 +#elif defined(CONFIG_INITIAL_UART3) /* Output to STUART */ + orr \rb, \rb, #0x00700000 +#else +#warning No serial output selected +#endif .endm .macro senduart,rd,rx diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/cpu-freq-pxa27x.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/cpu-freq-pxa27x.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/cpu-freq-pxa27x.c 2005-05-02 15:16:10.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/cpu-freq-pxa27x.c 2005-05-01 01:18:02.000000000 -0400 @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include "cpu-freq-voltage-pxa27x.h" @@ -274,7 +274,6 @@ static int enter_13M(void) { int cccr; - int clkcfg; if (CCCR & (1 << 31)) return 1; @@ -309,7 +308,7 @@ unsigned int turbo_mode_flag, unsigned int memc_clock_flag) { - int cccr, ccsr, clkcfg; + int cccr, clkcfg; unsigned int time_delay; if (!(CCCR & (1 << 31))) diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/generic.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/generic.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/generic.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/generic.c 2005-05-01 01:22:32.000000000 -0400 @@ -117,7 +117,7 @@ }, }; -static u64 pxamci_dmamask = 0xffffffffUL; +static u64 pxamci_dmamask = ~(u32)0; static struct platform_device pxamci_device = { .name = "pxa2xx-mci", @@ -219,6 +219,33 @@ .id = 2, }; +#if defined(CONFIG_PXA27x) + +static struct resource pxartc_resource[] = { + [0] = { + .start = 0x40900000, + .end = 0x409000ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC1Hz, + .end = IRQ_RTC1Hz, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_RTCAlrm, + .end = IRQ_RTCAlrm, + .flags = IORESOURCE_IRQ + } +}; + +struct platform_device pxartc_device = { + .name = "pxartc", + .id = -1, + .num_resources = ARRAY_SIZE(pxartc_resource), + .resource = pxartc_resource, +}; + static struct resource pxacamera_resources[] = { [0] = { .start = 0x50000000, @@ -232,7 +259,7 @@ }, }; -static u64 pxa_camera_dmamask = 0xffffffffUL; +static u64 pxa_camera_dmamask = ~(u64)0; static struct platform_device pxacamera_device = { .name = "pxa2xx-camera", @@ -241,34 +268,34 @@ .dma_mask = &pxa_camera_dmamask, .coherent_dma_mask = 0xffffffff, }, - .num_resources = ARRAY_SIZE(pxacamera_resources), + .num_resources = ARRAY_SIZE(pxacamera_resources), .resource = pxacamera_resources, }; static struct resource pxaac97_resources[] = { - [0] = { - .start = 0x40500000, - .end = 0x40500fff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_AC97, - .end = IRQ_AC97, - .flags = IORESOURCE_IRQ, - }, + [0] = { + .start = 0x40500000, + .end = 0x405ffffc, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_AC97, + .end = IRQ_AC97, + .flags = IORESOURCE_IRQ, + }, }; -static u64 pxa_ac97_dmamask = 0xffffffffUL; +static u64 pxaac97_dmamask = ~(u64)0; static struct platform_device pxaac97_device = { - .name = "pxa2xx-ac97", - .id = -1, - .dev = { - .dma_mask = &pxa_ac97_dmamask, - .coherent_dma_mask = 0xffffffff, - }, - .num_resources = ARRAY_SIZE(pxaac97_resources), - .resource = pxaac97_resources, + .name = "pxa-ac97", + .id = -1, + .dev = { + .dma_mask = &pxaac97_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxaac97_resources), + .resource = pxaac97_resources, }; static struct platform_device pxaflash_device = { @@ -276,6 +303,8 @@ .id = -1, }; +#endif + static struct platform_device *devices[] __initdata = { &pxamci_device, &udc_device, @@ -283,9 +312,12 @@ &ffuart_device, &btuart_device, &stuart_device, +#if defined(CONFIG_PXA27x) + &pxartc_device, &pxacamera_device, &pxaac97_device, &pxaflash_device, +#endif }; static int __init pxa_init(void) diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/glencoe.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/glencoe.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/glencoe.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/glencoe.c 2005-05-01 01:25:46.000000000 -0400 @@ -0,0 +1,275 @@ +/* + * linux/arch/arm/mach-pxa/glencoe.c + * + * Support for the Intel B3G Glencoe Platform. + * based on mainstone.c + * Changed by Matthias Ihmig + * + * Original author: Nicolas Pitre + * Created: Nov 05, 2002 + * Copyright: MontaVista Software Inc. + * + * 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 + +#include +#include +#include +#include +#include + +#include "generic.h" + + +static void __init glencoe_init_irq(void) +{ + pxa_init_irq(); +} +/* Framework future power code. +static int gln_audio_startup(snd_pcm_substream_t *substream, void *priv) +{ + // audio routing/power up/GPIOs/... + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ; + return 0; +} +static void gln_audio_shutdown(snd_pcm_substream_t *substream, void *priv) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ; +} +static void gln_audio_suspend(void *priv) +{ } +static void gln_audio_resume(void *priv) +{ } +*/ + +static pxa2xx_audio_ops_t gln_audio_ops = { +/* .startup = gln_audio_startup, + .shutdown = gln_audio_shutdown, + .suspend = gln_audio_suspend, + .resume = gln_audio_resume,*/ +}; + +static struct platform_device gln_audio_device = { + .name = "pxa2xx-ac97", + .id = -1, + .dev = { .platform_data = &gln_audio_ops }, +}; + +static void glencoe_backlight_power(int on) +{ + if (on) { + pxa_gpio_mode(GLN_GPIO_BL_PWR_ON | GPIO_OUT); + pxa_gpio_mode(GLN_BACKLIGHT_PWM_MD); + pxa_set_cken(GLN_BACKLIGHT_PWM_CKEN, 1); + GLN_BACKLIGHT_PWM_CTRL = 0; + GLN_BACKLIGHT_PWM_PWDUTY = 0x3ff; + GLN_BACKLIGHT_PWM_PERVAL = 0x3ff; + GPIO_set(GLN_GPIO_BL_PWR_ON); + } else { + PWM_CTRL0 = 0; + GLN_BACKLIGHT_PWM_PWDUTY = 0x0; + GLN_BACKLIGHT_PWM_PERVAL = 0x3ff; + pxa_set_cken(GLN_BACKLIGHT_PWM_CKEN, 0); + GPIO_clr(GLN_GPIO_BL_PWR_ON); + } +} + +static void glencoe_lcd_power(int on) +{ + if (on) { + GPIO_set(GLN_GPIO_LCD_PWR_ON); + } else { + GPIO_clr(GLN_GPIO_LCD_PWR_ON); + } +} + +static struct pxafb_mach_info toshiba_ltm04c380k __initdata = { + .pixclock = 50000, + .xres = 640, + .yres = 480, + .bpp = 16, + .hsync_len = 1, + .left_margin = 0x9f, + .right_margin = 1, + .vsync_len = 44, + .upper_margin = 0, + .lower_margin = 0, + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, + .lccr0 = LCCR0_Act, + .lccr3 = LCCR3_PCP, + .pxafb_backlight_power = glencoe_backlight_power, + .pxafb_lcd_power = glencoe_lcd_power, +}; + +static struct pxafb_mach_info toshiba_ltm035a776c __initdata = { + .pixclock = 110000, + .xres = 240, + .yres = 320, + .bpp = 16, + .hsync_len = 4, + .left_margin = 8+4, /* 8 */ + .right_margin = 20, /* 14 */ + .vsync_len = 3, + .upper_margin = 1+4, /* 1 */ + .lower_margin = 10, /* 6 */ + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, + .lccr0 = LCCR0_Act, + .lccr3 = LCCR3_PCP, + .pxafb_backlight_power = glencoe_backlight_power, + .pxafb_lcd_power = glencoe_lcd_power, +}; + +/* + * MMC/SD Device + * + * The card detect interrupt isn't debounced and even triggers + * too early on Glencoe, so delay it by HZ/4 to give the card + * a chance to fully insert/eject. + */ +static struct mmc_detect { + struct timer_list detect_timer; + void *devid; +} mmc_detect; + +static void mmc_detect_callback(unsigned long data) +{ +#ifdef CONFIG_MMC + mmc_detect_change(mmc_detect.devid); +#endif +} + +static irqreturn_t glencoe_mmc_detect_int(int irq, void *devid, struct pt_regs *regs) +{ + mmc_detect.devid=devid; + mod_timer(&mmc_detect.detect_timer, jiffies + HZ/4); + return IRQ_HANDLED; +} + +static int glencoe_mci_init(struct device *dev, irqreturn_t (*unused_detect_int)(int, void *, struct pt_regs *), void *data) +{ + struct mmc_host *mmc = (struct mmc_host*)data; + int err; + + pxa_set_cken(CKEN12_MMC, 0); + pxa_gpio_mode(GPIO32_MMCCLK_MD); + pxa_gpio_mode(GPIO112_MMCCMD_MD); + pxa_gpio_mode(GPIO92_MMCDAT0_MD); + pxa_gpio_mode(GPIO109_MMCDAT1_MD); + pxa_gpio_mode(GPIO110_MMCDAT2_MD); + pxa_gpio_mode(GPIO111_MMCDAT3_MD); + + pxa_gpio_mode(GLN_GPIO_nMMC_CARD_DET | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_MMC_PWR_ON | GPIO_OUT); + GPIO_clr(GLN_GPIO_MMC_PWR_ON); + + + init_timer(&mmc_detect.detect_timer); + mmc_detect.detect_timer.function = mmc_detect_callback; + mmc_detect.detect_timer.data = (unsigned long) &mmc_detect; + + err = request_irq(GLENCOE_MMC_IRQ, glencoe_mmc_detect_int, SA_INTERRUPT, + "MMC card detect", (void *)mmc); + if (err) { + printk(KERN_ERR "glencoe_mci_init: MMC/SD: can't request MMC card detect IRQ\n"); + return -1; + } + set_irq_type(GLENCOE_MMC_IRQ, IRQT_BOTHEDGE); + return 0; +} + +static void glencoe_mci_setpower(struct device *dev, unsigned int vdd) +{ + struct pxamci_platform_data* p_d = dev->platform_data; + + if ( vdd == 0 ) { + printk(KERN_DEBUG "glencoe_mci_setpower: off\n"); + pxa_gpio_mode(GLN_GPIO_MMC_PWR_ON | GPIO_OUT); + GPIO_clr(GLN_GPIO_MMC_PWR_ON); + + } else if ( ( 1 << vdd ) & p_d->ocr_mask ) { + printk(KERN_DEBUG "glencoe_mci_setpower: on\n"); + pxa_gpio_mode(GLN_GPIO_MMC_PWR_ON | GPIO_OUT); + GPIO_set(GLN_GPIO_MMC_PWR_ON); + + } else + printk(KERN_ERR "glencoe_mci_setpower: unable to set power\n"); + +} + +static void glencoe_mci_exit(struct device *dev, void *data) +{ + struct mmc_host *mmc = (struct mmc_host*)data; + unsigned long flags; + + local_irq_save(flags); + free_irq(GLENCOE_MMC_IRQ, (void *)mmc); + del_timer(&mmc_detect.detect_timer); + local_irq_restore(flags); +} + +static struct pxamci_platform_data glencoe_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = glencoe_mci_init, + .setpower = glencoe_mci_setpower, + .exit = glencoe_mci_exit, +}; + +static void __init glencoe_init(void) +{ + /* system bus arbiter setting + * - Core_Park + * - LCD_wt:DMA_wt:CORE_Wt = 2:3:4 + */ + ARB_CNTRL = ARB_CORE_PARK | 0x234; + + GLN_SW_GREEN_LED_ON; + +#ifdef CONFIG_FB_PXA_LCD_VGA + set_pxa_fb_info(&toshiba_ltm04c380k); +#elif CONFIG_FB_PXA_LCD_QVGA + set_pxa_fb_info(&toshiba_ltm035a776c); +#endif + + pxa_set_mci_info(&glencoe_mci_platform_data); + + // audio init + pxa_gpio_mode(GLN_GPIO_nAUDIO_PWR_DWN | GPIO_OUT); + GPIO_set(GLN_GPIO_nAUDIO_PWR_DWN); // power up audio/ts codec + platform_device_register(&gln_audio_device); +} + +static void __init glencoe_map_io(void) +{ + pxa_map_io(); +} + +MACHINE_START(GLENCOE, "Intel Beyond 3G Glencoe Platform") + MAINTAINER("Intel") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + BOOT_PARAMS(0xa0000100) + MAPIO(glencoe_map_io) + INITIRQ(glencoe_init_irq) + INITTIME(pxa_init_time) + INIT_MACHINE(glencoe_init) +MACHINE_END diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/ipmc.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/ipmc.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/ipmc.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/ipmc.c 2005-02-26 17:28:56.000000000 -0500 @@ -467,7 +467,7 @@ printk( KERN_INFO "Could not register device ipmc, res = %d.\n ",rc ); return -EBUSY; } - printk( KERN_INFO "Register device ipmc successgul.\n " ); + printk( KERN_INFO "Register device ipmc successful.\n " ); return 0; } diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/Kconfig linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/Kconfig --- linux-2.6.9-intc1/arch/arm/mach-pxa/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/Kconfig 2005-04-25 15:54:29.000000000 -0400 @@ -13,7 +13,12 @@ bool "Intel HCDDBBVA0 Development Platform" select PXA27x select IWMMXT - + +config MACH_GLENCOE + bool "Intel B3G Glencoe Platform" + select PXA27x + select IWMMXT + config ARCH_PXA_IDP bool "Accelent Xscale IDP" select PXA25x diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/leds.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/leds.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/leds.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/leds.c 2005-02-22 19:15:36.000000000 -0500 @@ -22,6 +22,8 @@ leds_event = lubbock_leds_event; if (machine_is_mainstone()) leds_event = mainstone_leds_event; + if (machine_is_glencoe()) + leds_event = glencoe_leds_event; if (machine_is_pxa_idp()) leds_event = idp_leds_event; diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/leds-glencoe.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/leds-glencoe.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/leds-glencoe.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/leds-glencoe.c 2005-05-01 00:16:31.000000000 -0400 @@ -0,0 +1,140 @@ +/* + * linux/arch/arm/mach-pxa/leds-glencoe.c + * + * Modified from linux/arch/arm/mach-pxa/leds-lubbock.c + * by Matthias Ihmig + * Copyright (C) 2000 John Dorsey + * + * Copyright (c) 2001 Jeff Sutherland + * + * Original (leds-footbridge.c) by Russell King + * + * See leds.h for bit definitions. The first version defines D28 on the + * Lubbock dev board as the heartbeat, and D27 as the Sys_busy led. + * There's plenty more if you're interested in adding them :) + */ + + +#include +#include + +#include +#include +#include +#include + +#include "leds.h" +#include + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +#define HEARTBEAT_LED 1 +#define SYS_BUSY_LED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +void glencoe_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = HEARTBEAT_LED | SYS_BUSY_LED; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = HEARTBEAT_LED | SYS_BUSY_LED; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = HEARTBEAT_LED | SYS_BUSY_LED; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= HEARTBEAT_LED; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= SYS_BUSY_LED; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~SYS_BUSY_LED; + break; +#endif + + case led_halted: + break; + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~HEARTBEAT_LED; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= HEARTBEAT_LED; + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~SYS_BUSY_LED; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= SYS_BUSY_LED; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + { + switch (hw_led_state) { + case 0: // all on + HEARTBEAT_LED_ON; + SYS_BUSY_LED_ON; + break; + case 1: // turn off heartbeat, status on: + HEARTBEAT_LED_OFF; + SYS_BUSY_LED_ON; + break; + case 2: // status off, heartbeat on: + HEARTBEAT_LED_ON; + SYS_BUSY_LED_OFF; + break; + case 3: // turn them both off... + HEARTBEAT_LED_OFF; + SYS_BUSY_LED_OFF; + break; + default: + break; + } + } + local_irq_restore(flags); +} diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/leds.h linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/leds.h --- linux-2.6.9-intc1/arch/arm/mach-pxa/leds.h 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/leds.h 2005-02-22 20:34:45.000000000 -0500 @@ -10,3 +10,4 @@ extern void idp_leds_event(led_event_t evt); extern void lubbock_leds_event(led_event_t evt); extern void mainstone_leds_event(led_event_t evt); +extern void glencoe_leds_event(led_event_t evt); diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/Makefile linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/Makefile --- linux-2.6.9-intc1/arch/arm/mach-pxa/Makefile 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/Makefile 2005-05-01 14:54:02.000000000 -0400 @@ -10,12 +10,14 @@ # Specific board support obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o +obj-$(CONFIG_MACH_GLENCOE) += glencoe.o obj-$(CONFIG_ARCH_PXA_IDP) += idp.o # Support for blinky lights led-y := leds.o led-$(CONFIG_ARCH_LUBBOCK) += leds-lubbock.o led-$(CONFIG_MACH_MAINSTONE) += leds-mainstone.o +led-$(CONFIG_MACH_GLENCOE) += leds-glencoe.o led-$(CONFIG_ARCH_PXA_IDP) += leds-idp.o obj-$(CONFIG_LEDS) += $(led-y) diff -Nur linux-2.6.9-intc1/arch/arm/mach-pxa/pm.c linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/pm.c --- linux-2.6.9-intc1/arch/arm/mach-pxa/pm.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mach-pxa/pm.c 2005-04-21 20:40:13.000000000 -0400 @@ -21,12 +21,15 @@ #include #include #include +#include #include #include #include #include -#include +#ifdef CONFIG_MACH_MAINSTONE + #include +#endif #include #include "cpu-freq-voltage-pxa27x.h" @@ -38,6 +41,7 @@ extern struct subsystem power_subsys; extern int pxa27x_set_voltage(unsigned int, unsigned int, unsigned int); +extern unsigned int pxa27x_get_voltage(void); extern void pxa27x_cpufreq_restore(void); extern void pxa_cpu_suspend(void); diff -Nur linux-2.6.9-intc1/arch/arm/mm/consistent.c linux-2.6.9-intc1-mih1/arch/arm/mm/consistent.c --- linux-2.6.9-intc1/arch/arm/mm/consistent.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/mm/consistent.c 2005-03-24 15:52:16.000000000 -0500 @@ -1,7 +1,7 @@ /* * linux/arch/arm/mm/consistent.c * - * Copyright (C) 2000-2002 Russell King + * Copyright (C) 2000-2004 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -30,7 +30,7 @@ * This is the page table (2MB) covering uncached, DMA consistent allocations */ static pte_t *consistent_pte; -static spinlock_t consistent_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(consistent_lock); /* * VM region handling support. @@ -65,6 +65,7 @@ struct list_head vm_list; unsigned long vm_start; unsigned long vm_end; + struct page *vm_pages; }; static struct vm_region consistent_head = { @@ -206,6 +207,8 @@ pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); struct page *end = page + (1 << order); + c->vm_pages = page; + /* * Set the "dma handle" */ @@ -215,6 +218,9 @@ BUG_ON(!pte_none(*pte)); set_page_count(page, 1); + /* + * x86 does not mark the pages reserved... + */ SetPageReserved(page); set_pte(pte, mk_pte(page, prot)); page++; @@ -264,6 +270,53 @@ } EXPORT_SYMBOL(dma_alloc_writecombine); +static int dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + unsigned long flags, user_size, kern_size; + struct vm_region *c; + int ret = -ENXIO; + + user_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + + spin_lock_irqsave(&consistent_lock, flags); + c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); + spin_unlock_irqrestore(&consistent_lock, flags); + + if (c) { + unsigned long off = vma->vm_pgoff; + + kern_size = (c->vm_end - c->vm_start) >> PAGE_SHIFT; + + if (off < kern_size && + user_size <= (kern_size - off)) { + vma->vm_flags |= VM_RESERVED; + ret = remap_pfn_range(vma, vma->vm_start, + page_to_pfn(c->vm_pages) + off, + user_size << PAGE_SHIFT, + vma->vm_page_prot); + } + } + + return ret; +} + +int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return dma_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL(dma_mmap_coherent); + +int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + return dma_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL(dma_mmap_writecombine); + /* * free a page as defined by the above mapping. */ @@ -300,6 +353,10 @@ if (pfn_valid(pfn)) { struct page *page = pfn_to_page(pfn); + + /* + * x86 does not mark the pages reserved... + */ ClearPageReserved(page); __free_page(page); diff -Nur linux-2.6.9-intc1/arch/arm/tools/mach-types linux-2.6.9-intc1-mih1/arch/arm/tools/mach-types --- linux-2.6.9-intc1/arch/arm/tools/mach-types 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/arch/arm/tools/mach-types 2005-02-22 19:25:52.000000000 -0500 @@ -615,3 +615,4 @@ tomtomgo MACH_TOMTOMGO TOMTOMGO 605 versatile_ab MACH_VERSATILE_AB VERSATILE_AB 606 edb9307 MACH_EDB9307 EDB9307 607 +glencoe MACH_GLENCOE GLENCOE 4061 diff -Nur linux-2.6.9-intc1/Documentation/DocBook/librs.tmpl linux-2.6.9-intc1-mih1/Documentation/DocBook/librs.tmpl --- linux-2.6.9-intc1/Documentation/DocBook/librs.tmpl 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/Documentation/DocBook/librs.tmpl 2005-03-21 21:28:40.000000000 -0500 @@ -0,0 +1,289 @@ + + + + + + Reed-Solomon Library Programming Interface + + + + Thomas + Gleixner + +
+ tglx@linutronix.de +
+
+
+
+ + + 2004 + Thomas Gleixner + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + The generic Reed-Solomon Library provides encoding, decoding + and error correction functions. + + + Reed-Solomon codes are used in communication and storage + applications to ensure data integrity. + + + This documentation is provided for developers who want to utilize + the functions provided by the library. + + + + + Known Bugs And Assumptions + + None. + + + + + Usage + + This chapter provides examples how to use the library. + + + Initializing + + The init function init_rs returns a pointer to a + rs decoder structure, which holds the necessary + information for encoding, decoding and error correction + with the given polynomial. It either uses an existing + matching decoder or creates a new one. On creation all + the lookup tables for fast en/decoding are created. + The function may take a while, so make sure not to + call it in critical code paths. + + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 0 + * primitve element to generate roots = 1 + * generator polinomial degree (number of roots) = 6 + */ +rs_decoder = init_rs (10, 0x409, 0, 1, 6); + + + + Encoding + + The encoder calculates the Reed-Solomon code over + the given data length and stores the result in + the parity buffer. Note that the parity buffer must + be initialized before calling the encoder. + + + The expanded data can be inverted on the fly by + providing a non zero inversion mask. The expanded data is + XOR'ed with the mask. This is used e.g. for FLASH + ECC, where the all 0xFF is inverted to an all 0x00. + The Reed-Solomon code for all 0x00 is all 0x00. The + code is inverted before storing to FLASH so it is 0xFF + too. This prevent's that reading from an erased FLASH + results in ECC errors. + + + The databytes are expanded to the given symbol size + on the fly. There is no support for encoding continuous + bitstreams with a symbol size != 8 at the moment. If + it is necessary it should be not a big deal to implement + such functionality. + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6]; +/* Initialize the parity buffer */ +memset(par, 0, sizeof(par)); +/* Encode 512 byte in data8. Store parity in buffer par */ +encode_rs8 (rs_decoder, data8, 512, par, 0); + + + + Decoding + + The decoder calculates the syndrome over + the given data length and the received parity symbols + and corrects errors in the data. + + + If a syndrome is available from a hardware decoder + then the syndrome calculation is skipped. + + + The correction of the data buffer can be suppressed + by providing a correction pattern buffer and an error + location buffer to the decoder. The decoder stores the + calculated error location and the correction bitmask + in the given buffers. This is useful for hardware + decoders which use a weird bit ordering scheme. + + + The databytes are expanded to the given symbol size + on the fly. There is no support for decoding continuous + bitstreams with a symbolsize != 8 at the moment. If + it is necessary it should be not a big deal to implement + such functionality. + + + + + Decoding with syndrome calculation, direct data correction + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6]; +uint8_t data[512]; +int numerr; +/* Receive data */ +..... +/* Receive parity */ +..... +/* Decode 512 byte in data8.*/ +numerr = decode_rs8 (rs_decoder, data8, par, 512, NULL, 0, NULL, 0, NULL); + + + + + + Decoding with syndrome given by hardware decoder, direct data correction + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6], syn[6]; +uint8_t data[512]; +int numerr; +/* Receive data */ +..... +/* Receive parity */ +..... +/* Get syndrome from hardware decoder */ +..... +/* Decode 512 byte in data8.*/ +numerr = decode_rs8 (rs_decoder, data8, par, 512, syn, 0, NULL, 0, NULL); + + + + + + Decoding with syndrome given by hardware decoder, no direct data correction. + + + Note: It's not necessary to give data and received parity to the decoder. + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6], syn[6], corr[8]; +uint8_t data[512]; +int numerr, errpos[8]; +/* Receive data */ +..... +/* Receive parity */ +..... +/* Get syndrome from hardware decoder */ +..... +/* Decode 512 byte in data8.*/ +numerr = decode_rs8 (rs_decoder, NULL, NULL, 512, syn, 0, errpos, 0, corr); +for (i = 0; i < numerr; i++) { + do_error_correction_in_your_buffer(errpos[i], corr[i]); +} + + + + + Cleanup + + The function free_rs frees the allocated resources, + if the caller is the last user of the decoder. + + +/* Release resources */ +free_rs(rs_decoder); + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the Reed-Solomon Library and are relevant for a developer. + +!Iinclude/linux/rslib.h + + + + Public Functions Provided + + This chapter contains the autogenerated documentation of the Reed-Solomon functions + which are exported. + +!Elib/reed_solomon/reed_solomon.c + + + + Credits + + The library code for encoding and decoding was written by Phil Karn. + + + Copyright 2002, Phil Karn, KA9Q + May be used under the terms of the GNU General Public License (GPL) + + + The wrapper functions and interfaces are written by Thomas Gleixner + + + Many users have provided bugfixes, improvements and helping hands for testing. + Thanks a lot. + + + The following people have contributed to this document: + + + Thomas Gleixnertglx@linutronix.de + + +
diff -Nur linux-2.6.9-intc1/Documentation/DocBook/mtdnand.tmpl linux-2.6.9-intc1-mih1/Documentation/DocBook/mtdnand.tmpl --- linux-2.6.9-intc1/Documentation/DocBook/mtdnand.tmpl 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/Documentation/DocBook/mtdnand.tmpl 2005-03-21 21:28:40.000000000 -0500 @@ -0,0 +1,1320 @@ + + + + + + MTD NAND Driver Programming Interface + + + + Thomas + Gleixner + +
+ tglx@linutronix.de +
+
+
+
+ + + 2004 + Thomas Gleixner + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + The generic NAND driver supports almost all NAND and AG-AND based + chips and connects them to the Memory Technology Devices (MTD) + subsystem of the Linux Kernel. + + + This documentation is provided for developers who want to implement + board drivers or filesystem drivers suitable for NAND devices. + + + + + Known Bugs And Assumptions + + None. + + + + + Documentation hints + + The function and structure docs are autogenerated. Each function and + struct member has a short description which is marked with an [XXX] identifier. + The following chapters explain the meaning of those identifiers. + + + Function identifiers [XXX] + + The functions are marked with [XXX] identifiers in the short + comment. The identifiers explain the usage and scope of the + functions. Following identifiers are used: + + + + [MTD Interface] + These functions provide the interface to the MTD kernel API. + They are not replacable and provide functionality + which is complete hardware independent. + + + [NAND Interface] + These functions are exported and provide the interface to the NAND kernel API. + + + [GENERIC] + Generic functions are not replacable and provide functionality + which is complete hardware independent. + + + [DEFAULT] + Default functions provide hardware related functionality which is suitable + for most of the implementations. These functions can be replaced by the + board driver if neccecary. Those functions are called via pointers in the + NAND chip description structure. The board driver can set the functions which + should be replaced by board dependend functions before calling nand_scan(). + If the function pointer is NULL on entry to nand_scan() then the pointer + is set to the default function which is suitable for the detected chip type. + + + + + Struct member identifiers [XXX] + + The struct members are marked with [XXX] identifiers in the + comment. The identifiers explain the usage and scope of the + members. Following identifiers are used: + + + + [INTERN] + These members are for NAND driver internal use only and must not be + modified. Most of these values are calculated from the chip geometry + information which is evaluated during nand_scan(). + + + [REPLACEABLE] + Replaceable members hold hardware related functions which can be + provided by the board driver. The board driver can set the functions which + should be replaced by board dependend functions before calling nand_scan(). + If the function pointer is NULL on entry to nand_scan() then the pointer + is set to the default function which is suitable for the detected chip type. + + + [BOARDSPECIFIC] + Board specific members hold hardware related information which must + be provided by the board driver. The board driver must set the function + pointers and datafields before calling nand_scan(). + + + [OPTIONAL] + Optional members can hold information relevant for the board driver. The + generic NAND driver code does not use this information. + + + + + + + Basic board driver + + For most boards it will be sufficient to provide just the + basic functions and fill out some really board dependend + members in the nand chip description structure. + See drivers/mtd/nand/skeleton for reference. + + + Basic defines + + At least you have to provide a mtd structure and + a storage for the ioremap'ed chip address. + You can allocate the mtd structure using kmalloc + or you can allocate it statically. + In case of static allocation you have to allocate + a nand_chip structure too. + + + Kmalloc based example + + +static struct mtd_info *board_mtd; +static unsigned long baseaddr; + + + Static example + + +static struct mtd_info board_mtd; +static struct nand_chip board_chip; +static unsigned long baseaddr; + + + + Partition defines + + If you want to divide your device into parititions, then + enable the configuration switch CONFIG_MTD_PARITIONS and define + a paritioning scheme suitable to your board. + + +#define NUM_PARTITIONS 2 +static struct mtd_partition partition_info[] = { + { .name = "Flash partition 1", + .offset = 0, + .size = 8 * 1024 * 1024 }, + { .name = "Flash partition 2", + .offset = MTDPART_OFS_NEXT, + .size = MTDPART_SIZ_FULL }, +}; + + + + Hardware control function + + The hardware control function provides access to the + control pins of the NAND chip(s). + The access can be done by GPIO pins or by address lines. + If you use address lines, make sure that the timing + requirements are met. + + + GPIO based example + + +static void board_hwcontrol(struct mtd_info *mtd, int cmd) +{ + switch(cmd){ + case NAND_CTL_SETCLE: /* Set CLE pin high */ break; + case NAND_CTL_CLRCLE: /* Set CLE pin low */ break; + case NAND_CTL_SETALE: /* Set ALE pin high */ break; + case NAND_CTL_CLRALE: /* Set ALE pin low */ break; + case NAND_CTL_SETNCE: /* Set nCE pin low */ break; + case NAND_CTL_CLRNCE: /* Set nCE pin high */ break; + } +} + + + Address lines based example. It's assumed that the + nCE pin is driven by a chip select decoder. + + +static void board_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip *this = (struct nand_chip *) mtd->priv; + switch(cmd){ + case NAND_CTL_SETCLE: this->IO_ADDR_W |= CLE_ADRR_BIT; break; + case NAND_CTL_CLRCLE: this->IO_ADDR_W &= ~CLE_ADRR_BIT; break; + case NAND_CTL_SETALE: this->IO_ADDR_W |= ALE_ADRR_BIT; break; + case NAND_CTL_CLRALE: this->IO_ADDR_W &= ~ALE_ADRR_BIT; break; + } +} + + + + Device ready function + + If the hardware interface has the ready busy pin of the NAND chip connected to a + GPIO or other accesible I/O pin, this function is used to read back the state of the + pin. The function has no arguments and should return 0, if the device is busy (R/B pin + is low) and 1, if the device is ready (R/B pin is high). + If the hardware interface does not give access to the ready busy pin, then + the function must not be defined and the function pointer this->dev_ready is set to NULL. + + + + Init function + + The init function allocates memory and sets up all the board + specific parameters and function pointers. When everything + is set up nand_scan() is called. This function tries to + detect and identify then chip. If a chip is found all the + internal data fields are initialized accordingly. + The structure(s) have to be zeroed out first and then filled with the neccecary + information about the device. + + +int __init board_init (void) +{ + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!board_mtd) { + printk ("Unable to allocate NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + /* Initialize structures */ + memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip)); + + /* map physical adress */ + baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024); + if(!baseaddr){ + printk("Ioremap to access NAND chip failed\n"); + err = -EIO; + goto out_mtd; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (); + /* Link the private data with the MTD structure */ + board_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = baseaddr; + this->IO_ADDR_W = baseaddr; + /* Reference hardware control function */ + this->hwcontrol = board_hwcontrol; + /* Set command delay time, see datasheet for correct value */ + this->chip_delay = CHIP_DEPENDEND_COMMAND_DELAY; + /* Assign the device ready function, if available */ + this->dev_ready = board_dev_ready; + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existance of the device */ + if (nand_scan (board_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } + + add_mtd_partitions(board_mtd, partition_info, NUM_PARTITIONS); + goto out; + +out_ior: + iounmap((void *)baseaddr); +out_mtd: + kfree (board_mtd); +out: + return err; +} +module_init(board_init); + + + + Exit function + + The exit function is only neccecary if the driver is + compiled as a module. It releases all resources which + are held by the chip driver and unregisters the partitions + in the MTD layer. + + +#ifdef MODULE +static void __exit board_cleanup (void) +{ + /* Release resources, unregister device */ + nand_release (board_mtd); + + /* unmap physical adress */ + iounmap((void *)baseaddr); + + /* Free the MTD device structure */ + kfree (board_mtd); +} +module_exit(board_cleanup); +#endif + + + + + + Advanced board driver functions + + This chapter describes the advanced functionality of the NAND + driver. For a list of functions which can be overridden by the board + driver see the documentation of the nand_chip structure. + + + Multiple chip control + + The nand driver can control chip arrays. Therefor the + board driver must provide an own select_chip function. This + function must (de)select the requested chip. + The function pointer in the nand_chip structure must + be set before calling nand_scan(). The maxchip parameter + of nand_scan() defines the maximum number of chips to + scan for. Make sure that the select_chip function can + handle the requested number of chips. + + + The nand driver concatenates the chips to one virtual + chip and provides this virtual chip to the MTD layer. + + + Note: The driver can only handle linear chip arrays + of equally sized chips. There is no support for + parallel arrays which extend the buswidth. + + + GPIO based example + + +static void board_select_chip (struct mtd_info *mtd, int chip) +{ + /* Deselect all chips, set all nCE pins high */ + GPIO(BOARD_NAND_NCE) |= 0xff; + if (chip >= 0) + GPIO(BOARD_NAND_NCE) &= ~ (1 << chip); +} + + + Address lines based example. + Its assumed that the nCE pins are connected to an + address decoder. + + +static void board_select_chip (struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = (struct nand_chip *) mtd->priv; + + /* Deselect all chips */ + this->IO_ADDR_R &= ~BOARD_NAND_ADDR_MASK; + this->IO_ADDR_W &= ~BOARD_NAND_ADDR_MASK; + switch (chip) { + case 0: + this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIP0; + this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIP0; + break; + .... + case n: + this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIPn; + this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIPn; + break; + } +} + + + + Hardware ECC support + + Functions and constants + + The nand driver supports three different types of + hardware ECC. + + NAND_ECC_HW3_256 + Hardware ECC generator providing 3 bytes ECC per + 256 byte. + + NAND_ECC_HW3_512 + Hardware ECC generator providing 3 bytes ECC per + 512 byte. + + NAND_ECC_HW6_512 + Hardware ECC generator providing 6 bytes ECC per + 512 byte. + + NAND_ECC_HW8_512 + Hardware ECC generator providing 6 bytes ECC per + 512 byte. + + + If your hardware generator has a different functionality + add it at the appropriate place in nand_base.c + + + The board driver must provide following functions: + + enable_hwecc + This function is called before reading / writing to + the chip. Reset or initialize the hardware generator + in this function. The function is called with an + argument which let you distinguish between read + and write operations. + + calculate_ecc + This function is called after read / write from / to + the chip. Transfer the ECC from the hardware to + the buffer. If the option NAND_HWECC_SYNDROME is set + then the function is only called on write. See below. + + correct_data + In case of an ECC error this function is called for + error detection and correction. Return 1 respectively 2 + in case the error can be corrected. If the error is + not correctable return -1. If your hardware generator + matches the default algorithm of the nand_ecc software + generator then use the correction function provided + by nand_ecc instead of implementing duplicated code. + + + + + + Hardware ECC with syndrome calculation + + Many hardware ECC implementations provide Reed-Solomon + codes and calculate an error syndrome on read. The syndrome + must be converted to a standard Reed-Solomon syndrome + before calling the error correction code in the generic + Reed-Solomon library. + + + The ECC bytes must be placed immidiately after the data + bytes in order to make the syndrome generator work. This + is contrary to the usual layout used by software ECC. The + seperation of data and out of band area is not longer + possible. The nand driver code handles this layout and + the remaining free bytes in the oob area are managed by + the autoplacement code. Provide a matching oob-layout + in this case. See rts_from4.c and diskonchip.c for + implementation reference. In those cases we must also + use bad block tables on FLASH, because the ECC layout is + interferring with the bad block marker positions. + See bad block table support for details. + + + + + Bad block table support + + Most NAND chips mark the bad blocks at a defined + position in the spare area. Those blocks must + not be erased under any circumstances as the bad + block information would be lost. + It is possible to check the bad block mark each + time when the blocks are accessed by reading the + spare area of the first page in the block. This + is time consuming so a bad block table is used. + + + The nand driver supports various types of bad block + tables. + + Per device + The bad block table contains all bad block information + of the device which can consist of multiple chips. + + Per chip + A bad block table is used per chip and contains the + bad block information for this particular chip. + + Fixed offset + The bad block table is located at a fixed offset + in the chip (device). This applies to various + DiskOnChip devices. + + Automatic placed + The bad block table is automatically placed and + detected either at the end or at the beginning + of a chip (device) + + Mirrored tables + The bad block table is mirrored on the chip (device) to + allow updates of the bad block table without data loss. + + + + + nand_scan() calls the function nand_default_bbt(). + nand_default_bbt() selects appropriate default + bad block table desriptors depending on the chip information + which was retrieved by nand_scan(). + + + The standard policy is scanning the device for bad + blocks and build a ram based bad block table which + allows faster access than always checking the + bad block information on the flash chip itself. + + + Flash based tables + + It may be desired or neccecary to keep a bad block table in FLASH. + For AG-AND chips this is mandatory, as they have no factory marked + bad blocks. They have factory marked good blocks. The marker pattern + is erased when the block is erased to be reused. So in case of + powerloss before writing the pattern back to the chip this block + would be lost and added to the bad blocks. Therefor we scan the + chip(s) when we detect them the first time for good blocks and + store this information in a bad block table before erasing any + of the blocks. + + + The blocks in which the tables are stored are procteted against + accidental access by marking them bad in the memory bad block + table. The bad block table managment functions are allowed + to circumvernt this protection. + + + The simplest way to activate the FLASH based bad block table support + is to set the option NAND_USE_FLASH_BBT in the option field of + the nand chip structure before calling nand_scan(). For AG-AND + chips is this done by default. + This activates the default FLASH based bad block table functionality + of the NAND driver. The default bad block table options are + + Store bad block table per chip + Use 2 bits per block + Automatic placement at the end of the chip + Use mirrored tables with version numbers + Reserve 4 blocks at the end of the chip + + + + + User defined tables + + User defined tables are created by filling out a + nand_bbt_descr structure and storing the pointer in the + nand_chip structure member bbt_td before calling nand_scan(). + If a mirror table is neccecary a second structure must be + created and a pointer to this structure must be stored + in bbt_md inside the nand_chip structure. If the bbt_md + member is set to NULL then only the main table is used + and no scan for the mirrored table is performed. + + + The most important field in the nand_bbt_descr structure + is the options field. The options define most of the + table properties. Use the predefined constants from + nand.h to define the options. + + Number of bits per block + The supported number of bits is 1, 2, 4, 8. + Table per chip + Setting the constant NAND_BBT_PERCHIP selects that + a bad block table is managed for each chip in a chip array. + If this option is not set then a per device bad block table + is used. + Table location is absolute + Use the option constant NAND_BBT_ABSPAGE and + define the absolute page number where the bad block + table starts in the field pages. If you have selected bad block + tables per chip and you have a multi chip array then the start page + must be given for each chip in the chip array. Note: there is no scan + for a table ident pattern performed, so the fields + pattern, veroffs, offs, len can be left uninitialized + Table location is automatically detected + The table can either be located in the first or the last good + blocks of the chip (device). Set NAND_BBT_LASTBLOCK to place + the bad block table at the end of the chip (device). The + bad block tables are marked and identified by a pattern which + is stored in the spare area of the first page in the block which + holds the bad block table. Store a pointer to the pattern + in the pattern field. Further the length of the pattern has to be + stored in len and the offset in the spare area must be given + in the offs member of the nand_bbt_descr stucture. For mirrored + bad block tables different patterns are mandatory. + Table creation + Set the option NAND_BBT_CREATE to enable the table creation + if no table can be found during the scan. Usually this is done only + once if a new chip is found. + Table write support + Set the option NAND_BBT_WRITE to enable the table write support. + This allows the update of the bad block table(s) in case a block has + to be marked bad due to wear. The MTD interface function block_markbad + is calling the update function of the bad block table. If the write + support is enabled then the table is updated on FLASH. + + Note: Write support should only be enabled for mirrored tables with + version control. + + Table version control + Set the option NAND_BBT_VERSION to enable the table version control. + It's highly recommended to enable this for mirrored tables with write + support. It makes sure that the risk of loosing the bad block + table information is reduced to the loss of the information about the + one worn out block which should be marked bad. The version is stored in + 4 consecutive bytes in the spare area of the device. The position of + the version number is defined by the member veroffs in the bad block table + descriptor. + Save block contents on write + + In case that the block which holds the bad block table does contain + other useful information, set the option NAND_BBT_SAVECONTENT. When + the bad block table is written then the whole block is read the bad + block table is updated and the block is erased and everything is + written back. If this option is not set only the bad block table + is written and everything else in the block is ignored and erased. + + Number of reserved blocks + + For automatic placement some blocks must be reserved for + bad block table storage. The number of reserved blocks is defined + in the maxblocks member of the babd block table description structure. + Reserving 4 blocks for mirrored tables should be a reasonable number. + This also limits the number of blocks which are scanned for the bad + block table ident pattern. + + + + + + + Spare area (auto)placement + + The nand driver implements different possibilities for + placement of filesystem data in the spare area, + + Placement defined by fs driver + Automatic placement + + The default placement function is automatic placement. The + nand driver has built in default placement schemes for the + various chiptypes. If due to hardware ECC functionality the + default placement does not fit then the board driver can + provide a own placement scheme. + + + File system drivers can provide a own placement scheme which + is used instead of the default placement scheme. + + + Placement schemes are defined by a nand_oobinfo structure + +struct nand_oobinfo { + int useecc; + int eccbytes; + int eccpos[24]; + int oobfree[8][2]; +}; + + + useecc + The useecc member controls the ecc and placement function. The header + file include/mtd/mtd-abi.h contains constants to select ecc and + placement. MTD_NANDECC_OFF switches off the ecc complete. This is + not recommended and available for testing and diagnosis only. + MTD_NANDECC_PLACE selects caller defined placement, MTD_NANDECC_AUTOPLACE + selects automatic placement. + + eccbytes + The eccbytes member defines the number of ecc bytes per page. + + eccpos + The eccpos array holds the byte offsets in the spare area where + the ecc codes are placed. + + oobfree + The oobfree array defines the areas in the spare area which can be + used for automatic placement. The information is given in the format + {offset, size}. offset defines the start of the usable area, size the + length in bytes. More than one area can be defined. The list is terminated + by an {0, 0} entry. + + + + + Placement defined by fs driver + + The calling function provides a pointer to a nand_oobinfo + structure which defines the ecc placement. For writes the + caller must provide a spare area buffer along with the + data buffer. The spare area buffer size is (number of pages) * + (size of spare area). For reads the buffer size is + (number of pages) * ((size of spare area) + (number of ecc + steps per page) * sizeof (int)). The driver stores the + result of the ecc check for each tuple in the spare buffer. + The storage sequence is + + + <spare data page 0><ecc result 0>...<ecc result n> + + + ... + + + <spare data page n><ecc result 0>...<ecc result n> + + + This is a legacy mode used by YAFFS1. + + + If the spare area buffer is NULL then only the ECC placement is + done according to the given scheme in the nand_oobinfo structure. + + + + Automatic placement + + Automatic placement uses the built in defaults to place the + ecc bytes in the spare area. If filesystem data have to be stored / + read into the spare area then the calling function must provide a + buffer. The buffer size per page is determined by the oobfree array in + the nand_oobinfo structure. + + + If the spare area buffer is NULL then only the ECC placement is + done according to the default builtin scheme. + + + + User space placement selection + + All non ecc functions like mtd->read and mtd->write use an internal + structure, which can be set by an ioctl. This structure is preset + to the autoplacement default. + + ioctl (fd, MEMSETOOBSEL, oobsel); + + oobsel is a pointer to a user supplied structure of type + nand_oobconfig. The contents of this structure must match the + criteria of the filesystem, which will be used. See an example in utils/nandwrite.c. + + + + + Spare area autoplacement default schemes + + 256 byte pagesize + + +Offset +Content +Comment + + +0x00 +ECC byte 0 +Error correction code byte 0 + + +0x01 +ECC byte 1 +Error correction code byte 1 + + +0x02 +ECC byte 2 +Error correction code byte 2 + + +0x03 +Autoplace 0 + + + +0x04 +Autoplace 1 + + + +0x05 +Bad block marker +If any bit in this byte is zero, then this block is bad. +This applies only to the first page in a block. In the remaining +pages this byte is reserved + + +0x06 +Autoplace 2 + + + +0x07 +Autoplace 3 + + + + + + 512 byte pagesize + + +Offset +Content +Comment + + +0x00 +ECC byte 0 +Error correction code byte 0 of the lower 256 Byte data in +this page + + +0x01 +ECC byte 1 +Error correction code byte 1 of the lower 256 Bytes of data +in this page + + +0x02 +ECC byte 2 +Error correction code byte 2 of the lower 256 Bytes of data +in this page + + +0x03 +ECC byte 3 +Error correction code byte 0 of the upper 256 Bytes of data +in this page + + +0x04 +reserved +reserved + + +0x05 +Bad block marker +If any bit in this byte is zero, then this block is bad. +This applies only to the first page in a block. In the remaining +pages this byte is reserved + + +0x06 +ECC byte 4 +Error correction code byte 1 of the upper 256 Bytes of data +in this page + + +0x07 +ECC byte 5 +Error correction code byte 2 of the upper 256 Bytes of data +in this page + + +0x08 - 0x0F +Autoplace 0 - 7 + + + + + + 2048 byte pagesize + + +Offset +Content +Comment + + +0x00 +Bad block marker +If any bit in this byte is zero, then this block is bad. +This applies only to the first page in a block. In the remaining +pages this byte is reserved + + +0x01 +Reserved +Reserved + + +0x02-0x27 +Autoplace 0 - 37 + + + +0x28 +ECC byte 0 +Error correction code byte 0 of the first 256 Byte data in +this page + + +0x29 +ECC byte 1 +Error correction code byte 1 of the first 256 Bytes of data +in this page + + +0x2A +ECC byte 2 +Error correction code byte 2 of the first 256 Bytes data in +this page + + +0x2B +ECC byte 3 +Error correction code byte 0 of the second 256 Bytes of data +in this page + + +0x2C +ECC byte 4 +Error correction code byte 1 of the second 256 Bytes of data +in this page + + +0x2D +ECC byte 5 +Error correction code byte 2 of the second 256 Bytes of data +in this page + + +0x2E +ECC byte 6 +Error correction code byte 0 of the third 256 Bytes of data +in this page + + +0x2F +ECC byte 7 +Error correction code byte 1 of the third 256 Bytes of data +in this page + + +0x30 +ECC byte 8 +Error correction code byte 2 of the third 256 Bytes of data +in this page + + +0x31 +ECC byte 9 +Error correction code byte 0 of the fourth 256 Bytes of data +in this page + + +0x32 +ECC byte 10 +Error correction code byte 1 of the fourth 256 Bytes of data +in this page + + +0x33 +ECC byte 11 +Error correction code byte 2 of the fourth 256 Bytes of data +in this page + + +0x34 +ECC byte 12 +Error correction code byte 0 of the fifth 256 Bytes of data +in this page + + +0x35 +ECC byte 13 +Error correction code byte 1 of the fifth 256 Bytes of data +in this page + + +0x36 +ECC byte 14 +Error correction code byte 2 of the fifth 256 Bytes of data +in this page + + +0x37 +ECC byte 15 +Error correction code byte 0 of the sixt 256 Bytes of data +in this page + + +0x38 +ECC byte 16 +Error correction code byte 1 of the sixt 256 Bytes of data +in this page + + +0x39 +ECC byte 17 +Error correction code byte 2 of the sixt 256 Bytes of data +in this page + + +0x3A +ECC byte 18 +Error correction code byte 0 of the seventh 256 Bytes of +data in this page + + +0x3B +ECC byte 19 +Error correction code byte 1 of the seventh 256 Bytes of +data in this page + + +0x3C +ECC byte 20 +Error correction code byte 2 of the seventh 256 Bytes of +data in this page + + +0x3D +ECC byte 21 +Error correction code byte 0 of the eigth 256 Bytes of data +in this page + + +0x3E +ECC byte 22 +Error correction code byte 1 of the eigth 256 Bytes of data +in this page + + +0x3F +ECC byte 23 +Error correction code byte 2 of the eigth 256 Bytes of data +in this page + + + + + + + + Filesystem support + + The NAND driver provides all neccecary functions for a + filesystem via the MTD interface. + + + Filesystems must be aware of the NAND pecularities and + restrictions. One major restrictions of NAND Flash is, that you cannot + write as often as you want to a page. The consecutive writes to a page, + before erasing it again, are restricted to 1-3 writes, depending on the + manufacturers specifications. This applies similar to the spare area. + + + Therefor NAND aware filesystems must either write in page size chunks + or hold a writebuffer to collect smaller writes until they sum up to + pagesize. Available NAND aware filesystems: JFFS2, YAFFS. + + + The spare area usage to store filesystem data is controlled by + the spare area placement functionality which is described in one + of the earlier chapters. + + + + Tools + + The MTD project provides a couple of helpful tools to handle NAND Flash. + + flasherase, flasheraseall: Erase and format FLASH partitions + nandwrite: write filesystem images to NAND FLASH + nanddump: dump the contents of a NAND FLASH partitions + + + + These tools are aware of the NAND restrictions. Please use those tools + instead of complaining about errors which are caused by non NAND aware + access methods. + + + + + Constants + + This chapter describes the constants which might be relevant for a driver developer. + + + Chip option constants + + Constants for chip id table + + These constants are defined in nand.h. They are ored together to describe + the chip functionality. + +/* Chip can not auto increment pages */ +#define NAND_NO_AUTOINCR 0x00000001 +/* Buswitdh is 16 bit */ +#define NAND_BUSWIDTH_16 0x00000002 +/* Device supports partial programming without padding */ +#define NAND_NO_PADDING 0x00000004 +/* Chip has cache program function */ +#define NAND_CACHEPRG 0x00000008 +/* Chip has copy back function */ +#define NAND_COPYBACK 0x00000010 +/* AND Chip which has 4 banks and a confusing page / block + * assignment. See Renesas datasheet for further information */ +#define NAND_IS_AND 0x00000020 +/* Chip has a array of 4 pages which can be read without + * additional ready /busy waits */ +#define NAND_4PAGE_ARRAY 0x00000040 + + + + + Constants for runtime options + + These constants are defined in nand.h. They are ored together to describe + the functionality. + +/* Use a flash based bad block table. This option is parsed by the + * default bad block table function (nand_default_bbt). */ +#define NAND_USE_FLASH_BBT 0x00010000 +/* The hw ecc generator provides a syndrome instead a ecc value on read + * This can only work if we have the ecc bytes directly behind the + * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ +#define NAND_HWECC_SYNDROME 0x00020000 + + + + + + + ECC selection constants + + Use these constants to select the ECC algorithm. + +/* No ECC. Usage is not recommended ! */ +#define NAND_ECC_NONE 0 +/* Software ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_SOFT 1 +/* Hardware ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_HW3_256 2 +/* Hardware ECC 3 byte ECC per 512 Byte data */ +#define NAND_ECC_HW3_512 3 +/* Hardware ECC 6 byte ECC per 512 Byte data */ +#define NAND_ECC_HW6_512 4 +/* Hardware ECC 6 byte ECC per 512 Byte data */ +#define NAND_ECC_HW8_512 6 + + + + + + Hardware control related constants + + These constants describe the requested hardware access function when + the boardspecific hardware control function is called + +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 +/* Set write protection by setting WP to high. Not used! */ +#define NAND_CTL_SETWP 7 +/* Clear write protection by setting WP to low. Not used! */ +#define NAND_CTL_CLRWP 8 + + + + + + Bad block table related constants + + These constants describe the options used for bad block + table descriptors. + +/* Options for the bad block table descriptors */ + +/* The number of bits used per block in the bbt on the device */ +#define NAND_BBT_NRBITS_MSK 0x0000000F +#define NAND_BBT_1BIT 0x00000001 +#define NAND_BBT_2BIT 0x00000002 +#define NAND_BBT_4BIT 0x00000004 +#define NAND_BBT_8BIT 0x00000008 +/* The bad block table is in the last good block of the device */ +#define NAND_BBT_LASTBLOCK 0x00000010 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_ABSPAGE 0x00000020 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_SEARCH 0x00000040 +/* bbt is stored per chip on multichip devices */ +#define NAND_BBT_PERCHIP 0x00000080 +/* bbt has a version counter at offset veroffs */ +#define NAND_BBT_VERSION 0x00000100 +/* Create a bbt if none axists */ +#define NAND_BBT_CREATE 0x00000200 +/* Search good / bad pattern through all pages of a block */ +#define NAND_BBT_SCANALLPAGES 0x00000400 +/* Scan block empty during good / bad block scan */ +#define NAND_BBT_SCANEMPTY 0x00000800 +/* Write bbt if neccecary */ +#define NAND_BBT_WRITE 0x00001000 +/* Read and write back block contents when writing bbt */ +#define NAND_BBT_SAVECONTENT 0x00002000 + + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the NAND driver and might be relevant for a driver developer. Each + struct member has a short description which is marked with an [XXX] identifier. + See the chapter "Documentation hints" for an explanation. + +!Iinclude/linux/mtd/nand.h + + + + Public Functions Provided + + This chapter contains the autogenerated documentation of the NAND kernel API functions + which are exported. Each function has a short description which is marked with an [XXX] identifier. + See the chapter "Documentation hints" for an explanation. + +!Edrivers/mtd/nand/nand_base.c +!Edrivers/mtd/nand/nand_bbt.c +!Edrivers/mtd/nand/nand_ecc.c + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the NAND driver internal functions. + Each function has a short description which is marked with an [XXX] identifier. + See the chapter "Documentation hints" for an explanation. + The functions marked with [DEFAULT] might be relevant for a board driver developer. + +!Idrivers/mtd/nand/nand_base.c +!Idrivers/mtd/nand/nand_bbt.c +!Idrivers/mtd/nand/nand_ecc.c + + + + Credits + + The following people have contributed to the NAND driver: + + Steven J. Hillsjhill@realitydiluted.com + David Woodhousedwmw2@infradead.org + Thomas Gleixnertglx@linutronix.de + + A lot of users have provided bugfixes, improvements and helping hands for testing. + Thanks a lot. + + + The following people have contributed to this document: + + Thomas Gleixnertglx@linutronix.de + + + +
diff -Nur linux-2.6.9-intc1/drivers/i2c/algos/Kconfig linux-2.6.9-intc1-mih1/drivers/i2c/algos/Kconfig --- linux-2.6.9-intc1/drivers/i2c/algos/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/i2c/algos/Kconfig 2005-02-26 18:57:52.000000000 -0500 @@ -54,7 +54,15 @@ depends on 8xx && I2C config I2C_ALGOPXA - tristate "PXA I2C Algorithm" - depends on PXA27x && I2C + tristate "PXA I2C Algorithm" + depends on ARCH_PXA && I2C + help + This supports the use of the PXA I2C interface found on XScale + processors. Say Y if you have one of these. You should also say Y + for the PXA I2C peripheral driver support below. + + This support is also available as a module. If so, the module + will be called i2c-algo-pxa. + endmenu diff -Nur linux-2.6.9-intc1/drivers/i2c/busses/i2c-adap-pxa.c linux-2.6.9-intc1-mih1/drivers/i2c/busses/i2c-adap-pxa.c --- linux-2.6.9-intc1/drivers/i2c/busses/i2c-adap-pxa.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/i2c/busses/i2c-adap-pxa.c 2005-05-01 01:40:55.000000000 -0400 @@ -15,6 +15,7 @@ * Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] * Jan 2003: added limited signal handling [Kai-Uwe Bloem] * Sep 2004: ported to 2.6 kernel, Yan Yin + * Feb 2005: IRQ fixes [Matthias Ihmig] */ #include @@ -56,6 +57,8 @@ static wait_queue_head_t i2c_wait; static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte); +static spinlock_t i2c_pxa_irqlock = SPIN_LOCK_UNLOCKED; + /* place a byte in the transmit register */ static void i2c_pxa_write_byte(u8 value) { @@ -131,7 +134,7 @@ ICR &= ~(ICR_MA | ICR_START | ICR_STOP); } -static int i2c_pxa_wait_bus_not_busy( void) +static int i2c_pxa_wait_bus_not_busy(void) { int timeout = DEF_TIMEOUT; @@ -142,22 +145,22 @@ return (timeout<=0); } -static void i2c_pxa_wait_for_ite(void){ +static void i2c_pxa_wait_for_ite(void) +{ unsigned long flags; if (irq > 0) { - local_irq_save(flags); + spin_lock_irqsave(&i2c_pxa_irqlock, flags); if (i2c_pending == 0) { interruptible_sleep_on_timeout(&i2c_wait, I2C_SLEEP_TIMEOUT ); } i2c_pending = 0; - local_irq_restore(flags); - + spin_unlock_irqrestore(&i2c_pxa_irqlock, flags); } else { udelay(100); } } -static int i2c_pxa_wait_for_int( int wait_type) +static int i2c_pxa_wait_for_int(int wait_type) { int timeout = DEF_TIMEOUT; #ifdef DEBUG @@ -169,14 +172,14 @@ printk(KERN_INFO"i2c_pxa_wait_for_int: Transmit interrupt on enter\n"); #endif - if (wait_type == I2C_RECEIVE){ /* wait on receive */ + if (wait_type == I2C_RECEIVE) { /* wait on receive */ do { i2c_pxa_wait_for_ite(); } while (!(rx_finished) && timeout-- && !signal_pending(current)); #ifdef DEBUG - if (timeout<0){ + if (timeout<0) { if (tx_finished) printk("Error: i2c-algo-pxa.o: received a tx" " interrupt while waiting on a rx in wait_for_int"); @@ -189,7 +192,7 @@ } while (!(tx_finished) && timeout-- && !signal_pending(current)); #ifdef DEBUG - if (timeout<0){ + if (timeout<0) { if (rx_finished) printk("Error: i2c-algo-pxa.o: received a rx" " interrupt while waiting on a tx in wait_for_int"); @@ -202,9 +205,9 @@ tx_finished=0; rx_finished=0; - if (bus_error){ + if (bus_error) { bus_error=0; - if( i2c_debug > 2)printk("wait_for_int: error - no ack.\n"); + if (i2c_debug > 2)printk("wait_for_int: error - no ack.\n"); return BUS_ERROR; } @@ -264,32 +267,35 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { + unsigned long flags; int status, wakeup = 0; status = (ISR); - if (status & ISR_BED){ + if (status & ISR_BED) { (ISR) |= ISR_BED; bus_error=ISR_BED; wakeup = 1; } - if (status & ISR_ITE){ + if (status & ISR_ITE) { (ISR) |= ISR_ITE; tx_finished=ISR_ITE; wakeup = 1; } - if (status & ISR_IRF){ + if (status & ISR_IRF) { (ISR) |= ISR_IRF; rx_finished=ISR_IRF; wakeup = 1; } if (wakeup) { + spin_lock_irqsave(&i2c_pxa_irqlock, flags); i2c_pending = 1; + spin_unlock_irqrestore(&i2c_pxa_irqlock, flags); wake_up_interruptible(&i2c_wait); } return IRQ_HANDLED; } -static int i2c_pxa_resource_init( void) +static int i2c_pxa_resource_init(void) { /* Set I2C GPIO pins */ #ifdef CONFIG_PXA27x /* set i2c command & data GPIO */ @@ -301,7 +307,7 @@ if (request_irq(IRQ_I2C, &i2c_pxa_handler, SA_INTERRUPT, "I2C_PXA", 0) < 0) { irq = 0; - if( i2c_debug) + if (i2c_debug) printk(KERN_INFO "I2C: Failed to register I2C irq %i\n", IRQ_I2C); return -ENODEV; }else{ @@ -326,7 +332,7 @@ read_byte: i2c_pxa_read_byte, start: i2c_pxa_start, - repeat_start: i2c_pxa_repeat_start, + repeat_start: i2c_pxa_repeat_start, stop: i2c_pxa_stop, abort: i2c_pxa_abort, @@ -341,7 +347,7 @@ static struct i2c_adapter i2c_pxa_ops = { .owner = THIS_MODULE, - .id = I2C_ALGO_PXA, + .id = I2C_ALGO_PXA, .name = "PXA i2c adapter", .algo_data = &i2c_pxa_data, .retries = 2, @@ -353,7 +359,6 @@ static int __init i2c_adap_pxa_init(void) { if( i2c_pxa_resource_init() == 0) { - if (i2c_pxa_add_bus(&i2c_pxa_ops) < 0) { i2c_pxa_resource_release(); printk(KERN_INFO "I2C: Failed to add bus\n"); @@ -364,7 +369,6 @@ } printk(KERN_INFO "I2C: Successfully added bus\n"); - return 0; } @@ -372,12 +376,11 @@ { i2c_pxa_del_bus( &i2c_pxa_ops); i2c_pxa_resource_release(); - printk(KERN_INFO "I2C: Successfully removed bus\n"); } MODULE_DESCRIPTION("PXA27x i2c driver"); MODULE_LICENSE("GPL"); - + module_init(i2c_adap_pxa_init); module_exit(i2c_adap_pxa_exit); diff -Nur linux-2.6.9-intc1/drivers/i2c/busses/Kconfig linux-2.6.9-intc1-mih1/drivers/i2c/busses/Kconfig --- linux-2.6.9-intc1/drivers/i2c/busses/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/i2c/busses/Kconfig 2005-02-26 19:00:10.000000000 -0500 @@ -434,5 +434,11 @@ tristate "PXA I2C Adapter" depends on I2C && I2C_ALGOPXA help - + This supports the use of the PXA I2C interface found on the Intel + PXA 25x, PXA 26x and PXA 27x systems. Say Y if you have one of these. + You should also say Y for the PXA I2C peripheral driver support below. + + To compile this driver as a module, say M here: the + modules will be called i2c-pxa and i2c-algo-pxa. + endmenu diff -Nur linux-2.6.9-intc1/drivers/input/Kconfig linux-2.6.9-intc1-mih1/drivers/input/Kconfig --- linux-2.6.9-intc1/drivers/input/Kconfig 2004-10-18 17:53:13.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/input/Kconfig 2005-04-22 02:35:15.000000000 -0400 @@ -87,7 +87,7 @@ module will be called joydev. config INPUT_TSDEV - tristate "Touchscreen interface" + tristate "Compaq Touchscreen interface" depends on INPUT ---help--- Say Y here if you have an application that only can understand the @@ -109,6 +109,10 @@ depends on INPUT_TSDEV default "320" +config INPUT_TSLIBDEV + tristate "TSLIB Touchscreen interface" + depends on INPUT + config INPUT_EVDEV tristate "Event interface" depends on INPUT diff -Nur linux-2.6.9-intc1/drivers/input/keyboard/fastap_serial.c linux-2.6.9-intc1-mih1/drivers/input/keyboard/fastap_serial.c --- linux-2.6.9-intc1/drivers/input/keyboard/fastap_serial.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/input/keyboard/fastap_serial.c 2005-05-01 01:41:23.000000000 -0400 @@ -0,0 +1,406 @@ +/* + * Keyboard input driver for the Fastap keypad, interfaced through serial port (B9600, 8n1) + * and 8 direct keys, interfaced through PXA27x's DKPIN input (on PXA27x machine only) + * + * Fastap sample key: key down: 0x80 0x1b or 0x82 0x1b if previous key not yet released key up (key independent): 0x81 0x0 * + * Special mappings: + * Shift + No -> Fn key + * Alt + Cursor -> Home/End/PgUp/PgDown + * + * Usage: + * To connect keypad with serial port, use inputattach tool, + * available from http://linuxconsole.sourceforge.net/quick.html + * For compiling hints: http://agendawiki.com/cgi-bin/aw.pl?DatacompSerialKeyboard + * + * $ inputattach -ftap /dev/ttyS1 & + * + * Changelog: + * 2/13/2005 : initial version + * + * Copyright (c) 2005 Matthias Ihmig + * + */ + +/* + * 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 +#include + +#ifdef CONFIG_PXA27x +#include +#include +#include +#endif + +#define DRIVER_DESC "Fastap serial & DKIN keyboard driver v1.0" + +// defined in linux/serio.h: #define SERIO_FASTAP 0x29 + +// Key array sizes +#define KEYCODE_SIZE 96 +#define DIRECTKEY_SIZE 12 +#define REMAP_SIZE 20 + +#define DIRECTKEY_REMAP 4 +#define DIRECTKEY_OFFSET 4 + +// Remapped keys use a special lookup table +#define KMAPBASE 230 +#define KMAP(x) (KMAPBASE+x) + +// states +// KPRESS: next byte is scancode +// KRELEASE: next byte is 0x0 +#define KEMPTY 0 +#define KPRESS 1 +#define KRELEASE 2 + +// QT specific key events, see http://doc.trolltech.com/qtopia2.1/html/devices-keys-buttons.html for details +#define QT_MENU 65 /* Application menu key */ +#define QT_BACK 67 /* Accept/Close dialog key */ +#define QT_CALL 87 /* Start call */ +#define QT_HANG 88 /* End call */ +#define QT_SEL 116 /* Select menu option, checkbox, pressbutton */ +#define QT_CON1 124 /* First Context button */ +#define QT_CON2 125 /* Second Context button */ + +MODULE_AUTHOR("Matthias Ihmig "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +// Mappings for keys without modifier, - +static unsigned char fastap_keycode[KEYCODE_SIZE] = { +// 0x00-a 01-1 02-b 03-~! 04-c 05-2 06-d 07-|? 08-e 09-3 0A-f B C D E F + KEY_A, KEY_1, KEY_B, KMAP(0), KEY_C, KEY_2, KEY_D, KMAP(1), KEY_E, KEY_3, KEY_F, 0, 0, 0, 0, 0, +// 0x10-g 11-4 12-h 13-^$ 14-i 15-5 16-j 17-_& 18-k 19-6 1A-l B C D E F + KEY_G, KEY_4, KEY_H, KMAP(2), KEY_I, KEY_5, KEY_J, KMAP(3), KEY_K, KEY_6, KEY_L, 0, 0, 0, 0, 0, +// 0x20-m 21-7 22-n 23-+: 24-o 25-8 26-p 27--; 28-q 29-9 2A-r B C D E F + KEY_M, KEY_7, KEY_N, KMAP(4), KEY_O, KEY_8, KEY_P, KMAP(5), KEY_Q, KEY_9, KEY_R, 0, 0, 0, 0, 0, +// 0x30-s 31-* 32-t 33-"' 34-u 35-0 36-v 37-%, 38-w 39-# 3A-x B C D E F + KEY_S, KEY_KPASTERISK, KEY_T, KMAP(6), KEY_U, KEY_0, KEY_V, KMAP(7), KEY_W, KMAP(8), KEY_X, 0, 0, 0, 0, 0, +// 0x40-<. 41-=tab 42->@ 43-[( 44-y 45-del\bs 46-z 47-]) 48-{/ 49-enter 4A-)\ B C D E F + KMAP(9), KMAP(10), KMAP(11), KMAP(12), KEY_Y, KMAP(13), KEY_Z, KMAP(14), KMAP(15), KEY_ENTER, KMAP(16), 0, 0, 0, 0, 0, +// 0x50-home 51 52-arrow up 53 54-lspace 55 56-rspace 57 58-blue 59 5A-esc B C D E F + QT_MENU, 0, QT_SEL, 0, KEY_SPACE, 0, KEY_SPACE, 0, KEY_INSERT, 0, QT_BACK, 0, 0, 0, 0, 0, +}; + +// Mappings from Direct Key Input to scancodes +static unsigned char fastap_directkey[DIRECTKEY_SIZE] = { +// 0x00-top left, 01-top right, 02-green, 03-red, 04-left, 05-right, 06-up, 07-down + KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTCTRL, KEY_ESC, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, +// keys with Alt pressed, use scancode offset by DIRECTKEY_OFFSET + KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN +}; + +// key mappings when shift on keyboard IS NOT pressed: index: KMAP(#), +// { AT scancode, AT scancode needs shift (insert)}, always: need_shift_cancel=0 +static unsigned char fastap_remap_noshift[REMAP_SIZE][2] = { +/* 0-! 1-? 2-$ 3-& */ + { KEY_1, 1 }, { KEY_SLASH, 1 }, { KEY_4, 1 }, { KEY_7, 1 }, +/* 4-: 5-; 6-' 7-, */ + { KEY_SEMICOLON, 1 }, { KEY_SEMICOLON, 0 }, { KEY_APOSTROPHE, 0 }, { KEY_COMMA, 0 }, +/* 8-# 9-. 10-tab 11-@ */ + { KEY_3, 1}, { KEY_DOT, 0 }, { KEY_TAB, 0 }, { KEY_2, 1 }, +/* 12-( 13-bs 14-) 15-/ 16-\ */ + { KEY_9, 1}, { KEY_BACKSPACE, 0 }, { KEY_0, 1 }, { KEY_SLASH, 0 }, { KEY_BACKSLASH, 0 } +}; + +// key mappings when shift on keyboard IS pressed: index=KMAP(#) +// { AT scancode, AT scancode needs shift= do not cancel shift}, always: need_shift_insert=0 +static unsigned char fastap_remap_withshift[REMAP_SIZE][2] = { +// 0-~ 1-| 2-^ 3-_ + { KEY_GRAVE, 1 }, { KEY_BACKSLASH, 1 }, { KEY_6, 1 }, { KEY_MINUS, 1 }, +// 4-+ 5-- 6-" 7-% + { KEY_EQUAL, 1 }, { KEY_MINUS, 0 }, { KEY_APOSTROPHE, 1 }, { KEY_5, 1 }, +// 8-` 9-< 10-= 11-> + { KEY_GRAVE, 0 }, { KEY_COMMA, 1 }, { KEY_EQUAL, 0 }, { KEY_DOT, 1 }, +// 12-[ 13-del 14-] 15-{ 16-} + { KEY_LEFTBRACE, 0 }, { KEY_DELETE, 0 }, { KEY_RIGHTBRACE, 0 }, { KEY_LEFTBRACE, 1 }, { KEY_RIGHTBRACE, 1 } +}; + +static char *fastap_name = "Fastap Keyboard"; + +struct fkbd { + unsigned char keycode[KEYCODE_SIZE]; + unsigned char directkey[DIRECTKEY_SIZE]; + unsigned char remap_noshift[REMAP_SIZE][2]; + unsigned char remap_withshift[REMAP_SIZE][2]; + unsigned char lastkey; // last pressed and still down fastap-key, 0xFF if released + unsigned char status; // what to do next.. + unsigned char isKeyboardShift; // true if user holds shift on keyboard, controlled by DirectKey Shift + unsigned char isKeyboardCtrl; // true if user holds ctrl on keyboard, controlled by DirectKey Ctrl + unsigned char isKeyboardAlt; // true if user holds alt on keyboard, controlled by DirectKey Alt + unsigned char directkeystate; // last state of direct keys (DKIN mapping) + struct input_dev dev; + struct serio *serio; + char phys[32]; +}; + +struct fscancode { + unsigned char scancode; // AT scancode + char need_shift_insert; // key needs new shift inserted + char need_shift_cancel; // key needs previous shift cancelled +}; + +// Takes Fastap keycode and returns AT key with shift insertion hints +void fkbd_getcode(struct fkbd *fkbd, unsigned char keycode, struct fscancode *sc) +{ + unsigned char code; + + if (keycode >= 128) + printk (KERN_WARNING "fastap_serial: invalid key received\n"); + + code = fkbd->keycode[keycode]; + + if (code >= KMAP(0)) { // Fastap Remapping required + code -= KMAP(0); // "strip" KMAP + if (!fkbd->isKeyboardShift) { // just the key, not shift key is held + sc->scancode = fkbd->remap_noshift[code][0]; + sc->need_shift_insert = fkbd->remap_noshift[code][1]; + sc->need_shift_cancel = 0; + } else { + sc->scancode = fkbd->remap_withshift[code][0]; + sc->need_shift_insert = 0; + sc->need_shift_cancel = 1 - fkbd->remap_withshift[code][1]; + } + } else { // no fastap remapping + sc->need_shift_insert = 0; + sc->need_shift_cancel = 0; + if (fkbd->isKeyboardShift && (code >= KEY_1 && code <= KEY_0) ) { // shift+No -> Fn. Key + code += (KEY_F1 - KEY_1); // Convert to Fn. Key + sc->need_shift_cancel = 1; + } + sc->scancode = code; + } +} + +irqreturn_t fkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct fkbd *fkbd = serio->private; + struct fscancode scancode; + + if ((data & 0xFC) == 0x80) { // new key press or new key release following + if (data & 0x01) + fkbd->status = KRELEASE; + else + fkbd->status = KPRESS; + } else { // received scancode + if (fkbd->lastkey != 0xFF) { // old key still down -> release it + fkbd_getcode(fkbd, fkbd->lastkey, &scancode); + input_regs(&fkbd->dev, regs); + input_report_key(&fkbd->dev, scancode.scancode, 0); // release key + + if ( scancode.need_shift_insert ) + input_report_key(&fkbd->dev, KEY_LEFTSHIFT, 0); // cancel manually inserted shift + if ( scancode.need_shift_cancel ) + input_report_key(&fkbd->dev, KEY_LEFTSHIFT, 1); // restore shift + + input_sync(&fkbd->dev); + fkbd->lastkey = 0xFF; + } + fkbd_getcode(fkbd, data, &scancode); + if ((fkbd->status == KPRESS) && scancode.scancode) { // new and valid key arrived -> set it + input_regs(&fkbd->dev, regs); + if ( scancode.need_shift_insert ) // cancel manually inserted shift + input_report_key(&fkbd->dev, KEY_LEFTSHIFT, 1); // insert shift + if ( scancode.need_shift_cancel ) + input_report_key(&fkbd->dev, KEY_LEFTSHIFT, 0); // cancel shift + + input_report_key(&fkbd->dev, scancode.scancode, 1); // press new key + input_sync(&fkbd->dev); + fkbd->lastkey = data; + } + fkbd->status = KEMPTY; + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_PXA27x +static irqreturn_t fkbd_directkey_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char dkxor; + unsigned char code, val; + char doremap, i, p; + unsigned char newstate, kpc; + struct fkbd *fkbd = dev_id; + + newstate = (unsigned char) KPDK & 0xFF; + kpc = (unsigned char) KPC; + + dkxor = (newstate) ^ fkbd->directkeystate; + fkbd->directkeystate = newstate; + + input_regs(&fkbd->dev, regs); + for (i=0; i<8; i++) { + p = 1 << i; + if (dkxor & p) { + // if Alt is pressed for remapping and not a modifier key -> add remap offset + doremap = (fkbd->isKeyboardAlt) && (i >= DIRECTKEY_REMAP ); + code = fkbd->directkey[i + (doremap ? DIRECTKEY_OFFSET : 0)]; + val = ((fkbd->directkeystate & p) != 0 ? 1 : 0); + + if (doremap && val) // key pressed and remapping -> cancel Alt to make Home/End/PgUp/PgDown work + input_report_key(&fkbd->dev, KEY_LEFTALT, 0); + if (doremap && !val) // key released and remapping -> restore Alt + input_report_key(&fkbd->dev, KEY_LEFTALT, 1); + + input_report_key(&fkbd->dev, code, val); + + if (code == KEY_LEFTSHIFT) + fkbd->isKeyboardShift = val; + if (code == KEY_LEFTCTRL) + fkbd->isKeyboardCtrl = val; + if (code == KEY_LEFTALT) + fkbd->isKeyboardAlt = val; + } + } + input_sync(&fkbd->dev); + + return IRQ_HANDLED; +} +#endif + +void fkbd_connect(struct serio *serio, struct serio_driver *drv) +{ + struct fkbd *fkbd; + int i; + unsigned char c; + + if (serio->type != (SERIO_RS232 | SERIO_FASTAP)) + return; + + if (!(fkbd = kmalloc(sizeof(struct fkbd), GFP_KERNEL))) + return; + + memset(fkbd, 0, sizeof(struct fkbd)); + + fkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + fkbd->serio = serio; + + init_input_dev(&fkbd->dev); + + fkbd->dev.keycode = fkbd->keycode; + fkbd->dev.keycodesize = sizeof(unsigned char); + fkbd->dev.keycodemax = ARRAY_SIZE(fastap_keycode); + fkbd->dev.private = fkbd; + serio->private = fkbd; + + if (serio_open(serio, drv)) { + kfree(fkbd); + return; + } + + memcpy(fkbd->keycode, fastap_keycode, sizeof(fkbd->keycode)); + memcpy(fkbd->directkey, fastap_directkey, sizeof(fkbd->directkey)); + memcpy(fkbd->remap_noshift, fastap_remap_noshift, sizeof(fkbd->remap_noshift)); + memcpy(fkbd->remap_withshift, fastap_remap_withshift, sizeof(fkbd->remap_withshift)); + + for (i = 0; i < 128; i++) { + c = fkbd->keycode[i]; + if (c < KMAP(0)) + set_bit(c, fkbd->dev.keybit); + } + for (i = 0; i < ARRAY_SIZE(fkbd->directkey); i++) + set_bit(fkbd->directkey[i], fkbd->dev.keybit); + for (i = 0; i < ARRAY_SIZE(fkbd->remap_noshift); i++) + set_bit(fkbd->remap_noshift[i][0], fkbd->dev.keybit); + for (i = 0; i < ARRAY_SIZE(fkbd->remap_withshift); i++) + set_bit(fkbd->remap_withshift[i][0], fkbd->dev.keybit); + for (i = KEY_F1; i <= KEY_F10; i++) + set_bit(i, fkbd->dev.keybit); + + clear_bit(0, fkbd->dev.keybit); + + sprintf(fkbd->phys, "%s/input0", serio->phys); + + fkbd->dev.name = fastap_name; + fkbd->dev.phys = fkbd->phys; + fkbd->dev.id.bustype = BUS_RS232; + fkbd->dev.id.vendor = SERIO_FASTAP; + fkbd->dev.id.product = 0x0001; + fkbd->dev.id.version = 0x0001; + + input_register_device(&fkbd->dev); + + printk(KERN_INFO "fastap_serial: %s connected to %s\n", fastap_name, serio->phys); + +#ifdef CONFIG_PXA27x + // Configure DKIN Input pins, enable DKIN and request IRQ for Direct Keys + + pxa_set_cken(CKEN19_KEYPAD, 1); + KPKDI = 60; // debounce in ms + + // Configure Pins as DKIN + pxa_gpio_mode( 93 | GPIO_ALT_FN_1_IN ); + pxa_gpio_mode( 94 | GPIO_ALT_FN_1_IN ); + pxa_gpio_mode( 95 | GPIO_ALT_FN_1_IN ); + pxa_gpio_mode( 96 | GPIO_ALT_FN_1_IN ); + pxa_gpio_mode( 98 | GPIO_ALT_FN_1_IN ); + pxa_gpio_mode( 99 | GPIO_ALT_FN_1_IN ); + pxa_gpio_mode( 13 | GPIO_ALT_FN_2_IN ); + + set_irq_type(IRQ_KEYPAD, IRQT_BOTHEDGE); + + if (request_irq(IRQ_KEYPAD, fkbd_directkey_interrupt, SA_INTERRUPT, "Fastap Buttons", fkbd)) { + printk(KERN_ERR "fastap_serial: Can't allocate irq %d for direct key input.\n", IRQ_KEYPAD); + return; + } + KPC = (7 << 6) | KPC_DIE | KPC_DE ; // 7+1=8 input pins used + printk(KERN_INFO "fastap_serial: Direct Key input pins enabled\n"); +#endif +} + +void fkbd_disconnect(struct serio *serio) +{ + struct fkbd *fkbd = serio->private; +#ifdef CONFIG_PXA27x + KPC = 0 ; // Disable DKIN + pxa_set_cken(CKEN19_KEYPAD, 0); + free_irq(IRQ_KEYPAD, fkbd); +#endif + input_unregister_device(&fkbd->dev); + serio_close(serio); + kfree(fkbd); +} + +struct serio_driver fkbd_drv = { + .driver = { + .name = "fastapkbd", + }, + .description = DRIVER_DESC, + .interrupt = fkbd_interrupt, + .connect = fkbd_connect, + .disconnect = fkbd_disconnect, +}; + +int __init fkbd_init(void) +{ + serio_register_driver(&fkbd_drv); + return 0; +} + +void __exit fkbd_exit(void) +{ + serio_unregister_driver(&fkbd_drv); +} + +module_init(fkbd_init); +module_exit(fkbd_exit); diff -Nur linux-2.6.9-intc1/drivers/input/keyboard/Kconfig linux-2.6.9-intc1-mih1/drivers/input/keyboard/Kconfig --- linux-2.6.9-intc1/drivers/input/keyboard/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/input/keyboard/Kconfig 2005-04-25 23:54:17.000000000 -0400 @@ -74,6 +74,21 @@ help Say Y to support the mainstone board keypad. +config KEYBOARD_FASTAPSERIAL + tristate "Fastap serial keyboard" + depends on INPUT && INPUT_KEYBOARD + select SERIO + select SERIO_SERPORT + help + Say Y here if you have a FastAp keyboard on a serial port. The modifier keys + connect to Direkt Key Input (only used, when configured for PXA27x, ignored otherwise). + + Say Y if you are using the Intel Glencoe platform + + To compile this driver as a module, choose M here: the + module will be called fastap_serial. + To attach the keyboard to ttyS1, use inputattach -ftap /dev/ttyS1 & + config KEYBOARD_NEWTON tristate "Newton keyboard" depends on INPUT && INPUT_KEYBOARD diff -Nur linux-2.6.9-intc1/drivers/input/keyboard/Makefile linux-2.6.9-intc1-mih1/drivers/input/keyboard/Makefile --- linux-2.6.9-intc1/drivers/input/keyboard/Makefile 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/input/keyboard/Makefile 2005-02-21 13:39:30.000000000 -0500 @@ -13,3 +13,4 @@ obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o obj-$(CONFIG_KEYPAD_MAINSTONE) += mainstone_kp.o +obj-$(CONFIG_KEYBOARD_FASTAPSERIAL) += fastap_serial.o diff -Nur linux-2.6.9-intc1/drivers/input/Makefile linux-2.6.9-intc1-mih1/drivers/input/Makefile --- linux-2.6.9-intc1/drivers/input/Makefile 2004-10-18 17:55:24.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/input/Makefile 2005-04-22 02:35:15.000000000 -0400 @@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o +obj-$(CONFIG_INPUT_TSLIBDEV) += tslibdev.o obj-$(CONFIG_INPUT_TSDEV) += tsdev.o obj-$(CONFIG_INPUT_POWER) += power.o obj-$(CONFIG_INPUT_EVBUG) += evbug.o diff -Nur linux-2.6.9-intc1/drivers/input/tslibdev.c linux-2.6.9-intc1-mih1/drivers/input/tslibdev.c --- linux-2.6.9-intc1/drivers/input/tslibdev.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/input/tslibdev.c 2005-04-25 17:39:28.000000000 -0400 @@ -0,0 +1,359 @@ +/* + * linux/drivers/input/tslibdev.c + * + * Copyright (C) 2002 Russell King + * + * From tsdev.c: + * + * Copyright (c) 2001 "Crazy" james Simmons + * + * Input driver to Touchscreen device driver module. + * + * Sponsored by Transvirtual Technology + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ucb1x00_dev { + int exist; + char name[16]; + wait_queue_head_t wait; + struct list_head list; + struct input_handle handle; + int x; + int y; + int pressure; +}; + +struct ts_event { + u16 pressure; + u16 x; + u16 y; + u16 pad; + struct timeval stamp; +}; + +#define TSDEV_BUFFER_SIZE 64 + +struct ucb1x00_list { + struct list_head list; + struct fasync_struct *fasync; + struct ucb1x00_dev *tsdev; + unsigned int head; + unsigned int tail; + struct ts_event event[TSDEV_BUFFER_SIZE]; +}; + +static struct input_handler ucb1x00_handler; +static struct ucb1x00_dev *ucb1x00_dev; + +static void ucb1x00_remove(struct ucb1x00_dev *tsdev); + + +static int ucb1x00_fasync(int fd, struct file *file, int on) +{ + struct ucb1x00_list *list = file->private_data; + int retval; + + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int ucb1x00_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_list *list; + int empty; + + if (!ucb1x00_dev || !ucb1x00_dev->exist) + return -ENODEV; + + printk(KERN_WARNING + "tslibdev: process %s (%d) uses obsolete tslib device\n", + current->comm, current->pid); + + list = kmalloc(sizeof(struct ucb1x00_list), GFP_KERNEL); + if (!list) + return -ENOMEM; + + memset(list, 0, sizeof(struct ucb1x00_list)); + + empty = list_empty(&ucb1x00_dev->list); + + list->tsdev = ucb1x00_dev; + list_add(&list->list, &list->tsdev->list); + + file->private_data = list; + + if (empty && list->tsdev->exist) + input_open_device(&list->tsdev->handle); + + return 0; +} + +static int ucb1x00_release(struct inode *inode, struct file *file) +{ + struct ucb1x00_list *list = file->private_data; + + ucb1x00_fasync(-1, file, 0); + + list_del(&list->list); + + ucb1x00_remove(list->tsdev); + + kfree(list); + + return 0; +} + +static ssize_t +ucb1x00_read(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct ucb1x00_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + add_wait_queue(&list->tsdev->wait, &wait); + + while (list->head == list->tail) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!list->tsdev->exist) { + retval = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->tsdev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail && count >= sizeof(struct ts_event)) { + if (copy_to_user(buffer, list->event + list->tail, + sizeof(struct ts_event))) + return retval ? retval : -EFAULT; + list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); + retval += sizeof(struct ts_event); + buffer += sizeof(struct ts_event); + count -= sizeof(struct ts_event); + } + return retval; +} + +/* No kernel lock - fine */ +static unsigned int ucb1x00_poll(struct file *file, poll_table * wait) +{ + struct ucb1x00_list *list = file->private_data; + + poll_wait(file, &list->tsdev->wait, wait); + if (list->head != list->tail || !list->tsdev->exist) + return POLLIN | POLLRDNORM; + return 0; +} + +static int +ucb1x00_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} + +struct file_operations ucb1x00_fops = { + .owner = THIS_MODULE, + .open = ucb1x00_open, + .release = ucb1x00_release, + .read = ucb1x00_read, + .poll = ucb1x00_poll, + .fasync = ucb1x00_fasync, + .ioctl = ucb1x00_ioctl, +}; + +/* + * The official UCB1x00 touchscreen is a miscdevice: + * 10 char Non-serial mice, misc features + * 14 = /dev/touchscreen/ucb1x00 UCB 1x00 touchscreen + */ +static struct miscdevice ucb1x00_ts_dev = { + .minor = 14, + .name = "touchscreen/ucb1x00", + .fops = &ucb1x00_fops, + .devfs_name = "touchscreen/ucb1x00", +}; + +static void ucb1x00_remove(struct ucb1x00_dev *tsdev) +{ + if (list_empty(&tsdev->list)) { + if (tsdev->exist) { + input_close_device(&tsdev->handle); + wake_up_interruptible(&tsdev->wait); + } else { + misc_deregister(&ucb1x00_ts_dev); + ucb1x00_dev = NULL; + kfree(tsdev); + } + } +} + + +static void +ucb1x00_event(struct input_handle *handle, unsigned int type, unsigned int code, + int value) +{ + struct ucb1x00_dev *tsdev = handle->private; + struct list_head *l; + + /* sorry, we only handle absolute stuff */ + if (type == EV_ABS) { + switch (code) { + case ABS_X: + tsdev->x = value; + break; + case ABS_Y: + tsdev->y = value; + break; + case ABS_PRESSURE: + tsdev->pressure = value; + break; + } + return; + } + + if (type != EV_SYN || code != SYN_REPORT) + return; + + list_for_each(l, &tsdev->list) { + struct ucb1x00_list *list = list_entry(l, struct ucb1x00_list, list); + list->event[list->head].pressure = tsdev->pressure; + if (tsdev->pressure) { + list->event[list->head].x = tsdev->x; + list->event[list->head].y = tsdev->y; + } else { + list->event[list->head].x = 0; + list->event[list->head].y = 0; + } + do_gettimeofday(&list->event[list->head].stamp); + list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + wake_up_interruptible(&tsdev->wait); +} + +static struct input_handle * +ucb1x00_connect(struct input_handler *handler, struct input_dev *dev, + struct input_device_id *id) +{ + struct ucb1x00_dev *tsdev; + + if (ucb1x00_dev) + return NULL; + + tsdev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL); + if (!tsdev) + return NULL; + + memset(tsdev, 0, sizeof(struct ucb1x00_dev)); + init_waitqueue_head(&tsdev->wait); + INIT_LIST_HEAD(&tsdev->list); + + ucb1x00_dev = tsdev; + + strncpy(tsdev->name, "ucb1x00-ts", sizeof(tsdev->name)); + //strncpy(tsdev->name, "wm97xx-ts", sizeof(tsdev->name)); + + tsdev->handle.dev = dev; + tsdev->handle.name = tsdev->name; + tsdev->handle.handler = handler; + tsdev->handle.private = tsdev; + + misc_register(&ucb1x00_ts_dev); + + tsdev->exist = 1; + + return &tsdev->handle; +} + +static void ucb1x00_disconnect(struct input_handle *handle) +{ + struct ucb1x00_dev *tsdev = handle->private; + + tsdev->exist = 0; + ucb1x00_remove(tsdev); +} + +static struct input_device_id ucb1x00_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_ABS) }, + .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) }, + }, + + {},/* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, ucb1x00_ids); + +static struct input_handler ucb1x00_handler = { + .event = ucb1x00_event, + .connect = ucb1x00_connect, + .disconnect = ucb1x00_disconnect, + .name = "touchscreen/ucb1x00", + .id_table = ucb1x00_ids, +}; + +static int __init ucb1x00_init(void) +{ + input_register_handler(&ucb1x00_handler); + printk(KERN_INFO "ts: UCB1x00 touchscreen protocol output\n"); + return 0; +} + +static void __exit ucb1x00_exit(void) +{ + input_unregister_handler(&ucb1x00_handler); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Input driver to UCB1x00 touchscreen converter"); diff -Nur linux-2.6.9-intc1/drivers/misc/glencoe_periph.c linux-2.6.9-intc1-mih1/drivers/misc/glencoe_periph.c --- linux-2.6.9-intc1/drivers/misc/glencoe_periph.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/glencoe_periph.c 2005-05-03 14:39:27.000000000 -0400 @@ -0,0 +1,284 @@ +/* + * glencoe_periph.c: adds entries to /proc fs to provide manual + * access&control to peripherals on Glencoe (LEDs, LCD backlight, + * card detection, ...) + * + * - write 1 to assert, 0 to de-assert pins + * + * Copyright (c) 2005 Matthias Ihmig + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MODULE_VERS "1.0" +#define MODULE_NAME "glencoe_periph" + +static struct proc_dir_entry *gem_dir, *gpio0, *usim, *mmc, *cf1, *cf2, *green, *blue, *bl, *lcd, *lcd_pwm; + +static int proc_read_gpio(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "%s\n", (GPIO_lev( (int)data) ? "1" : "0")); // active high signal +} +static int proc_read_ngpio(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "%s\n", (GPIO_lev( (int)data) ? "0" : "1")); // active low signal -> invert +} + +static int proc_write_gpio(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int len=0; + char value='0'; + + if(count >= 1) + len = 1; + else + return -EFAULT; + + if(copy_from_user(&value, buffer, len)) + return -EFAULT; + + if (value == '1') + GPIO_set( (int)data ); // assert pin (active high) + if (value == '0') + GPIO_clr( (int)data ); // deassert pin + return len; +} +static int proc_write_ngpio(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int len=0; + char value='0'; + + if(count >= 1) + len = 1; + else + return -EFAULT; + + if(copy_from_user(&value, buffer, len)) + return -EFAULT; + + if (value == '1') + GPIO_clr( (int)data ); // assert pin (active low) + if (value == '0') + GPIO_set( (int)data ); // deassert pin + return len; +} + +static int proc_read_pwm(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "current:%d max:%d\n", GLN_BACKLIGHT_PWM_PWDUTY, GLN_BACKLIGHT_PWM_PERVAL); +} + +static int proc_write_pwm(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int len=count, width; + char value[10]; + + if(count > 9) + len = 9; + else + len = count; + + if(copy_from_user(&value, buffer, len)) + return -EFAULT; + + if (sscanf(value, "%d", &width) != 1) + return -EINVAL; + + if ( (width >= 0) && (width <= GLN_BACKLIGHT_PWM_PERVAL) ) { + pxa_set_cken(GLN_BACKLIGHT_PWM_CKEN, 1); + GLN_BACKLIGHT_PWM_CTRL = 0; + GLN_BACKLIGHT_PWM_PWDUTY = width; + GLN_BACKLIGHT_PWM_PERVAL = 0x3ff; + } + return len; +} + +static int __init init_glencoe_periph(void) +{ + int rv = 0; + + // Initialize GPIO registers + pxa_gpio_mode(GLN_GPIO_GPIO0 | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nUSIM_CD_CPU | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nMMC_CARD_DET | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nGREEN_LED | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_nBLUE_LED | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_BL_PWR_ON | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_LCD_PWR_ON | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_nCF1_CARD_DET | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nCF2_CARD_DET | GPIO_IN); + pxa_gpio_mode(GLN_BACKLIGHT_PWM_MD); + + /* create directory */ + gem_dir = proc_mkdir("driver/" MODULE_NAME, NULL); + if(gem_dir == NULL) { + rv = -ENOMEM; goto out; + } + gem_dir->owner = THIS_MODULE; + + // input files + gpio0 = create_proc_read_entry("gpio0_state", 0444, gem_dir, proc_read_gpio, (void*)GLN_GPIO_GPIO0); + if(gpio0 == NULL) { + rv = -ENOMEM; goto no_gpio0; + } + gpio0->owner = THIS_MODULE; + + usim = create_proc_read_entry("sim_inserted", 0444, gem_dir, proc_read_ngpio, (void*)GLN_GPIO_nUSIM_CD_CPU); + if(usim == NULL) { + rv = -ENOMEM; goto no_usim; + } + usim->owner = THIS_MODULE; + + mmc = create_proc_read_entry("mmc_inserted", 0444, gem_dir, proc_read_ngpio, (void*)GLN_GPIO_nMMC_CARD_DET); + if(mmc == NULL) { + rv = -ENOMEM; goto no_mmc; + } + mmc->owner = THIS_MODULE; + + cf1 = create_proc_read_entry("cf1_inserted", 0444, gem_dir, proc_read_ngpio, (void*)GLN_GPIO_nCF1_CARD_DET); + if(cf1 == NULL) { + rv = -ENOMEM; goto no_cf1; + } + cf1->owner = THIS_MODULE; + + cf2 = create_proc_read_entry("cf2_inserted", 0444, gem_dir, proc_read_ngpio, (void*)GLN_GPIO_nCF2_CARD_DET); + if(cf2 == NULL) { + rv = -ENOMEM; goto no_cf2; + } + cf2->owner = THIS_MODULE; + + + // output files + green = create_proc_entry("green_led_on", 0644, gem_dir); + if(green == NULL) { + rv = -ENOMEM; + goto no_green; + } + green->data = (void*)GLN_GPIO_nGREEN_LED; + green->read_proc = proc_read_ngpio; + green->write_proc = proc_write_ngpio; + green->owner = THIS_MODULE; + + blue = create_proc_entry("blue_led_on", 0644, gem_dir); + if(blue == NULL) { + rv = -ENOMEM; + goto no_blue; + } + blue->data = (void*)GLN_GPIO_nBLUE_LED; + blue->read_proc = proc_read_ngpio; + blue->write_proc = proc_write_ngpio; + blue->owner = THIS_MODULE; + + bl = create_proc_entry("backlight_on", 0644, gem_dir); + if(bl == NULL) { + rv = -ENOMEM; + goto no_bl; + } + bl->data = (void*)GLN_GPIO_BL_PWR_ON; + bl->read_proc = proc_read_gpio; + bl->write_proc = proc_write_gpio; + bl->owner = THIS_MODULE; + + lcd = create_proc_entry("lcd_on", 0644, gem_dir); + if(lcd == NULL) { + rv = -ENOMEM; + goto no_lcd; + } + lcd->data = (void*)GLN_GPIO_LCD_PWR_ON; + lcd->read_proc = proc_read_gpio; + lcd->write_proc = proc_write_gpio; + lcd->owner = THIS_MODULE; + + lcd_pwm = create_proc_entry("lcd_pwm", 0644, gem_dir); + if(lcd_pwm == NULL) { + rv = -ENOMEM; + goto no_lcd_pwm; + } + lcd_pwm->data = NULL; + lcd_pwm->read_proc = proc_read_pwm; + lcd_pwm->write_proc = proc_write_pwm; + lcd_pwm->owner = THIS_MODULE; + + + /* everything OK */ + printk(KERN_INFO "%s %s proc interface initialised\n", + MODULE_NAME, MODULE_VERS); + return 0; + +no_lcd_pwm: + remove_proc_entry("lcd_on", gem_dir); +no_lcd: + remove_proc_entry("backlight_on", gem_dir); +no_bl: + remove_proc_entry("blue_led_on", gem_dir); +no_blue: + remove_proc_entry("green_led_on", gem_dir); +no_green: + + remove_proc_entry("cf2_inserted", gem_dir); +no_cf2: + remove_proc_entry("cf1_inserted", gem_dir); +no_cf1: + remove_proc_entry("mmc_inserted", gem_dir); +no_mmc: + remove_proc_entry("sim_inserted", gem_dir); +no_usim: + remove_proc_entry("gpio0_state", gem_dir); +no_gpio0: + remove_proc_entry(MODULE_NAME, NULL); +out: + return rv; +} + +static void __exit cleanup_glencoe_periph(void) +{ + remove_proc_entry("lcd_pwm", gem_dir); + remove_proc_entry("lcd_on", gem_dir); + remove_proc_entry("backlight_on", gem_dir); + remove_proc_entry("blue_led_on", gem_dir); + remove_proc_entry("green_led_on", gem_dir); + remove_proc_entry("cf2_inserted", gem_dir); + remove_proc_entry("cf1_inserted", gem_dir); + remove_proc_entry("mmc_inserted", gem_dir); + remove_proc_entry("sim_inserted", gem_dir); + remove_proc_entry("gpio0_state", gem_dir); + remove_proc_entry(MODULE_NAME, NULL); + + printk(KERN_INFO "%s %s interface removed\n", + MODULE_NAME, MODULE_VERS); +} + +module_init(init_glencoe_periph); +module_exit(cleanup_glencoe_periph); + +MODULE_AUTHOR("Matthias Ihmig "); +MODULE_DESCRIPTION("Glencoe GPIO peripherals /proc interface"); +MODULE_LICENSE("GPL"); + diff -Nur linux-2.6.9-intc1/drivers/misc/Kconfig linux-2.6.9-intc1-mih1/drivers/misc/Kconfig --- linux-2.6.9-intc1/drivers/misc/Kconfig 2004-10-18 17:53:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/misc/Kconfig 2005-05-03 14:38:17.000000000 -0400 @@ -29,5 +29,106 @@ If unsure, say N. +config XIRCOM_GEM + tristate "Xircom GEM Module support" + depends on ARCH_PXA + ---help--- + Say Y here if you have an Intel GEM GSM/GPRS module connected to your + PXA GPIOs and want to control it through the /proc/sys interface. + + Examples: + Power down: $ echo 1 > /proc/driver/xircom_gem/power_down + Wake up: $ echo 1 > /proc/driver/xircom_gem/wake_up + Reset: $ echo 1 > /proc/driver/xircom_gem/reset + + Get sleep status: $ cat /proc/driver/xircom_gem/asleep + +config GLENCOE_PERIPH + tristate "Intel Glencoe board manual /proc interface to control peripherals" + depends on ARCH_PXA + ---help--- + Say Y here if you want to access and control Glencoe through the /proc/sys interface. This includes LEDs, LCD and SIM/MMC/CompactFlash card detection + + Examples: + Input (query state, returns 1 when asserted): + $ cat /proc/driver/glencoe_periph/gpio0_state + $ cat /proc/driver/glencoe_periph/sim_inserted + $ cat /proc/driver/glencoe_periph/mmc_inserted + $ cat /proc/driver/glencoe_periph/cf1_inserted + $ cat /proc/driver/glencoe_periph/cf2_inserted + + Output (set state): + $ echo 1 > /proc/driver/glencoe_periph/green_led_on + $ echo 1 > /proc/driver/glencoe_periph/blue_led_on + $ echo 1 > /proc/driver/glencoe_periph/backlight_on + $ echo 1 > /proc/driver/glencoe_periph/lcd_on + + Set LCD backlight brightness (value between 0-1023): + $ echo 300 > /proc/driver/glencoe_periph/lcd_pwm + + Please note that setting the LEDs only works when the Timer and CPU LEDs + are deactivated (disable General setup->Timer and CPU usage LEDs) + endmenu +menu "Multimedia Capabilities Port drivers" + +config MCP + tristate "MCP layer" + +# Interface drivers +config MCP_SA1100 + tristate "Support SA1100 MCP interface" + depends on ARCH_SA1100 + select MCP + +# Chip drivers +config MCP_UCB1200 + tristate "Support for UCB1200 / UCB1300" + depends on MCP + +config MCP_UCB1200_AUDIO + tristate "Audio / Telephony interface support" + depends on MCP_UCB1200 && SOUND + +config MCP_UCB1200_TS + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + +config UCB1400_TS + tristate "UCB1400 Touchscreen support" + depends on ARCH_PXA + +config MCP_WM97XX + tristate "Support for WM97xx MCP interface" + depends on SND_AC97_CODEC + +config MCP_WM9713_TS + tristate "WM9713 Touchscreen interface support" + depends on MCP_WM97XX && INPUT + help + Say Y here if you have the wm9713 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wm9713-ts. + +config MCP_WM9705_TS + tristate "WM9705/12 Touchscreen interface support" + depends on MCP_WM97XX && INPUT + help + Say Y here if you have the wm9705/12 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wm9705-ts. + +config MCP_WM97XX_PXA + tristate "WM97xx PXA accelerated touch" + depends on MCP_WM97XX && ARCH_PXA + help + Say Y here for continuous mode touch on the PXA + +endmenu diff -Nur linux-2.6.9-intc1/drivers/misc/Makefile linux-2.6.9-intc1-mih1/drivers/misc/Makefile --- linux-2.6.9-intc1/drivers/misc/Makefile 2004-10-18 17:55:24.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/misc/Makefile 2005-04-22 02:35:45.000000000 -0400 @@ -4,3 +4,22 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ +obj-$(CONFIG_XIRCOM_GEM) += xircom_gem.o +obj-$(CONFIG_GLENCOE_PERIPH) += glencoe_periph.o + +obj-$(CONFIG_MCP) += mcp-core.o +obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o +obj-$(CONFIG_MCP_UCB1200_AUDIO) += ucb1x00-audio.o +obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o +obj-$(CONFIG_MCP_WM97XX) += wm97xx-core.o mcp-ac97.o +obj-$(CONFIG_MCP_WM9713_TS) += wm9713-ts.o +obj-$(CONFIG_MCP_WM9705_TS) += wm9705-ts.o +obj-$(CONFIG_MCP_WM97XX_PXA) += pxa-wm97xx-touch.o + +ifeq ($(CONFIG_SA1100_ASSABET),y) +obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o +endif + +obj-$(CONFIG_MCP_SA1100) += mcp-sa1100.o +ucb1400-core-y := ucb1x00-core.o mcp-ac97.o +obj-$(CONFIG_UCB1400_TS) += ucb1400-core.o ucb1x00-ts.o diff -Nur linux-2.6.9-intc1/drivers/misc/mcp-ac97.c linux-2.6.9-intc1-mih1/drivers/misc/mcp-ac97.c --- linux-2.6.9-intc1/drivers/misc/mcp-ac97.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/mcp-ac97.c 2005-05-02 17:40:07.000000000 -0400 @@ -0,0 +1,148 @@ +/* + * linux/drivers/misc/mcp-ac97.c + * + * Author: Nicolas Pitre + * Created: Jan 14, 2005 + * Copyright: (C) MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This module provides the minimum replacement for mcp-core.c allowing for + * the UCB1400 chip to be driven by the ucb1x00 driver over an AC97 link. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mcp.h" + + +unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) +{ + ac97_t *ac97 = mcp->me->platform_data; + return ac97->bus->ops->read(ac97, reg); +} + +void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + ac97_t *ac97 = mcp->me->platform_data; + ac97->bus->ops->write(ac97, reg, val); +} + +void mcp_enable(struct mcp *mcp) +{ +} + +void mcp_disable(struct mcp *mcp) +{ +} + +#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) + +static int mcp_probe(struct device *dev) +{ + struct mcp_driver *drv = to_mcp_driver(dev->driver); + struct mcp *mcp; + int ret; + + printk("mcp_probe: probing..\n"); + + ret = -ENOMEM; + mcp = kmalloc(sizeof(*mcp), GFP_KERNEL); + if (mcp) { + memset(mcp, 0, sizeof(*mcp)); + mcp->owner = THIS_MODULE; + mcp->reg_read = mcp_reg_read; + mcp->reg_write = mcp_reg_write; + mcp->enable = mcp_enable; + mcp->disable = mcp_disable; + mcp->me = dev; + mcp->attached_device = dev; + + ret = drv->probe(mcp); + printk("mcp_probe: drv->probe called..\n"); + if (ret) + kfree(mcp); + } + return ret; +} + +static int mcp_remove(struct device *dev) +{ + struct mcp_driver *drv = to_mcp_driver(dev->driver); + struct mcp *mcp = *(struct mcp **)dev_get_drvdata(dev); + + drv->remove(mcp); + kfree(mcp); + return 0; +} + +static int mcp_suspend(struct device *dev, u32 state, u32 level) +{ + struct mcp_driver *drv = to_mcp_driver(dev->driver); + struct mcp *mcp = dev_get_drvdata(dev); + int ret = 0; + + if (drv->suspend && level == SUSPEND_DISABLE) + ret = drv->suspend(mcp, state); + return ret; +} + +static int mcp_resume(struct device *dev, u32 level) +{ + struct mcp_driver *drv = to_mcp_driver(dev->driver); + struct mcp *mcp = dev_get_drvdata(dev); + int ret = 0; + + if (drv->resume && level == RESUME_ENABLE) + ret = drv->resume(mcp); + return ret; +} + +int mcp_driver_register(struct mcp_driver *mcpdrv) +{ +/* +#if defined( CONFIG_MCP_WM9713_TS ) || defined( CONFIG_MCP_WM9713_TS_MODULE ) + mcpdrv->drv.name = "WM9713"; +#elif defined( CONFIG_MCP_WM9705_TS ) || defined( CONFIG_MCP_WM9705_TS_MODULE ) + mcpdrv->drv.name = "WM97xx"; +#elif defined( CONFIG_UCB1400_TS ) || defined( CONFIG_UCB1400_TS_MODULE ) + mcpdrv->drv.name = "UCB1400"; +#else + #warning "Using default Touchpad driver: WM9713" + mcpdrv->drv.name = "WM9713"; +#endif +*/ + mcpdrv->drv.name = "WM9713"; + printk("mcp_driver_register: called\n"); + + mcpdrv->drv.bus = &ac97_bus_type; + mcpdrv->drv.probe = mcp_probe; + mcpdrv->drv.remove = mcp_remove; + mcpdrv->drv.suspend = mcp_suspend; + mcpdrv->drv.resume = mcp_resume; + return driver_register(&mcpdrv->drv); +} + +void mcp_driver_unregister(struct mcp_driver *mcpdrv) +{ + driver_unregister(&mcpdrv->drv); +} + +EXPORT_SYMBOL(mcp_reg_read); +EXPORT_SYMBOL(mcp_reg_write); +EXPORT_SYMBOL(mcp_enable); +EXPORT_SYMBOL(mcp_disable); +EXPORT_SYMBOL(mcp_driver_register); +EXPORT_SYMBOL(mcp_driver_unregister); + +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/mcp-core.c linux-2.6.9-intc1-mih1/drivers/misc/mcp-core.c --- linux-2.6.9-intc1/drivers/misc/mcp-core.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/mcp-core.c 2005-04-22 17:30:33.000000000 -0400 @@ -0,0 +1,253 @@ +/* + * linux/drivers/misc/mcp-core.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * Generic MCP (Multimedia Communications Port) layer. All MCP locking + * is solely held within this file. + */ +#include +#include +#include +#include +#include + +#include +#include + +#include "mcp.h" + +#define to_mcp(d) ((struct mcp *)(d)->platform_data) +#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) + +static int mcp_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static int mcp_bus_probe(struct device *dev) +{ + struct mcp *mcp = to_mcp(dev); + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + return drv->probe(mcp); +} + +static int mcp_bus_remove(struct device *dev) +{ + struct mcp *mcp = to_mcp(dev); + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + drv->remove(mcp); + return 0; +} + +static int mcp_bus_suspend(struct device *dev, u32 state) +{ + struct mcp *mcp = to_mcp(dev); + int ret = 0; + + if (dev->driver) { + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + ret = drv->suspend(mcp, state); + } + return ret; +} + +static int mcp_bus_resume(struct device *dev) +{ + struct mcp *mcp = to_mcp(dev); + int ret = 0; + + if (dev->driver) { + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + ret = drv->resume(mcp); + } + return ret; +} + +static struct bus_type mcp_bus_type = { + .name = "mcp", + .match = mcp_bus_match, + .suspend = mcp_bus_suspend, + .resume = mcp_bus_resume, +}; + +#ifdef CONFIG_ARCH_SA1100 +/** + * mcp_set_telecom_divisor - set the telecom divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the telecom divisor on the MCP interface. The resulting + * sample rate is SIBCLOCK/div. + */ +void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_telecom_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_set_audio_divisor - set the audio divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the audio divisor on the MCP interface. + */ +void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_audio_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} +#endif + +/** + * mcp_reg_write - write a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * @val: 16-bit data value + * + * Write a device register. The MCP interface must be enabled + * to prevent this function hanging. + */ +void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + mcp->reg_write(mcp, reg, val); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +/** + * mcp_reg_read - read a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * + * Read a device register and return its value. The MCP interface + * must be enabled to prevent this function hanging. + */ +unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&mcp->lock, flags); + val = mcp->reg_read(mcp, reg); + spin_unlock_irqrestore(&mcp->lock, flags); + + return val; +} + +/** + * mcp_enable - enable the MCP interface + * @mcp: MCP interface to enable + * + * Enable the MCP interface. Each call to mcp_enable will need + * a corresponding call to mcp_disable to disable the interface. + */ +void mcp_enable(struct mcp *mcp) +{ + spin_lock_irq(&mcp->lock); + if (mcp->use_count++ == 0) + mcp->enable(mcp); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_disable - disable the MCP interface + * @mcp: MCP interface to disable + * + * Disable the MCP interface. The MCP interface will only be + * disabled once the number of calls to mcp_enable matches the + * number of calls to mcp_disable. + */ +void mcp_disable(struct mcp *mcp) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + if (--mcp->use_count == 0) + mcp->disable(mcp); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +int mcp_host_register(struct mcp *mcp, struct device *parent) +{ + int ret; + struct device *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + memset(dev, 0, sizeof(*dev)); + dev->platform_data = mcp; + dev->parent = parent; + dev->bus = &mcp_bus_type; + dev->dma_mask = parent->dma_mask; + strcpy(dev->bus_id, "mcp0"); + mcp->attached_device = dev; + ret = device_register(dev); + if (ret) { + mcp->attached_device = NULL; + kfree(dev); + } + return ret; +} + +void mcp_host_unregister(struct mcp *mcp) +{ + device_unregister(mcp->attached_device); + kfree(mcp->attached_device); + mcp->attached_device = NULL; +} + +int mcp_driver_register(struct mcp_driver *mcpdrv) +{ + mcpdrv->drv.bus = &mcp_bus_type; + mcpdrv->drv.probe = mcp_bus_probe; + mcpdrv->drv.remove = mcp_bus_remove; + return driver_register(&mcpdrv->drv); +} + +void mcp_driver_unregister(struct mcp_driver *mcpdrv) +{ + driver_unregister(&mcpdrv->drv); +} + +static int __init mcp_init(void) +{ + return bus_register(&mcp_bus_type); +} + +static void __exit mcp_exit(void) +{ + bus_unregister(&mcp_bus_type); +} + +module_init(mcp_init); +module_exit(mcp_exit); + +#ifdef CONFIG_ARCH_SA1100 +EXPORT_SYMBOL(mcp_set_telecom_divisor); +EXPORT_SYMBOL(mcp_set_audio_divisor); +#endif +EXPORT_SYMBOL(mcp_reg_write); +EXPORT_SYMBOL(mcp_reg_read); +EXPORT_SYMBOL(mcp_enable); +EXPORT_SYMBOL(mcp_disable); +EXPORT_SYMBOL(mcp_host_register); +EXPORT_SYMBOL(mcp_host_unregister); +EXPORT_SYMBOL(mcp_driver_register); +EXPORT_SYMBOL(mcp_driver_unregister); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Core multimedia communications port driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/mcp.h linux-2.6.9-intc1-mih1/drivers/misc/mcp.h --- linux-2.6.9-intc1/drivers/misc/mcp.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/mcp.h 2005-04-22 02:35:25.000000000 -0400 @@ -0,0 +1,64 @@ +/* + * linux/drivers/misc/mcp.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef MCP_H +#define MCP_H + +#ifdef CONFIG_ARCH_SA1100 +#include +#endif + +struct mcp { + struct module *owner; + struct device *me; + spinlock_t lock; + int use_count; + unsigned int sclk_rate; +#ifdef CONFIG_ARCH_SA1100 + unsigned int rw_timeout; + dma_device_t dma_audio_rd; + dma_device_t dma_audio_wr; + dma_device_t dma_telco_rd; + dma_device_t dma_telco_wr; + void (*set_telecom_divisor)(struct mcp *, unsigned int); + void (*set_audio_divisor)(struct mcp *, unsigned int); +#endif + void (*reg_write)(struct mcp *, unsigned int, unsigned int); + unsigned int (*reg_read)(struct mcp *, unsigned int); + void (*enable)(struct mcp *); + void (*disable)(struct mcp *); + struct device *attached_device; +}; + +void mcp_set_telecom_divisor(struct mcp *, unsigned int); +void mcp_set_audio_divisor(struct mcp *, unsigned int); +void mcp_reg_write(struct mcp *, unsigned int, unsigned int); +unsigned int mcp_reg_read(struct mcp *, unsigned int); +void mcp_enable(struct mcp *); +void mcp_disable(struct mcp *); +#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) + +int mcp_host_register(struct mcp *, struct device *); +void mcp_host_unregister(struct mcp *); + +struct mcp_driver { + struct device_driver drv; + int (*probe)(struct mcp *); + void (*remove)(struct mcp *); + int (*suspend)(struct mcp *, u32); + int (*resume)(struct mcp *); +}; + +int mcp_driver_register(struct mcp_driver *); +void mcp_driver_unregister(struct mcp_driver *); + +#define mcp_get_drvdata(mcp) dev_get_drvdata((mcp)->attached_device) +#define mcp_set_drvdata(mcp,d) dev_set_drvdata((mcp)->attached_device, d) + +#endif diff -Nur linux-2.6.9-intc1/drivers/misc/mcp-sa1100.c linux-2.6.9-intc1-mih1/drivers/misc/mcp-sa1100.c --- linux-2.6.9-intc1/drivers/misc/mcp-sa1100.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/mcp-sa1100.c 2005-04-22 02:35:15.000000000 -0400 @@ -0,0 +1,275 @@ +/* + * linux/drivers/misc/mcp-sa1100.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * SA1100 MCP (Multimedia Communications Port) driver. + * + * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mcp.h" + +static void +mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x00007f00; + mccr0 |= divisor << 8; + Ser4MCCR0 = mccr0; +} + +static void +mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x0000007f; + mccr0 |= divisor; + Ser4MCCR0 = mccr0; +} + +/* + * Write data to the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static void +mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CWC) { + ret = 0; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: write timed out\n"); +} + +/* + * Read data from the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static unsigned int +mcp_sa1100_read(struct mcp *mcp, unsigned int reg) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Rd; + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CRC) { + ret = Ser4MCDR2 & 0xffff; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: read timed out\n"); + + return ret; +} + +static void mcp_sa1100_enable(struct mcp *mcp) +{ + Ser4MCSR = -1; + Ser4MCCR0 |= MCCR0_MCE; +} + +static void mcp_sa1100_disable(struct mcp *mcp) +{ + Ser4MCCR0 &= ~MCCR0_MCE; +} + +/* + * Our methods. + */ +static struct mcp mcp_sa1100 = { + .owner = THIS_MODULE, + .lock = SPIN_LOCK_UNLOCKED, + .sclk_rate = 11981000, + .dma_audio_rd = DMA_Ser4MCP0Rd, + .dma_audio_wr = DMA_Ser4MCP0Wr, + .dma_telco_rd = DMA_Ser4MCP1Rd, + .dma_telco_wr = DMA_Ser4MCP1Wr, + .set_telecom_divisor = mcp_sa1100_set_telecom_divisor, + .set_audio_divisor = mcp_sa1100_set_audio_divisor, + .reg_write = mcp_sa1100_write, + .reg_read = mcp_sa1100_read, + .enable = mcp_sa1100_enable, + .disable = mcp_sa1100_disable, +}; + +static int mcp_sa1100_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mcp *mcp = &mcp_sa1100; + int ret; + + if (!machine_is_adsbitsy() && !machine_is_assabet() && + !machine_is_cerf() && !machine_is_flexanet() && + !machine_is_freebird() && !machine_is_graphicsclient() && + !machine_is_graphicsmaster() && !machine_is_lart() && + !machine_is_omnimeter() && !machine_is_pfs168() && + !machine_is_shannon() && !machine_is_simpad() && + !machine_is_yopy()) + return -ENODEV; + + if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) + return -EBUSY; + + mcp->me = dev; + dev_set_drvdata(dev, mcp); + + if (machine_is_assabet()) { + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + Ser4MCSR = -1; + Ser4MCCR1 = 0; + Ser4MCCR0 = 0x00007f7f | MCCR0_ADM; + + /* + * Calculate the read/write timeout (us) from the bit clock + * rate. This is the period for 3 64-bit frames. Always + * round this time up. + */ + mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / + mcp->sclk_rate; + + ret = mcp_host_register(mcp, &pdev->dev); + if (ret != 0) { + release_mem_region(0x80060000, 0x60); + dev_set_drvdata(dev, NULL); + } + + return ret; +} + +static int mcp_sa1100_remove(struct device *dev) +{ + struct mcp *mcp = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + mcp_host_unregister(mcp); + release_mem_region(0x80060000, 0x60); + + return 0; +} + +struct mcp_sa1100_state { + u32 mccr0; + u32 mccr1; +}; + +static int mcp_sa1100_suspend(struct device *dev, u32 state, u32 level) +{ + struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state; + + if (!s) { + s = kmalloc(sizeof(struct mcp_sa1100_state), GFP_KERNEL); + dev->saved_state = (unsigned char *)s; + } + + if (s) { + s->mccr0 = Ser4MCCR0; + s->mccr1 = Ser4MCCR1; + } + + if (level == SUSPEND_DISABLE) + Ser4MCCR0 &= ~MCCR0_MCE; + return 0; +} + +static int mcp_sa1100_resume(struct device *dev, u32 level) +{ + struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state; + + if (s && level == RESUME_RESTORE_STATE) { + Ser4MCCR1 = s->mccr1; + Ser4MCCR0 = s->mccr0; + + dev->saved_state = NULL; + kfree(s); + } + return 0; +} + +/* + * The driver for the SA11x0 MCP port. + */ +static struct device_driver mcp_sa1100_driver = { + .name = "sa11x0-mcp", + .bus = &platform_bus_type, + .probe = mcp_sa1100_probe, + .remove = mcp_sa1100_remove, + .suspend = mcp_sa1100_suspend, + .resume = mcp_sa1100_resume, +}; + +/* + * This needs re-working + */ +static int __init mcp_sa1100_init(void) +{ + return driver_register(&mcp_sa1100_driver); +} + +static void __exit mcp_sa1100_exit(void) +{ + driver_unregister(&mcp_sa1100_driver); +} + +module_init(mcp_sa1100_init); +module_exit(mcp_sa1100_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/pxa-wm97xx-touch.c linux-2.6.9-intc1-mih1/drivers/misc/pxa-wm97xx-touch.c --- linux-2.6.9-intc1/drivers/misc/pxa-wm97xx-touch.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/pxa-wm97xx-touch.c 2005-04-22 02:35:45.000000000 -0400 @@ -0,0 +1,302 @@ +/* + * pxa-wm97xx-touch.c -- PXA Continuous Touch screen driver for + * Wolfson WM97xx AC97 Codecs. + * + * Copyright 2004 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * 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. + * + * Notes: + * This code uses an API exposed by ac97_plugin_wm97xx to capture touch + * data in a continuous manner on the Intel XScale archictecture + * + * Features: + * - codecs supported:- WM9705, WM9712, WM9713 + * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x + * + * Revision history + * 18th Aug 2004 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" + +#define TS_NAME "pxa_wm97xx_touch" +#define WM_TS_VERSION "0.5" + +#define WM97XX_PXA_SLOT 5 + +/* + * The WM97xx codecs can signal a pen down event by either sending an irq over + * the AC97 link (not wm9705) or by asserting a pen down GPIO to interrupt the + * OS. Set WM_PEN_DOWN_GPIO to 1 if you are using the dedicated GPIO to signal + * pen down events, otherwise set to 0 if you want to signal over the AC link. + */ +#define WM_PEN_DOWN_GPIO 1 + +/* + * Pen sampling frequency in continuous mode. + * + * This parameter needs an additional architecture-dependent driver + * such as pxa-ac97-wm97xx. + * + * The sampling frequency is given by a number in range 0-3: + * + * 0: 93.75Hz 1: 187.5Hz 2: 375Hz 3: 750Hz + */ +static int cont_rate = 1; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (0-3, default 2)"); + + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int = 1; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* flush AC97 slot 5 FIFO on pxa machines */ +#define PXA_FLUSH_FIFO \ + do { \ + int count = 0; \ + for (; count < 16; count++) MODR; \ + } while (0); + + +static int pxa_wm97xx_cont_read (struct input_dev* idev, int pen_down_time, int pressure) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int count = 0, find_x = 3; + + /* sometimes there is stale data in the FIFO just after a pen down */ + if (pen_down_time < 4 ) { + PXA_FLUSH_FIFO; + return RC_PENDOWN; + } + + /* find the first X and sync */ + while (find_x--) { + x = MODR; + if ((x & 0x7000) == WM97XX_ADCSEL_X) + break; + } + + /* are we now syncronised X,Y[,P],X,Y[,P],X... ? */ + if (!find_x) + return RC_PENDOWN; + + /* read Y [and pressure] */ + y = MODR & 0xffff; + if (pressure) + p = MODR & 0xffff; + +#ifdef CONFIG_PXA27x + while (MISR & (1 << 2)) { +#else + while (count < 16) { +#endif + + /* are samples valid */ + if ((x & 0x7000) != WM97XX_ADCSEL_X || + (y & 0x7000) != WM97XX_ADCSEL_Y || + (p & 0x7000) != WM97XX_ADCSEL_PRES) + goto next; + + /* is pen down pressure great enough ? */ + if (pressure && ((p & 0xfff) > pressure)) + goto next; + + /* coordinate is good */ + printk("x %x y %x p %x\n", x,y,p); + input_report_abs (idev, ABS_X, x & 0xfff); + input_report_abs (idev, ABS_Y, y & 0xfff); + input_report_abs (idev, ABS_PRESSURE, p & 0xfff); + input_sync (idev); + +next: + x = MODR; + y = MODR; + count += 2; + if (pressure) { + p = MODR; + count++; + } + } + + return RC_PENDOWN; +} + +#ifdef CONFIG_PXA27x +/* + * Sometimes the PXA27x cannot reset sticky codec GPIO's. In this case we + * also need to reset the GPIO in codec space. + */ +static int pxa27x_clear_sticky(int status) +{ + volatile u32 *reg_addr; + + reg_addr = (u32 *)&PAC_REG_BASE + (0x54 >> 1); + *reg_addr = (status & ~WM97XX_GPIO_13) << 1; + + udelay(50); + return 1; +} +#endif + + +/* + * PXA arch pen down handler. + */ +static int pxa_pen_handler(int status) +{ + u32 gsr; + +#ifdef CONFIG_PXA27x + pxa27x_clear_sticky(status); +#endif + + gsr = GSR; + GSR = gsr; +#ifdef CONFIG_PXA27x + /* Although we don't use those we still need to clear them + since they tend to spuriously trigger when MMC is used + (hardware bug? go figure)... */ + MISR = MISR_EOC; + PISR = PISR_EOC; + MCSR = MCSR_EOC; +#endif + return 0; +} + + +static int pxa_wm97xx_ts_add(struct class_device *dev) +{ + struct wm97xx *wm = classdev_to_wm97xx(dev); + + if (wm->wm97xx_codec == NULL) + return -ENODEV; + + /* codec specific irq config */ + if (pen_int) { + switch (wm->id & 0xffff) { + case WM9705_ID2: + wm97xx_set_pen_irq(wm, IRQ_GPIO(4)); + set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); + break; + case WM9712_ID2: + case WM9713_ID2: + /* enable pen down interrupt */ +#if WM_PEN_DOWN_GPIO + /* use the PEN_DOWN GPIO */ + if (wm97xx_set_pen_irq(wm, MAINSTONE_PEN_IRQ) == 0) + wm97xx_config_codec_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); + else + pen_int = 0; +#else + /* use the virtual PEN_DOWN GPIO to send irq over AC97 */ + if(wm97xx_set_codec_irq(wm, IRQ_AC97, 1) == 0) { + wm97xx_request_codec_event(wm, WM97XX_GPIO_13, pxa_pen_handler); + GCR |= GCR_GIE; + } else + pen_int = 0; +#endif + break; + default: + printk("pen down irq not supported on this device\n"); + pen_int = 0; + break; + } + } + + /* Go you big red fire engine */ + wm97xx_set_continuous_mode (wm, pxa_wm97xx_cont_read, 1, WM97XX_PXA_SLOT, cont_rate); + return 0; +} + +static void pxa_wm97xx_ts_remove(struct class_device *dev) +{ + struct wm97xx *wm = classdev_to_wm97xx(dev); + + /* codec specific deconfig */ + if (pen_int) { + switch (wm->id & 0xffff) { + case WM9705_ID2: + wm97xx_clear_pen_irq(wm); + break; + case WM9712_ID2: + case WM9713_ID2: + /* disable interrupt */ +#if WM_PEN_DOWN_GPIO + wm97xx_clear_pen_irq(wm); +#else + if (wm97xx_clear_codec_irq(wm) == 0) + GCR &= ~GCR_GIE; + wm97xx_free_codec_event(wm, WM97XX_GPIO_13); +#endif + break; + } + } + + wm97xx_set_continuous_mode (wm, NULL, 0, 0, 0); +} + + +static struct class_interface pxa_wm97xx_ts_interface = { + .add = pxa_wm97xx_ts_add, + .remove = pxa_wm97xx_ts_remove, +}; + +static int __init pxa_wm97xx_ts_init(void) +{ + return wm97xx_register_interface(&pxa_wm97xx_ts_interface); +} + +static void __exit pxa_wm97xx_ts_exit(void) +{ + wm97xx_unregister_interface(&pxa_wm97xx_ts_interface); +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("AC97 continuous touch driver for PXA25x,PXA26x and PXA27x"); +MODULE_LICENSE("GPL"); + +module_init(pxa_wm97xx_ts_init); +module_exit(pxa_wm97xx_ts_exit); diff -Nur linux-2.6.9-intc1/drivers/misc/ucb1x00-assabet.c linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-assabet.c --- linux-2.6.9-intc1/drivers/misc/ucb1x00-assabet.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-assabet.c 2005-04-22 02:35:15.000000000 -0400 @@ -0,0 +1,73 @@ +/* + * linux/drivers/misc/ucb1x00-assabet.c + * + * Copyright (C) 2001-2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * We handle the machine-specific bits of the UCB1x00 driver here. + */ +#include +#include +#include +#include +#include + +#include + +#include "ucb1x00.h" + +#define UCB1X00_ATTR(name,input)\ +static ssize_t name##_show(struct class_device *dev, char *buf) \ +{ \ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \ + int val; \ + ucb1x00_adc_enable(ucb); \ + val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \ + ucb1x00_adc_disable(ucb); \ + return sprintf(buf, "%d\n", val); \ +} \ +static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL) + +UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1); +UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD0); +UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2); + +static int ucb1x00_assabet_add(struct class_device *dev) +{ + class_device_create_file(dev, &class_device_attr_vbatt); + class_device_create_file(dev, &class_device_attr_vcharger); + class_device_create_file(dev, &class_device_attr_batt_temp); + return 0; +} + +static void ucb1x00_assabet_remove(struct class_device *dev) +{ + class_device_remove_file(dev, &class_device_attr_batt_temp); + class_device_remove_file(dev, &class_device_attr_vcharger); + class_device_remove_file(dev, &class_device_attr_vbatt); +} + +static struct class_interface ucb1x00_assabet_interface = { + .add = ucb1x00_assabet_add, + .remove = ucb1x00_assabet_remove, +}; + +static int __init ucb1x00_assabet_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_assabet_interface); +} + +static void __exit ucb1x00_assabet_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_assabet_interface); +} + +module_init(ucb1x00_assabet_init); +module_exit(ucb1x00_assabet_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Assabet noddy testing only example ADC driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/ucb1x00-audio.c linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-audio.c --- linux-2.6.9-intc1/drivers/misc/ucb1x00-audio.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-audio.c 2005-04-22 02:35:15.000000000 -0400 @@ -0,0 +1,439 @@ +/* + * linux/drivers/misc/ucb1x00-audio.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ucb1x00.h" + +#include "../sound/oss/sa1100-audio.h" + +#define MAGIC 0x41544154 + +struct ucb1x00_audio { + struct file_operations fops; + struct file_operations mops; + struct ucb1x00 *ucb; + audio_stream_t output_stream; + audio_stream_t input_stream; + audio_state_t state; + unsigned int rate; + int dev_id; + int mix_id; + unsigned int daa_oh_bit; + unsigned int telecom; + unsigned int magic; + unsigned int ctrl_a; + unsigned int ctrl_b; + + /* mixer info */ + unsigned int mod_cnt; + unsigned short output_level; + unsigned short input_level; +}; + +#define REC_MASK (SOUND_MASK_VOLUME | SOUND_MASK_MIC) +#define DEV_MASK REC_MASK + +static int +ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + unsigned int val, gain; + int ret = 0; + + ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops); + + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "UCB1x00", sizeof(mi.id)); + strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name)); + mi.modify_counter = ucba->mod_cnt; + return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0; + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + unsigned int left, right; + + ret = get_user(val, (unsigned int *)arg); + if (ret) + goto out; + + left = val & 255; + right = val >> 8; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + gain = (left + right) / 2; + + ret = -EINVAL; + if (!ucba->telecom) { + switch(_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + ucba->output_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_b = (ucba->ctrl_b & 0xff00) | + ((gain * 31) / 100); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, + ucba->ctrl_b); + ret = 0; + break; + + case SOUND_MIXER_MIC: + ucba->input_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_a = (ucba->ctrl_a & 0x7f) | + (((gain * 31) / 100) << 7); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, + ucba->ctrl_a); + ret = 0; + break; + } + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + val = ucba->output_level; + break; + + case SOUND_MIXER_MIC: + val = ucba->input_level; + break; + + case SOUND_MIXER_RECSRC: + case SOUND_MIXER_RECMASK: + val = ucba->telecom ? 0 : REC_MASK; + break; + + case SOUND_MIXER_DEVMASK: + val = ucba->telecom ? 0 : DEV_MASK; + break; + + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + default: + val = 0; + ret = -EINVAL; + break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } + out: + return ret; +} + +static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate) +{ + unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32; + unsigned int div; + + div = (div_rate + (rate / 2)) / rate; + if (div < 6) + div = 6; + if (div > 127) + div = 127; + + ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div; + + if (ucba->telecom) { + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0); + ucb1x00_set_telecom_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b); + } else { + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0); + ucb1x00_set_audio_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b); + } + + ucba->rate = div_rate / div; + + return ucba->rate; +} + +static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba) +{ + return ucba->rate; +} + +static void ucb1x00_audio_startup(void *data) +{ + struct ucb1x00_audio *ucba = data; + + ucb1x00_enable(ucba->ucb); + ucb1x00_audio_setrate(ucba, ucba->rate); + + ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA); + + /* + * Take off-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit); +} + +static void ucb1x00_audio_shutdown(void *data) +{ + struct ucb1x00_audio *ucba = data; + + /* + * Place on-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0); + + ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0); + ucb1x00_disable(ucba->ucb); +} + +static int +ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + int val, ret = 0; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + /* + * Make sure we have our magic number + */ + if (ucba->magic != MAGIC) + return -ENODEV; + + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + if (val != 0) + return -EINVAL; + val = 0; + break; + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + val = 1; + break; + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + val = ucb1x00_audio_setrate(ucba, val); + break; + + case SOUND_PCM_READ_RATE: + val = ucb1x00_audio_getrate(ucba); + break; + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + val = AFMT_S16_LE; + break; + + default: + return ucb1x00_mixer_ioctl(inode, file, cmd, arg); + } + + return put_user(val, (int *)arg); +} + +static int ucb1x00_audio_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_audio *ucba; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + return sa1100_audio_attach(inode, file, &ucba->state); +} + +static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb) +{ + struct ucb1x00_audio *ucba; + + ucba = kmalloc(sizeof(*ucba), GFP_KERNEL); + if (ucba) { + memset(ucba, 0, sizeof(*ucba)); + + ucba->magic = MAGIC; + ucba->ucb = ucb; + ucba->fops.owner = THIS_MODULE; + ucba->fops.open = ucb1x00_audio_open; + ucba->mops.owner = THIS_MODULE; + ucba->mops.ioctl = ucb1x00_mixer_ioctl; + ucba->state.output_stream = &ucba->output_stream; + ucba->state.input_stream = &ucba->input_stream; + ucba->state.data = ucba; + ucba->state.hw_init = ucb1x00_audio_startup; + ucba->state.hw_shutdown = ucb1x00_audio_shutdown; + ucba->state.client_ioctl = ucb1x00_audio_ioctl; + + /* There is a bug in the StrongARM causes corrupt MCP data to be sent to + * the codec when the FIFOs are empty and writes are made to the OS timer + * match register 0. To avoid this we must make sure that data is always + * sent to the codec. + */ + ucba->state.need_tx_for_rx = 1; + + init_MUTEX(&ucba->state.sem); + ucba->rate = 8000; + } + return ucba; +} + +static struct ucb1x00_audio *ucb1x00_audio_add_one(struct ucb1x00 *ucb, int telecom) +{ + struct ucb1x00_audio *a; + + a = ucb1x00_audio_alloc(ucb); + if (a) { + a->telecom = telecom; + + a->input_stream.dev = ucb->cdev.dev; + a->output_stream.dev = ucb->cdev.dev; + a->ctrl_a = 0; + + if (a->telecom) { + a->input_stream.dma_dev = ucb->mcp->dma_telco_rd; + a->input_stream.id = "UCB1x00 telco in"; + a->output_stream.dma_dev = ucb->mcp->dma_telco_wr; + a->output_stream.id = "UCB1x00 telco out"; + a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA; +#if 0 + a->daa_oh_bit = UCB_IO_8; + + ucb1x00_enable(ucb); + ucb1x00_io_write(ucb, a->daa_oh_bit, 0); + ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit); + ucb1x00_disable(ucb); +#endif + } else { + a->input_stream.dma_dev = ucb->mcp->dma_audio_rd; + a->input_stream.id = "UCB1x00 audio in"; + a->output_stream.dma_dev = ucb->mcp->dma_audio_wr; + a->output_stream.id = "UCB1x00 audio out"; + a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA; + } + + a->dev_id = register_sound_dsp(&a->fops, -1); + a->mix_id = register_sound_mixer(&a->mops, -1); + + printk("Sound: UCB1x00 %s: dsp id %d mixer id %d\n", + a->telecom ? "telecom" : "audio", + a->dev_id, a->mix_id); + } + + return a; +} + +static void ucb1x00_audio_remove_one(struct ucb1x00_audio *a) +{ + unregister_sound_dsp(a->dev_id); + unregister_sound_mixer(a->mix_id); + kfree(a); +} + +static int ucb1x00_audio_add(struct class_device *cdev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev); + + if (ucb->cdev.dev == NULL || ucb->cdev.dev->dma_mask == NULL) + return -ENXIO; + + ucb->audio_data = ucb1x00_audio_add_one(ucb, 0); + ucb->telecom_data = ucb1x00_audio_add_one(ucb, 1); + + return 0; +} + +static void ucb1x00_audio_remove(struct class_device *cdev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev); + + ucb1x00_audio_remove_one(ucb->audio_data); + ucb1x00_audio_remove_one(ucb->telecom_data); +} + +#if 0 //def CONFIG_PM +static int ucb1x00_audio_suspend(struct ucb1x00 *ucb, u32 state) +{ + struct ucb1x00_audio *a; + + a = ucb->audio_data; + sa1100_audio_suspend(&a->state, state); + a = ucb->telecom_data; + sa1100_audio_suspend(&a->state, state); + + return 0; +} + +static int ucb1x00_audio_resume(struct ucb1x00 *ucb) +{ + struct ucb1x00_audio *a; + + a = ucb->audio_data; + sa1100_audio_resume(&a->state); + a = ucb->telecom_data; + sa1100_audio_resume(&a->state); + + return 0; +} +#else +#define ucb1x00_audio_suspend NULL +#define ucb1x00_audio_resume NULL +#endif + +static struct class_interface ucb1x00_audio_interface = { + .add = ucb1x00_audio_add, + .remove = ucb1x00_audio_remove, +}; + +static int __init ucb1x00_audio_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_audio_interface); +} + +static void __exit ucb1x00_audio_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_audio_interface); +} + +module_init(ucb1x00_audio_init); +module_exit(ucb1x00_audio_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 telecom/audio driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/ucb1x00-core.c linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-core.c --- linux-2.6.9-intc1/drivers/misc/ucb1x00-core.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-core.c 2005-04-22 02:35:25.000000000 -0400 @@ -0,0 +1,638 @@ +/* + * linux/drivers/misc/ucb1x00-core.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * The UCB1x00 core driver provides basic services for handling IO, + * the ADC, interrupts, and accessing registers. It is designed + * such that everything goes through this layer, thereby providing + * a consistent locking methodology, as well as allowing the drivers + * to be used on other non-MCP-enabled hardware platforms. + * + * Note that all locks are private to this file. Nothing else may + * touch them. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ucb1x00.h" + +/** + * ucb1x00_io_set_dir - set IO direction + * @ucb: UCB1x00 structure describing chip + * @in: bitfield of IO pins to be set as inputs + * @out: bitfield of IO pins to be set as outputs + * + * Set the IO direction of the ten general purpose IO pins on + * the UCB1x00 chip. The @in bitfield has priority over the + * @out bitfield, in that if you specify a pin as both input + * and output, it will end up as an input. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir |= out; + ucb->io_dir &= ~in; + spin_unlock_irqrestore(&ucb->io_lock, flags); + + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); +} + +/** + * ucb1x00_io_write - set or clear IO outputs + * @ucb: UCB1x00 structure describing chip + * @set: bitfield of IO pins to set to logic '1' + * @clear: bitfield of IO pins to set to logic '0' + * + * Set the IO output state of the specified IO pins. The value + * is retained if the pins are subsequently configured as inputs. + * The @clear bitfield has priority over the @set bitfield - + * outputs will be cleared. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_out |= set; + ucb->io_out &= ~clear; + spin_unlock_irqrestore(&ucb->io_lock, flags); + + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); +} + +/** + * ucb1x00_io_read - read the current state of the IO pins + * @ucb: UCB1x00 structure describing chip + * + * Return a bitfield describing the logic state of the ten + * general purpose IO pins. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function does not take any semaphores or spinlocks. + */ +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) +{ + return ucb1x00_reg_read(ucb, UCB_IO_DATA); +} + +/* + * UCB1300 data sheet says we must: + * 1. enable ADC => 5us (including reference startup time) + * 2. select input => 51*tsibclk => 4.3us + * 3. start conversion => 102*tsibclk => 8.5us + * (tsibclk = 1/11981000) + * Period between SIB 128-bit frames = 10.7us + */ + +/** + * ucb1x00_adc_enable - enable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. + * Any code wishing to use the ADC converter must call this + * function prior to using it. + * + * This function takes the ADC semaphore to prevent two or more + * concurrent uses, and therefore may sleep. As a result, it + * can only be called from process context, not interrupt + * context. + * + * You should release the ADC as soon as possible using + * ucb1x00_adc_disable. + */ +void ucb1x00_adc_enable(struct ucb1x00 *ucb) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); +} + +/** + * ucb1x00_adc_read - read the specified ADC channel + * @ucb: UCB1x00 structure describing chip + * @adc_channel: ADC channel mask + * @sync: wait for syncronisation pulse. + * + * Start an ADC conversion and wait for the result. Note that + * synchronised ADC conversions (via the ADCSYNC pin) must wait + * until the trigger is asserted and the conversion is finished. + * + * This function currently spins waiting for the conversion to + * complete (2 frames max without sync). + * + * If called for a synchronised ADC conversion, it may sleep + * with the ADC semaphore held. + */ +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) +{ + unsigned int val; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + /* yield to other processes */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + return UCB_ADC_DAT(val); +} + +/** + * ucb1x00_adc_disable - disable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Disable the ADC converter and release the ADC semaphore. + */ +void ucb1x00_adc_disable(struct ucb1x00 *ucb) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); + ucb1x00_disable(ucb); + + up(&ucb->adc_sem); +} + +/* + * UCB1x00 Interrupt handling. + * + * The UCB1x00 can generate interrupts when the SIBCLK is stopped. + * Since we need to read an internal register, we must re-enable + * SIBCLK to talk to the chip. We leave the clock running until + * we have finished processing all interrupts from the chip. + */ +static irqreturn_t ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + struct ucb1x00 *ucb = devid; + struct ucb1x00_irq *irq; + unsigned int isr, i; + + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) + if (isr & 1 && irq->fn) + irq->fn(i, irq->devid); + ucb1x00_disable(ucb); + + return IRQ_HANDLED; +} + +/* + * A restriction with interrupts exists when using the ucb1400, as + * the codec read/write routines may sleep while waiting for codec + * access completion and uses semaphores for access control to the + * AC97 bus. A complete codec read cycle could take anywhere from + * 60 to 100uSec so we *definitely* don't want to spin inside the + * interrupt handler waiting for codec access. So, we handle the + * interrupt by scheduling a RT kernel thread to run in process + * context instead of interrupt context. + */ +static int ucb1x00_thread(void *_ucb) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + struct ucb1x00 *ucb = _ucb; + + ucb->irq_task = tsk; + daemonize("kUCB1x00d"); + allow_signal(SIGKILL); + tsk->policy = SCHED_FIFO; + tsk->rt_priority = 1; + + add_wait_queue(&ucb->irq_wait, &wait); + set_task_state(tsk, TASK_INTERRUPTIBLE); + complete(&ucb->complete); + + for (;;) { + if (signal_pending(tsk)) + break; + schedule(); + ucb1x00_irq(-1, ucb, NULL); + set_task_state(tsk, TASK_INTERRUPTIBLE); + enable_irq(ucb->irq); + } + + remove_wait_queue(&ucb->irq_wait, &wait); + ucb->irq_task = NULL; + complete_and_exit(&ucb->complete, 0); +} + +static irqreturn_t ucb1x00_threaded_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + struct ucb1x00 *ucb = devid; + if (irqnr == ucb->irq) { + disable_irq(ucb->irq); + wake_up(&ucb->irq_wait); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/** + * ucb1x00_hook_irq - hook a UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @fn: function to call when interrupt is triggered + * @devid: device id to pass to interrupt handler + * + * Hook the specified interrupt. You can only register one handler + * for each interrupt source. The interrupt source is not enabled + * by this function; use ucb1x00_enable_irq instead. + * + * Interrupt handlers will be called with other interrupts enabled. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -EBUSY if the interrupt has already been hooked + */ +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +{ + struct ucb1x00_irq *irq; + int ret = -EINVAL; + + if (idx < 16) { + irq = ucb->irq_handler + idx; + ret = -EBUSY; + + spin_lock_irq(&ucb->lock); + if (irq->fn == NULL) { + irq->devid = devid; + irq->fn = fn; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + } + return ret; +} + +/** + * ucb1x00_enable_irq - enable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @edges: interrupt edges to enable + * + * Enable the specified interrupt to trigger on %UCB_RISING, + * %UCB_FALLING or both edges. The interrupt should have been + * hooked by ucb1x00_hook_irq. + */ +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + if (edges & UCB_RISING) + ucb->irq_ris_enbl |= 1 << idx; + if (edges & UCB_FALLING) + ucb->irq_fal_enbl |= 1 << idx; + spin_unlock_irqrestore(&ucb->lock, flags); + + ucb1x00_enable(ucb); + + /* This prevents spurious interrupts on the UCB1400 */ + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + + ucb1x00_disable(ucb); + } +} + +/** + * ucb1x00_disable_irq - disable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @edges: interrupt edges to disable + * + * Disable the specified interrupt triggering on the specified + * (%UCB_RISING, %UCB_FALLING or both) edges. + */ +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + if (edges & UCB_RISING) + ucb->irq_ris_enbl &= ~(1 << idx); + if (edges & UCB_FALLING) + ucb->irq_fal_enbl &= ~(1 << idx); + spin_unlock_irqrestore(&ucb->lock, flags); + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + ucb1x00_disable(ucb); + } +} + +/** + * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @devid: device id. + * + * Disable the interrupt source and remove the handler. devid must + * match the devid passed when hooking the interrupt. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -ENOENT if devid does not match + */ +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +{ + struct ucb1x00_irq *irq; + int ret; + + if (idx >= 16) + goto bad; + + irq = ucb->irq_handler + idx; + ret = -ENOENT; + + spin_lock_irq(&ucb->lock); + if (irq->devid == devid) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb->irq_fal_enbl &= ~(1 << idx); + + irq->fn = NULL; + irq->devid = NULL; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + ucb1x00_disable(ucb); + + return ret; + +bad: + printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); + return -EINVAL; +} + +/* + * Try to probe our interrupt, rather than relying on lots of + * hard-coded machine dependencies. For reference, the expected + * IRQ mappings are: + * + * Machine Default IRQ + * adsbitsy IRQ_GPCIN4 + * cerf IRQ_GPIO_UCB1200_IRQ + * flexanet IRQ_GPIO_GUI + * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ + * graphicsclient ADS_EXT_IRQ(8) + * graphicsmaster ADS_EXT_IRQ(8) + * lart LART_IRQ_UCB1200 + * omnimeter IRQ_GPIO23 + * pfs168 IRQ_GPIO_UCB1300_IRQ + * simpad IRQ_GPIO_UCB1300_IRQ + * shannon SHANNON_IRQ_GPIO_IRQ_CODEC + * yopy IRQ_GPIO_UCB1200_IRQ + */ +static int ucb1x00_detect_irq(struct ucb1x00 *ucb) +{ + unsigned long mask; + + mask = probe_irq_on(); + if (!mask) + return NO_IRQ; + + /* + * Enable the ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Cause an ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + + /* + * Wait for the conversion to complete. + */ + while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); + ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); + + /* + * Disable and clear interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); + ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Read triggered interrupt. + */ + return probe_irq_off(mask); +} + +static int ucb1x00_probe(struct mcp *mcp) +{ + struct ucb1x00 *ucb; + unsigned int id; + int ret = -ENODEV; + + mcp_enable(mcp); + id = mcp_reg_read(mcp, UCB_ID); + + if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_1400) { + printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); + goto err_disable; + } + + ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL); + ret = -ENOMEM; + if (!ucb) + goto err_disable; + + memset(ucb, 0, sizeof(struct ucb1x00)); + + ucb->cdev.class = &ucb1x00_class; + ucb->cdev.dev = mcp->attached_device; + strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id)); + + spin_lock_init(&ucb->lock); + spin_lock_init(&ucb->io_lock); + sema_init(&ucb->adc_sem, 1); + init_waitqueue_head(&ucb->irq_wait); + + ucb->mcp = mcp; + ucb->id = id; + /* distinguish between UCB1400 revs 1B and 2A */ + if (id == UCB_ID_1400 && mcp_reg_read(mcp, 0x00) == 0x002a) + ucb->id = UCB_ID_1400_BUGGY; + + ucb->irq = ucb1x00_detect_irq(ucb); + if (ucb->irq == NO_IRQ) { + printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); + ret = -ENODEV; + goto err_free; + } + + ret = request_irq(ucb->irq, + id != UCB_ID_1400 ? ucb1x00_irq : ucb1x00_threaded_irq, + 0, "UCB1x00", ucb); + if (ret) { + printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", + ucb->irq, ret); + goto err_free; + } + + set_irq_type(ucb->irq, IRQT_RISING); + mcp_set_drvdata(mcp, ucb); + + ret = class_device_register(&ucb->cdev); + + if (!ret && id == UCB_ID_1400) { + init_completion(&ucb->complete); + ret = kernel_thread(ucb1x00_thread, ucb, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&ucb->complete); + ret = 0; + } + } + + if (ret) { + free_irq(ucb->irq, ucb); + err_free: + kfree(ucb); + } + err_disable: + mcp_disable(mcp); + return ret; +} + +static void ucb1x00_remove(struct mcp *mcp) +{ + struct ucb1x00 *ucb = mcp_get_drvdata(mcp); + + class_device_unregister(&ucb->cdev); + if (ucb->id == UCB_ID_1400 || ucb->id == UCB_ID_1400_BUGGY) { + send_sig(SIGKILL, ucb->irq_task, 1); + wait_for_completion(&ucb->complete); + } + free_irq(ucb->irq, ucb); +} + +static void ucb1x00_release(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + kfree(ucb); +} + +static struct class ucb1x00_class = { + .name = "ucb1x00", + .release = ucb1x00_release, +}; + +int ucb1x00_register_interface(struct class_interface *intf) +{ + intf->class = &ucb1x00_class; + return class_interface_register(intf); +} + +void ucb1x00_unregister_interface(struct class_interface *intf) +{ + class_interface_unregister(intf); +} + +static struct mcp_driver ucb1x00_driver = { + .drv = { + .name = "ucb1x00", + }, + .probe = ucb1x00_probe, + .remove = ucb1x00_remove, +}; + +static int __init ucb1x00_init(void) +{ + int ret = class_register(&ucb1x00_class); + if (ret == 0) { + ret = mcp_driver_register(&ucb1x00_driver); + if (ret) + class_unregister(&ucb1x00_class); + } + return ret; +} + +static void __exit ucb1x00_exit(void) +{ + mcp_driver_unregister(&ucb1x00_driver); + class_unregister(&ucb1x00_class); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +EXPORT_SYMBOL(ucb1x00_class); + +EXPORT_SYMBOL(ucb1x00_io_set_dir); +EXPORT_SYMBOL(ucb1x00_io_write); +EXPORT_SYMBOL(ucb1x00_io_read); + +EXPORT_SYMBOL(ucb1x00_adc_enable); +EXPORT_SYMBOL(ucb1x00_adc_read); +EXPORT_SYMBOL(ucb1x00_adc_disable); + +EXPORT_SYMBOL(ucb1x00_hook_irq); +EXPORT_SYMBOL(ucb1x00_free_irq); +EXPORT_SYMBOL(ucb1x00_enable_irq); +EXPORT_SYMBOL(ucb1x00_disable_irq); + +EXPORT_SYMBOL(ucb1x00_register_interface); +EXPORT_SYMBOL(ucb1x00_unregister_interface); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 core driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/ucb1x00.h linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00.h --- linux-2.6.9-intc1/drivers/misc/ucb1x00.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00.h 2005-04-22 02:35:25.000000000 -0400 @@ -0,0 +1,270 @@ +/* + * linux/drivers/misc/ucb1x00.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef UCB1200_H +#define UCB1200_H + +#ifdef CONFIG_ARCH_PXA + +/* ucb1400 aclink register mappings: */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_TS_CR 0x64 +#define UCB_ADC_CR 0x66 +#define UCB_ADC_DATA 0x68 +#define UCB_ID 0x7e /* 7c is mfr id, 7e part id (from aclink spec) */ + +#define UCB_ADC_DAT(x) ((x) & 0x3ff) + +#else + +/* ucb1x00 SIB register mappings: */ + +#define UCB_IO_DATA 0x00 +#define UCB_IO_DIR 0x01 +#define UCB_IE_RIS 0x02 +#define UCB_IE_FAL 0x03 +#define UCB_IE_STATUS 0x04 +#define UCB_IE_CLEAR 0x04 +#define UCB_TC_A 0x05 +#define UCB_TC_B 0x06 +#define UCB_AC_A 0x07 +#define UCB_AC_B 0x08 +#define UCB_TS_CR 0x09 +#define UCB_ADC_CR 0x0a +#define UCB_ADC_DATA 0x0b +#define UCB_ID 0x0c +#define UCB_MODE 0x0d + +#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) + +#endif + + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DAT_VAL (1 << 15) + +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +#include "mcp.h" + +struct ucb1x00_irq { + void *devid; + void (*fn)(int, void *); +}; + +extern struct class ucb1x00_class; + +struct ucb1x00 { + struct mcp *mcp; /* this needs to be first */ + spinlock_t lock; + unsigned int irq; + struct semaphore adc_sem; + spinlock_t io_lock; + wait_queue_head_t irq_wait; + struct completion complete; + struct task_struct *irq_task; + u16 id; + u16 io_dir; + u16 io_out; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + struct ucb1x00_irq irq_handler[16]; + struct class_device cdev; + void *audio_data; + void *telecom_data; + void *ts_data; +}; + +#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev) + +int ucb1x00_register_interface(struct class_interface *intf); +void ucb1x00_unregister_interface(struct class_interface *intf); + +/** + * ucb1x00_clkrate - return the UCB1x00 SIB clock rate + * @ucb: UCB1x00 structure describing chip + * + * Return the SIB clock rate in Hz. + */ +static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +{ + return mcp_get_sclk_rate(ucb->mcp); +} + +/** + * ucb1x00_enable - enable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Enable the SIB clock. This can be called multiple times. + */ +static inline void ucb1x00_enable(struct ucb1x00 *ucb) +{ + mcp_enable(ucb->mcp); +} + +/** + * ucb1x00_disable - disable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Disable the SIB clock. The SIB clock will only be disabled + * when the number of ucb1x00_enable calls match the number of + * ucb1x00_disable calls. + */ +static inline void ucb1x00_disable(struct ucb1x00 *ucb) +{ + mcp_disable(ucb->mcp); +} + +/** + * ucb1x00_reg_write - write a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * @val: UCB1x00 16-bit value to write + * + * Write the UCB1x00 register @reg with value @val. The SIB + * clock must be running for this function to return. + */ +static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +{ + mcp_reg_write(ucb->mcp, reg, val); +} + +/** + * ucb1x00_reg_read - read a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * + * Read the UCB1x00 register @reg and return its value. The SIB + * clock must be running for this function to return. + */ +static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +{ + return mcp_reg_read(ucb->mcp, reg); +} +/** + * ucb1x00_set_audio_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_audio_divisor(ucb->mcp, div); +} + +/** + * ucb1x00_set_telecom_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_telecom_divisor(ucb->mcp, div); +} + +#define ucb1x00_get() NULL + +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); + +#define UCB_NOSYNC (0) +#define UCB_SYNC (1) + +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +void ucb1x00_adc_enable(struct ucb1x00 *ucb); +void ucb1x00_adc_disable(struct ucb1x00 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); + +#endif diff -Nur linux-2.6.9-intc1/drivers/misc/ucb1x00-ts.c linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-ts.c --- linux-2.6.9-intc1/drivers/misc/ucb1x00-ts.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/ucb1x00-ts.c 2005-04-22 02:35:25.000000000 -0400 @@ -0,0 +1,453 @@ +/* + * linux/drivers/misc/ucb1x00-ts.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 21-Jan-2002 : + * + * Added support for synchronous A/D mode. This mode is useful to + * avoid noise induced in the touchpanel by the LCD, provided that + * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ucb1x00.h" + + +struct ucb1x00_ts { + struct input_dev idev; + struct ucb1x00 *ucb; + + struct completion irq_wait; + struct semaphore sem; + struct completion init_exit; + struct task_struct *rtask; + int use_count; + u16 x_res; + u16 y_res; + + int restart:1; + int adcsync:1; +}; + +static int adcsync = UCB_NOSYNC; + +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +{ + input_report_abs(&ts->idev, ABS_X, x); + input_report_abs(&ts->idev, ABS_Y, y); + input_report_abs(&ts->idev, ABS_PRESSURE, pressure); + input_sync(&ts->idev); +} + +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +{ + input_report_abs(&ts->idev, ABS_PRESSURE, 0); + input_sync(&ts->idev); +} + +/* + * Switch to interrupt mode. + */ +static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) +{ + int val = UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT; + if (ts->ucb->id == UCB_ID_1400_BUGGY) + val &= ~(UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, val); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); +} + +/* + * Switch to X plate resistance mode. Set MX to ground, PX to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * Switch to Y plate resistance mode. Set MY to ground, PY to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * This is a RT kernel thread that handles the ADC accesses + * (mainly so we can use semaphores in the UCB1200 core code + * to serialise accesses to the ADC). The UCB1400 access + * functions are expected to be able to sleep as well. + */ +static int ucb1x00_thread(void *_ts) +{ + struct ucb1x00_ts *ts = _ts; + struct task_struct *tsk = current; + int valid; + + ts->rtask = tsk; + + daemonize("ktsd"); + /* only want to receive SIGKILL */ + allow_signal(SIGKILL); + + /* + * We could run as a real-time thread. However, thus far + * this doesn't seem to be necessary. + */ +// tsk->policy = SCHED_FIFO; +// tsk->rt_priority = 1; + + complete(&ts->init_exit); + + valid = 0; + + for (;;) { + unsigned int x, y, p, val; + + ts->restart = 0; + + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); + y = ucb1x00_ts_read_ypos(ts); + p = ucb1x00_ts_read_pressure(ts); + + /* + * Switch back to interrupt mode. + */ + ucb1x00_ts_mode_int(ts); + ucb1x00_adc_disable(ts->ucb); + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + if (signal_pending(tsk)) + break; + + ucb1x00_enable(ts->ucb); + val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + + if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + ucb1x00_disable(ts->ucb); + + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. + */ + if (valid) { + ucb1x00_ts_event_release(ts); + valid = 0; + } + + /* + * Since ucb1x00_enable_irq() might sleep due + * to the way the UCB1400 regs are accessed, we + * can't use set_task_state() before that call, + * and not changing state before enabling the + * interrupt is racy. + */ + wait_for_completion_interruptible(&ts->irq_wait); + } else { + ucb1x00_disable(ts->ucb); + + /* + * Filtering is policy. Policy belongs in user + * space. We therefore leave it to user space + * to do any filtering they please. + */ + if (!ts->restart) { + ucb1x00_ts_evt_add(ts, p, x, y); + valid = 1; + } + + set_task_state(tsk, TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 100); + } + + if (signal_pending(tsk)) + break; + } + + ts->rtask = NULL; + complete_and_exit(&ts->init_exit, 0); +} + +/* + * We only detect touch screen _touches_ with this interrupt + * handler, and even then we just schedule our task. + */ +static void ucb1x00_ts_irq(int idx, void *id) +{ + struct ucb1x00_ts *ts = id; + ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + complete(&ts->irq_wait); +} + +static int ucb1x00_ts_open(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + int ret = 0; + + if (down_interruptible(&ts->sem)) + return -EINTR; + + if (ts->use_count++ != 0) + goto out; + + if (ts->rtask) + panic("ucb1x00: rtask running?"); + + init_completion(&ts->irq_wait); + ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + if (ret < 0) + goto out; + + /* + * If we do this at all, we should allow the user to + * measure and read the X and Y resistance at any time. + */ + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + init_completion(&ts->init_exit); + ret = kernel_thread(ucb1x00_thread, ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&ts->init_exit); + ret = 0; + } else { + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + } + + out: + if (ret) + ts->use_count--; + up(&ts->sem); + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void ucb1x00_ts_close(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + down(&ts->sem); + if (--ts->use_count == 0) { + if (ts->rtask) { + send_sig(SIGKILL, ts->rtask, 1); + wait_for_completion(&ts->init_exit); + } + + ucb1x00_enable(ts->ucb); + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); + ucb1x00_disable(ts->ucb); + } + up(&ts->sem); +} + +#if 0 +static int ucb1x00_ts_resume(struct device *_dev, u32 level) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_ts *ts = ucb1x00_get_drvdata(dev); + + if (level == RESUME_ENABLE && ts->rtask != NULL) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ts->restart = 1; + complete(&ts->irq_wait); + } + return 0; +} +#endif + + +/* + * Initialisation. + */ +static int ucb1x00_ts_add(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + struct ucb1x00_ts *ts; + + ts = kmalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + memset(ts, 0, sizeof(struct ucb1x00_ts)); + + ts->ucb = ucb; + ts->adcsync = adcsync; + init_MUTEX(&ts->sem); + + ts->idev.name = "Touchscreen panel"; + ts->idev.id.product = ts->ucb->id; + ts->idev.open = ucb1x00_ts_open; + ts->idev.close = ucb1x00_ts_close; + + __set_bit(EV_ABS, ts->idev.evbit); + __set_bit(ABS_X, ts->idev.absbit); + __set_bit(ABS_Y, ts->idev.absbit); + __set_bit(ABS_PRESSURE, ts->idev.absbit); + + input_register_device(&ts->idev); + + ucb->ts_data = ts; + + return 0; +} + +static void ucb1x00_ts_remove(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + struct ucb1x00_ts *ts = ucb->ts_data; + + input_unregister_device(&ts->idev); + kfree(ts); +} + +static struct class_interface ucb1x00_ts_interface = { + .add = ucb1x00_ts_add, + .remove = ucb1x00_ts_remove, +}; + +static int __init ucb1x00_ts_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_ts_interface); +} + +static void __exit ucb1x00_ts_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_ts_interface); +} + +#ifndef MODULE + +/* + * Parse kernel command-line options. + * + * syntax : ucbts=[sync|nosync],... + */ +static int __init ucb1x00_ts_setup(char *str) +{ + char *p; + + while ((p = strsep(&str, ",")) != NULL) { + if (strcmp(p, "sync") == 0) + adcsync = UCB_SYNC; + } + + return 1; +} + +__setup("ucbts=", ucb1x00_ts_setup); + +#else + +MODULE_PARM(adcsync, "i"); +MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal"); + +#endif + +module_init(ucb1x00_ts_init); +module_exit(ucb1x00_ts_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/wm9705-ts.c linux-2.6.9-intc1-mih1/drivers/misc/wm9705-ts.c --- linux-2.6.9-intc1/drivers/misc/wm9705-ts.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/wm9705-ts.c 2005-04-24 22:37:36.000000000 -0400 @@ -0,0 +1,722 @@ +/* + * wm9705-touch.c -- Touch screen driver for Wolfson WM9713 AC97 Codecs. + * + * Copyright 2003, 2004, 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * 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. + * + * Notes: + * + * Features: + * - polling mode + * - coordinate polling + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - pen down detection + * - battery monitor + * - sample AUX adc's + * - power management + * - codec GPIO + * - codec event notification + * + * Revision history + * 7th May 2003 Initial version. + * 6th June 2003 Added non module support and AC97 registration. + * 18th June 2003 Added AUX adc sampling. + * 23rd June 2003 Did some minimal reformatting, fixed a couple of + * locking bugs and noted a race to fix. + * 24th June 2003 Added power management and fixed race condition. + * 10th July 2003 Changed to a misc device. + * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h + * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() + * because some ac97_read/ac_97_write call schedule() + * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com + * 13th Nov 2003 Removed h3600 touch interface, added interrupt based + * pen down notification and implemented continous mode + * on XScale arch. + * 16th Nov 2003 Ian Molton + * Modified so that it suits the new 2.6 driver model. + * 25th Jan 2004 Andrew Zabolotny + * Implemented IRQ-driven pen down detection, implemented + * the private API meant to be exposed to platform-specific + * drivers, reorganized the driver so that it supports + * an arbitrary number of devices. + * 1st Feb 2004 Moved continuous mode handling to a separate + * architecture-dependent file. For now only PXA + * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). + * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both + * digitizer registers instead of reading them every time. + * A reorganization of the whole code for better + * error handling. + * 17th Apr 2004 Added BMON support. + * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual + * GPIOs) and 2.6 power management. + * 29th Nov 2004 Added WM9713 support. + * 1st Mar 2005 Changed from OSS plugin to ALSA audio bus device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" +#include "mcp.h" + +#define TS_NAME "wm9705" +#define WM_TS_VERSION "0.30" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Debug + */ +#if 1 +#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) +#endif +#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) + +/* + * Module parameters + */ + +/* + * Set the codec sample mode. + * + * The WM9713 can sample touchscreen data in 3 different operating + * modes. i.e. polling, coordinate and continous. + * + * Polling: The driver polls the codec and issues 3 seperate commands + * over the AC97 link to read X,Y and pressure. + * + * Coordinate: The driver polls the codec and only issues 1 command over + * the AC97 link to read X,Y and pressure. This mode has + * strict timing requirements and may drop samples if + * interrupted. However, it is less demanding on the AC97 + * link. Note: this mode requires a larger delay than polling + * mode. + * + * Continuous: The codec automatically samples X,Y and pressure and then + * sends the data over the AC97 link in slowm-> This is the + * same method used by the codec when recording audio. + * This mode is not selectable by this parameter because + * it needs separate architecture-dependent support. The + * following drivers will add continuouse mode support: + * + * pxa-wm97xx-touch.c - for Intel PXA built-in AC97 controller. + */ +static int mode; +module_param (mode, int, 0); +MODULE_PARM_DESC(mode, "WM97XX operation mode (0:polling 1:coordinate)"); + +/* + * WM9713 - Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down evenwm-> + */ +static int rpu; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil = 0; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * WM9705 - Pen detect comparator threshold. + * + * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold + * i.e. 1 = Vmid/15 threshold + * 15 = Vmid/1 threshold + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int pdd = 8; +module_param(pdd, int, 0); +MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); + +/* + * WM9712 - Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +static int five_wire; +module_param(five_wire, int, 0); +MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); + +/* + * Touchscreen absolute values + * + * These parameters are used to help the input layer discard out of + * range readings and reduce jitter etc. + * + * o min, max:- indicate the min and max values your touch screen returns + * o fuzz:- use a higher number to reduce jitter + * + * The default values correspond to Mainstone II in QVGA mode + * + * Please read + * Documentation/input/input-programming.txt for more details. + */ +static int num_params; + +static int abs_x[3] = {350,3900,5}; +module_param_array(abs_x, int, num_params, 0); +MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); + +static int abs_y[3] = {320,3750,40}; +module_param_array(abs_y, int, num_params, 0); +MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); + +static int abs_p[3] = {0,150,4}; +module_param_array(abs_p, int, num_params, 0); +MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); + + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, // 1 AC97 Link frames + 42, // 2 + 84, // 4 + 167, // 8 + 333, // 16 + 667, // 32 + 1000, // 48 + 1333, // 64 + 2000, // 96 + 2667, // 128 + 3333, // 160 + 4000, // 192 + 4667, // 224 + 5333, // 256 + 6000, // 288 + 0 // No delay, switch matrix always on +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay (3 * AC97_LINK_FRAME + delay_table [d]); +} + + + +static void wm9705_digitiser_save(struct wm97xx* wm) +{ + wm->dig1 = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER1); + wm->dig2 = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER2); +} + + +static void wm9705_digitiser_restore(struct wm97xx* wm) +{ + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, wm->dig1); + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, wm->dig2); +} + + +static void wm9705_aux_prepare(struct wm97xx* wm) +{ +mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, 0); + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); +} + + +static void wm9705_digitiser_start(struct wm97xx* wm) +{ + u16 dig2 = wm->dig2 | WM97XX_PRP_DET_DIG; + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, wm->dig2 = dig2); + mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); /* dummy read */ +} + +static void wm9705_digitiser_stop(struct wm97xx* wm) +{ + u16 val = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER2); + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, val & ~WM97XX_PRP_DET_DIG); +} + +static inline int is_pden (struct wm97xx* wm) +{ + int pden = 0; + switch (wm->id) { + case WM9705_ID2: + pden = wm->dig2 & WM9705_PDEN; + break; + case WM9712_ID2: + pden = wm->dig2 & WM9712_PDEN; + break; + } + return pden; +} + +/* + * Read a sample from the WM9713 adc in polling mode. + */ +static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample) +{ + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay (delay); + + /* wait for POLL to go low */ + while ((mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else { + dbg ("adc sample timeout"); + } + return 0; + } + + *sample = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { + dbg ("adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSEL_MASK); + return 0; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + + +/* + * Read WM9713 sample data (x,y,[p]) from the adc in coordinate mode. + */ +static int wm9705_poll_coord_touch(struct wm97xx* wm, struct wm97xx_data *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = (wm->dig1 & ~WM97XX_ADCSEL_MASK) | WM97XX_ADCSEL_PRES; + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, + (wm->dig1 = dig1) | WM97XX_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* read X then wait for 1 AC97 link frame + settling delay */ + data->x = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + udelay (AC97_LINK_FRAME + delay_table[delay]); + + /* read Y */ + data->y = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + + /* wait for POLL to go low and then read pressure */ + while ((mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)&& timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + if (is_pden(wm)) + wm->pen_probably_down = 0; + else { + + dbg ("adc sample timeout"); + } + return 0; + } + + /* read pressure */ + data->p = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct samples */ + if (((data->x & WM97XX_ADCSRC_MASK) != 0x1000) || + ((data->y & WM97XX_ADCSRC_MASK) != 0x2000) || + ((data->p & WM97XX_ADCSRC_MASK) != 0x3000)) { + dbg ("wrong sample order"); + return 0; + } + + if (!(data->x & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + + +/* + * Sample the WM9713 touchscreen in polling mode + */ +static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) +{ + int rc; + + if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) + return rc; + if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) + return rc; + if (pil && !five_wire) { + if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot + */ +static void wm9705_init_continuous_mode (struct wm97xx* wm, int enable, int slot, int rate) +{ + u16 dig1, dig2; + + // spin_lock(&wm->lock); + + dig1 = wm->dig1; + dig2 = wm->dig2; + + if (enable) { + /* continous mode */ + dbg("Enabling continous mode"); + //wm->cont_read_sample = dev->cont_read; + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY (delay) | + WM97XX_SLT (slot) | + WM97XX_RATE (rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + if (wm->id == WM9712_ID2) + dig2 |= WM9712_PDEN; + else + dig2 |= WM9705_PDEN; + } else { + wm->cont_read_sample = NULL; + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + if (mode == 1) + /* coordinate mode */ + dig1 |= WM97XX_COO; + + if (wm->id == WM9712_ID2) + dig2 &= ~WM9712_PDEN; + else + dig2 &= ~WM9705_PDEN; + } + + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, wm->dig1 = dig1); + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, wm->dig2 = dig2); + + // spin_unlock(&wm->lock); +} + +/* + * set up the physical settings of the WM9705 + */ +static void init_wm9705_phy(struct wm97xx* wm) +{ + wm->dig1 = 0; + + wm->dig2 = WM97XX_RPR; + + /* + * mute VIDEO and AUX as they share X and Y touchscreen + * inputs on the WM9705 + */ + mcp_reg_write(wm->mcp, AC97_AUX, 0x8000); + mcp_reg_write(wm->mcp, AC97_VIDEO, 0x8000); + + /* touchpanel pressure current*/ + if (pil == 2) { + wm->dig2 |= WM9705_PIL; + dbg("setting pressure measurement current to 400uA."); + } else if (pil) + dbg("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* polling mode sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + dbg("supplied delay out of range."); + delay = 4; + } + } + wm->dig1 &= 0xff0f; + wm->dig1 |= WM97XX_DELAY(delay); + dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); + + /* coordinate mode */ + if (mode == 1) { + wm->dig1 |= WM97XX_COO; + dbg("using coordinate mode"); + } + + /* WM9705 pdd */ + wm->dig2 |= (pdd & 0x000f); + dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); + + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, wm->dig1); + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, wm->dig2); +} + +/* + * set up the physical settings of the WM9712 + */ +static void init_wm9712_phy(struct wm97xx* wm) +{ + wm->dig1 = 0; + wm->dig2 = WM97XX_RPR | WM9712_RPU(1); + + /* WM9712 rpu */ + if (rpu) { + wm->dig2 &= 0xffc0; + wm->dig2 |= WM9712_RPU(rpu); + dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); + } + + /* touchpanel pressure current*/ + if (pil == 2) { + wm->dig2 |= WM9712_PIL; + dbg("setting pressure measurement current to 400uA."); + } else if (pil) + dbg("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* WM9712 five wire */ + if (five_wire) { + wm->dig2 |= WM9712_45W; + dbg("setting 5-wire touchscreen mode."); + } + + /* polling mode sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + dbg("supplied delay out of range."); + delay = 4; + } + } + wm->dig1 &= 0xff0f; + wm->dig1 |= WM97XX_DELAY(delay); + dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); + + /* coordinate mode */ + if (mode == 1) { + wm->dig1 |= WM97XX_COO; + dbg("using coordinate mode"); + } + + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER1, wm->dig1); + mcp_reg_write(wm->mcp, AC97_WM97XX_DIGITISER2, wm->dig2); +} + +void wm97xx_set_continuous_mode (struct wm97xx *wm, codec_read_t read, int enable, int slot, int rate) +{ + wm->cont_read_sample = read; + wm->wm97xx_codec->set_continuous_mode(wm, enable, slot, rate); +} +EXPORT_SYMBOL_GPL(wm97xx_set_continuous_mode); + +static struct wm97xx_codec wm9705_codec = { + .poll_sample = wm9705_poll_sample, + .poll_coord = wm9705_poll_coord_touch, + .poll_touch = wm9705_poll_touch, + .init_phy = init_wm9705_phy, + .digitiser_save = wm9705_digitiser_save, + .digitiser_restore = wm9705_digitiser_restore, + .aux_prepare = wm9705_aux_prepare, + .set_continuous_mode = wm9705_init_continuous_mode, + .digitiser_start = wm9705_digitiser_start, + .digitiser_stop = wm9705_digitiser_stop, +}; + + +static int wm9705_ts_add(struct class_device *dev) +{ + u16 id1, id2; + struct wm97xx *wm = classdev_to_wm97xx(dev); + + info("Wolfson WM9705/12 Touchscreen Controller driver"); + info("Version %s liam.girdwood@wolfsonmicro.com", WM_TS_VERSION); + + /* test for a WM9705/12 */ + id1 = mcp_reg_read(wm->mcp, AC97_VENDOR_ID1); + id2 = mcp_reg_read(wm->mcp, AC97_VENDOR_ID2); + if (id1 != WM97XX_ID1 || (id2 != WM9705_ID2 && id2 != WM9712_ID2)) { + err("could not find a WM9705/12 codec. Found a 0x%4x:0x%4x instead", id1, id2); + return -ENODEV; + } + + wm->wm97xx_codec = &wm9705_codec; + if(id2 == WM9712_ID2) + wm->wm97xx_codec->init_phy= init_wm9712_phy; + memset (&wm->wm97xx_input, 0, sizeof (struct input_dev)); + wm->wm97xx_input.name = "wm9705 touchscreen"; + wm->wm97xx_input.open = wm97xx_ts_input_open; + wm->wm97xx_input.close = wm97xx_ts_input_close; + set_bit(EV_ABS, wm->wm97xx_input.evbit); + set_bit(ABS_X, wm->wm97xx_input.absbit); + set_bit(ABS_Y, wm->wm97xx_input.absbit); + set_bit(ABS_PRESSURE, wm->wm97xx_input.absbit); + wm->wm97xx_input.absmax[ABS_X] = abs_x[1]; + wm->wm97xx_input.absmax[ABS_Y] = abs_y[1]; + wm->wm97xx_input.absmax[ABS_PRESSURE] = abs_p[1]; + wm->wm97xx_input.absmin[ABS_X] = abs_x[0]; + wm->wm97xx_input.absmin[ABS_Y] = abs_y[0]; + wm->wm97xx_input.absmin[ABS_PRESSURE] = abs_p[0]; + wm->wm97xx_input.absfuzz[ABS_X] = abs_x[2]; + wm->wm97xx_input.absfuzz[ABS_Y] = abs_y[2]; + wm->wm97xx_input.absfuzz[ABS_PRESSURE] = abs_p[2]; + wm->wm97xx_input.private = wm; + input_register_device(&wm->wm97xx_input); + + /* set up physical characteristics */ + init_wm9705_phy(wm); + return 0; +} + +static void wm9705_ts_remove(struct class_device *dev) +{ + struct wm97xx *wm = classdev_to_wm97xx(dev); + + input_unregister_device(&wm->wm97xx_input); +} + +static struct class_interface wm9705_ts_interface = { + .add = wm9705_ts_add, + .remove = wm9705_ts_remove, +}; + +static int __init wm9705_ts_init(void) +{ + return wm97xx_register_interface(&wm9705_ts_interface); +} + +static void __exit wm9705_ts_exit(void) +{ + wm97xx_unregister_interface(&wm9705_ts_interface); +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("WM9705/12 Touch Screen Driver"); +MODULE_LICENSE("GPL"); + +module_init(wm9705_ts_init); +module_exit(wm9705_ts_exit); diff -Nur linux-2.6.9-intc1/drivers/misc/wm9713-ts.c linux-2.6.9-intc1-mih1/drivers/misc/wm9713-ts.c --- linux-2.6.9-intc1/drivers/misc/wm9713-ts.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/wm9713-ts.c 2005-04-25 16:27:16.000000000 -0400 @@ -0,0 +1,646 @@ +/* + * wm9713-touch.c -- Touch screen driver for Wolfson WM9713 AC97 Codecs. + * + * Copyright 2003, 2004, 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * 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. + * + * Notes: + * + * Features: + * - polling mode + * - coordinate polling + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - pen down detection + * - battery monitor + * - sample AUX adc's + * - power management + * - codec GPIO + * - codec event notification + * + * Revision history + * 7th May 2003 Initial version. + * 6th June 2003 Added non module support and AC97 registration. + * 18th June 2003 Added AUX adc sampling. + * 23rd June 2003 Did some minimal reformatting, fixed a couple of + * locking bugs and noted a race to fix. + * 24th June 2003 Added power management and fixed race condition. + * 10th July 2003 Changed to a misc device. + * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h + * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() + * because some ac97_read/ac_97_write call schedule() + * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com + * 13th Nov 2003 Removed h3600 touch interface, added interrupt based + * pen down notification and implemented continous mode + * on XScale arch. + * 16th Nov 2003 Ian Molton + * Modified so that it suits the new 2.6 driver model. + * 25th Jan 2004 Andrew Zabolotny + * Implemented IRQ-driven pen down detection, implemented + * the private API meant to be exposed to platform-specific + * drivers, reorganized the driver so that it supports + * an arbitrary number of devices. + * 1st Feb 2004 Moved continuous mode handling to a separate + * architecture-dependent file. For now only PXA + * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). + * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both + * digitizer registers instead of reading them every time. + * A reorganization of the whole code for better + * error handling. + * 17th Apr 2004 Added BMON support. + * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual + * GPIOs) and 2.6 power management. + * 29th Nov 2004 Added WM9713 support. + * 1st Mar 2005 Changed from OSS plugin to ALSA audio bus device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" +#include "mcp.h" + +#define TS_NAME "wm9713" +#define WM_TS_VERSION "0.30" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Debug + */ +#if 0 +#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) +#endif +#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) + +/* + * Module parameters + */ + +/* + * Set the codec sample mode. + * + * The WM9713 can sample touchscreen data in 3 different operating + * modes. i.e. polling, coordinate and continous. + * + * Polling: The driver polls the codec and issues 3 seperate commands + * over the AC97 link to read X,Y and pressure. + * + * Coordinate: The driver polls the codec and only issues 1 command over + * the AC97 link to read X,Y and pressure. This mode has + * strict timing requirements and may drop samples if + * interrupted. However, it is less demanding on the AC97 + * link. Note: this mode requires a larger delay than polling + * mode. + * + * Continuous: The codec automatically samples X,Y and pressure and then + * sends the data over the AC97 link in slowm-> This is the + * same method used by the codec when recording audio. + * This mode is not selectable by this parameter because + * it needs separate architecture-dependent support. The + * following drivers will add continuouse mode support: + * + * pxa-wm97xx-touch.c - for Intel PXA built-in AC97 controller. + */ +static int mode; +module_param (mode, int, 0); +MODULE_PARM_DESC(mode, "WM97XX operation mode (0:polling 1:coordinate)"); + +/* + * WM9713 - Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down evenwm-> + */ +static int rpu; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil = 0; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + + +/* + * Touchscreen absolute values + * + * These parameters are used to help the input layer discard out of + * range readings and reduce jitter etc. + * + * o min, max:- indicate the min and max values your touch screen returns + * o fuzz:- use a higher number to reduce jitter + * + * The default values correspond to Mainstone II in QVGA mode + * + * Please read + * Documentation/input/input-programming.txt for more details. + */ + +static int num_params; + +static int abs_x[3] = {350,3900,5}; +module_param_array(abs_x, int, num_params, 0); +MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); + +static int abs_y[3] = {320,3750,40}; +module_param_array(abs_y, int, num_params, 0); +MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); + +static int abs_p[3] = {0,150,4}; +module_param_array(abs_p, int, num_params, 0); +MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); + + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, // 1 AC97 Link frames + 42, // 2 + 84, // 4 + 167, // 8 + 333, // 16 + 667, // 32 + 1000, // 48 + 1333, // 64 + 2000, // 96 + 2667, // 128 + 3333, // 160 + 4000, // 192 + 4667, // 224 + 5333, // 256 + 6000, // 288 + 0 // No delay, switch matrix always on +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay (3 * AC97_LINK_FRAME + delay_table [d]); +} + + + +static void wm9713_digitiser_save(struct wm97xx* wm) +{ + wm->dig1 = mcp_reg_read(wm->mcp, AC97_WM9713_DIG1); + wm->dig2 = mcp_reg_read(wm->mcp, AC97_WM9713_DIG2); + wm->dig3 = mcp_reg_read(wm->mcp, AC97_WM9713_DIG3); +} + + +static void wm9713_digitiser_restore(struct wm97xx* wm) +{ + mcp_reg_write(wm->mcp, AC97_WM9713_DIG1, wm->dig1); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG2, wm->dig2); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG3, wm->dig3); +} + + +static void wm9713_aux_prepare(struct wm97xx* wm) +{ + mcp_reg_write(wm->mcp, AC97_WM9713_DIG1, 0); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG2, 0); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); +} + + +static void wm9713_digitiser_start(struct wm97xx* wm) +{ + u16 val = mcp_reg_read(wm->mcp, AC97_EXTENDED_MID); + mcp_reg_write(wm->mcp, AC97_EXTENDED_MID, val & 0x7fff); + val = mcp_reg_read(wm->mcp, AC97_WM9713_DIG3); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG3, val | WM97XX_PRP_DET_DIG); + mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); /* dummy read */ +} + +static void wm9713_digitiser_stop(struct wm97xx* wm) +{ + u16 val = mcp_reg_read(wm->mcp, AC97_WM9713_DIG3); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG3, val & ~WM97XX_PRP_DET_DIG); + val = mcp_reg_read(wm->mcp, AC97_EXTENDED_MID); + mcp_reg_write(wm->mcp, AC97_EXTENDED_MID, val | 0x8000); +} + +static inline int is_pden (struct wm97xx* wm) +{ + return wm->dig3 & WM9713_PDEN; +} + +/* + * Read a sample from the WM9713 adc in polling mode. + */ +static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample) +{ + u16 dig1; + + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (adcsel & 0x8000) + adcsel = 1 << ((adcsel & 0x7fff) + 4); + dig1 = mcp_reg_read(wm->mcp, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + mcp_reg_write(wm->mcp, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((mcp_reg_read(wm->mcp, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden (wm)) + wm->pen_probably_down = 0; + else { + dbg ("adc sample timeout"); + } + return RC_PENUP; + } + *sample = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSRC_MASK) != generic_ffs(adcsel >> 1) << 12) { + dbg ("adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSRC_MASK); + return RC_PENUP; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + + +/* + * Read WM9713 sample data (x,y,[p]) from the adc in coordinate mode. + */ +static int wm9713_poll_coord_touch(struct wm97xx* wm, struct wm97xx_data *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = mcp_reg_read(wm->mcp, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + mcp_reg_write(wm->mcp, AC97_WM9713_DIG1, dig1 | WM9713_ADCSEL_PRES | + WM9713_POLL | WM9713_COO); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* read X then wait for 1 AC97 link frame + settling delay */ + data->x = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + udelay (AC97_LINK_FRAME + delay_table[delay]); + + /* read Y */ + data->y = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + + /* wait for POLL to go low and then read pressure */ + while ((mcp_reg_read(wm->mcp, AC97_WM9713_DIG1) & WM9713_POLL)&& timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + if (is_pden (wm)) + wm->pen_probably_down = 0; + else { + dbg ("adc sample timeout"); + } + return RC_PENUP; + } + + /* read pressure */ + data->p = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct samples */ + if (((data->x & WM97XX_ADCSRC_MASK) != 0x1000) || + ((data->y & WM97XX_ADCSRC_MASK) != 0x2000) || + ((data->p & WM97XX_ADCSRC_MASK) != 0x3000)) { + dbg ("wrong sample order"); + return RC_PENUP; + } + + if (!(data->x & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + + +/* + * Sample the WM9713 touchscreen in polling mode + */ +static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) +{ + int rc; + + if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID) + return rc; + if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID) + return rc; + if (pil) { + if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot + */ +static void wm9713_init_continuous_mode (struct wm97xx* wm, int enable, int slot, int rate) +{ + u16 dig1, dig2, dig3; + + //spin_lock(&wm->lock); + + dig1 = wm->dig1; + dig2 = wm->dig2; + dig3 = wm->dig3; + + if (enable) { + /* continous mode */ + dbg("Enabling continous mode"); + dig1 &= ~WM9713_ADCSEL_MASK; + dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; + if (pil) + dig1 |= WM9713_ADCSEL_PRES; + dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); + dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | WM97XX_SLT (slot) | WM97XX_RATE (rate); + dig3 |= WM9713_PDEN; + } else { + dig1 &= ~(WM97XX_CTC | WM97XX_COO); + dig2 &= ~WM97XX_SLEN; + if (mode == 1) + /* coordinate mode */ + dig1 |= WM97XX_COO; + dig3 &= ~WM9713_PDEN; + } + + mcp_reg_write(wm->mcp, AC97_WM9713_DIG1, wm->dig1 = dig1); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG2, wm->dig2 = dig2); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG3, wm->dig3 = dig3); + //spin_unlock (&wm->lock); +} + +/* + * set up the physical settings of the WM9713 + */ +static void init_wm9713_phy(struct wm97xx* wm) +{ + u16 dig1 = 0, dig2, dig3; + + /* default values */ + dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); + dig3= WM9712_RPU(1); + + /* rpu */ + if (rpu) { + dig3 &= 0xffc0; + dig3 |= WM9712_RPU(rpu); + info("setting pen detect pull-up to %d Ohms",64000 / rpu); + } + + /* touchpanel pressure */ + if (pil == 2) { + dig3 |= WM9712_PIL; + info("setting pressure measurement current to 400uA."); + } else if (pil) + info ("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + info ("supplied delay out of range."); + delay = 4; + info("setting adc sample delay to %d u Secs.", delay_table[delay]); + } + } + dig2 &= 0xff0f; + dig2 |= WM97XX_DELAY(delay); + + /* coordinate mode */ + if (mode == 1) { + dig1 |= WM9713_COO; + info("using coordinate mode"); + } + + mcp_reg_write(wm->mcp, AC97_WM9713_DIG1, wm->dig1 = dig1); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG2, wm->dig2 = dig2); + mcp_reg_write(wm->mcp, AC97_WM9713_DIG3, wm->dig3 = dig3); +} + +void wm97xx_set_continuous_mode (struct wm97xx *wm, codec_read_t read, int enable, int slot, int rate) +{ + wm->cont_read_sample = read; + wm->wm97xx_codec->set_continuous_mode(wm, enable, slot, rate); +} +EXPORT_SYMBOL_GPL(wm97xx_set_continuous_mode); + +static struct wm97xx_codec wm9713_codec = { + .poll_sample = wm9713_poll_sample, + .poll_coord = wm9713_poll_coord_touch, + .poll_touch = wm9713_poll_touch, + .init_phy = init_wm9713_phy, +// .codec_suspend = wm9713_suspend, +// .codec_resume = wm9713_resume, + .digitiser_save = wm9713_digitiser_save, + .digitiser_restore = wm9713_digitiser_restore, + .aux_prepare = wm9713_aux_prepare, + .set_continuous_mode = wm9713_init_continuous_mode, + .digitiser_start = wm9713_digitiser_start, + .digitiser_stop = wm9713_digitiser_stop, +}; + + +static int wm9713_ts_add(struct class_device *dev) +{ + u16 id1, id2; + struct wm97xx *wm = classdev_to_wm97xx(dev); + + dbg("adding..\n"); + + info("Wolfson WM9713 Touchscreen Controller driver"); + info("Version %s liam.girdwood@wolfsonmicro.com", WM_TS_VERSION); + + /* test for a WM9713 */ + id1 = mcp_reg_read(wm->mcp, AC97_VENDOR_ID1); + id2 = mcp_reg_read(wm->mcp, AC97_VENDOR_ID2); + if (id1 != WM97XX_ID1 || id2 != WM9713_ID2) { + err("could not find a WM9713 codec. Found a 0x%4x:0x%4x instead", id1, id2); + return -ENODEV; + } + + wm->wm97xx_codec = &wm9713_codec; + memset (&wm->wm97xx_input, 0, sizeof (struct input_dev)); + wm->wm97xx_input.name = "wm9713 touchscreen"; + wm->wm97xx_input.open = wm97xx_ts_input_open; + wm->wm97xx_input.close = wm97xx_ts_input_close; + set_bit(EV_ABS, wm->wm97xx_input.evbit); + set_bit(ABS_X, wm->wm97xx_input.absbit); + set_bit(ABS_Y, wm->wm97xx_input.absbit); + set_bit(ABS_PRESSURE, wm->wm97xx_input.absbit); + wm->wm97xx_input.absmax[ABS_X] = abs_x[1]; + wm->wm97xx_input.absmax[ABS_Y] = abs_y[1]; + wm->wm97xx_input.absmax[ABS_PRESSURE] = abs_p[1]; + wm->wm97xx_input.absmin[ABS_X] = abs_x[0]; + wm->wm97xx_input.absmin[ABS_Y] = abs_y[0]; + wm->wm97xx_input.absmin[ABS_PRESSURE] = abs_p[0]; + wm->wm97xx_input.absfuzz[ABS_X] = abs_x[2]; + wm->wm97xx_input.absfuzz[ABS_Y] = abs_y[2]; + wm->wm97xx_input.absfuzz[ABS_PRESSURE] = abs_p[2]; + wm->wm97xx_input.private = wm; + input_register_device(&wm->wm97xx_input); + + /* set up physical characteristics */ + init_wm9713_phy(wm); + return 0; +} + +static void wm9713_ts_remove(struct class_device *dev) +{ + struct wm97xx *wm = classdev_to_wm97xx(dev); + + input_unregister_device(&wm->wm97xx_input); +} + +static struct class_interface wm9713_ts_interface = { + .add = wm9713_ts_add, + .remove = wm9713_ts_remove, +}; + +static int __init wm9713_ts_init(void) +{ + dbg("ts_init: register_interface..\n"); + return wm97xx_register_interface(&wm9713_ts_interface); +} + +static void __exit wm9713_ts_exit(void) +{ + wm97xx_unregister_interface(&wm9713_ts_interface); +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); +MODULE_LICENSE("GPL"); + +module_init(wm9713_ts_init); +module_exit(wm9713_ts_exit); diff -Nur linux-2.6.9-intc1/drivers/misc/wm97xx-core.c linux-2.6.9-intc1-mih1/drivers/misc/wm97xx-core.c --- linux-2.6.9-intc1/drivers/misc/wm97xx-core.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/wm97xx-core.c 2005-04-25 16:24:24.000000000 -0400 @@ -0,0 +1,942 @@ +/* + * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 + * and WM9713 AC97 Codecs. + * + * Copyright 2003, 2004, 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * 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. + * + * Notes: + * + * Features: + * - supports WM9705, WM9712, WM9713 + * - polling mode + * - coordinate polling + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - 4 and 5 wire touchscreens (5 wire is WM9712 only) + * - pen down detection + * - battery monitor + * - sample AUX adc's + * - power management + * - codec GPIO + * - codec event notification + * + * Revision history + * 7th May 2003 Initial version. + * 6th June 2003 Added non module support and AC97 registration. + * 18th June 2003 Added AUX adc sampling. + * 23rd June 2003 Did some minimal reformatting, fixed a couple of + * locking bugs and noted a race to fix. + * 24th June 2003 Added power management and fixed race condition. + * 10th July 2003 Changed to a misc device. + * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h + * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() + * because some ac97_read/ac_97_write call schedule() + * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com + * 13th Nov 2003 Removed h3600 touch interface, added interrupt based + * pen down notification and implemented continous mode + * on XScale arch. + * 16th Nov 2003 Ian Molton + * Modified so that it suits the new 2.6 driver model. + * 25th Jan 2004 Andrew Zabolotny + * Implemented IRQ-driven pen down detection, implemented + * the private API meant to be exposed to platform-specific + * drivers, reorganized the driver so that it supports + * an arbitrary number of devices. + * 1st Feb 2004 Moved continuous mode handling to a separate + * architecture-dependent file. For now only PXA + * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). + * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both + * digitizer registers instead of reading them every time. + * A reorganization of the whole code for better + * error handling. + * 17th Apr 2004 Added BMON support. + * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual + * GPIOs) and 2.6 power management. + * 29th Nov 2004 Added WM9713 support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" +#include "mcp.h" + +#define TS_NAME "wm97xx" +#define WM_TS_VERSION "0.30" +#define DEFAULT_PRESSURE 0xb0c0 + +#define to_wm97xx(x) container_of((x), struct wm97xx, ts) +#define to_wm97xx_ts(x) container_of((x), struct wm97xx_ts, wm97xx_codec) + +/* + * Debug + */ +#if 1 +#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) +#endif +#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) + +extern struct class wm97xx_class; + +/* + * Read AUX adc + */ +static int wm97xx_read_aux_adc (struct wm97xx* wm, u16 adcsel) +{ + int power_adc = 0, auxval; + u16 power = 0; + + /* get codec */ + spin_lock(&wm->lock); + + /* When the touchscreen is not in use, we may have to power up the AUX ADC + * before we can use sample the AUX inputs-> + */ + if (wm->id == WM9713_ID2 && + (power = mcp_reg_read(wm->mcp, AC97_EXTENDED_MID)) & 0x8000) { + power_adc = 1; + mcp_reg_write(wm->mcp, AC97_EXTENDED_MID, power & 0x7fff); + } + + /* Prepare the codec for AUX reading */ + wm->wm97xx_codec->digitiser_save(wm); + wm->wm97xx_codec->aux_prepare(wm); + + /* Turn polling mode on to read AUX ADC */ + wm->pen_probably_down = 1; + wm->wm97xx_codec->poll_sample (wm, adcsel, &auxval); + + if (power_adc) + mcp_reg_write(wm->mcp, AC97_EXTENDED_MID, power | 0x8000); + + wm->pen_probably_down = 0; + wm->wm97xx_codec->digitiser_restore(wm); + + spin_unlock(&wm->lock); + + return auxval; +} + +#define WM97XX_ATTR(name,input)\ +static ssize_t name##_show(struct class_device *dev, char *buf) \ +{ \ + struct wm97xx *wm = classdev_to_wm97xx(dev); \ + return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \ +} \ +static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL) + +WM97XX_ATTR(aux1, WM97XX_AUX_ID1); +WM97XX_ATTR(aux2, WM97XX_AUX_ID2); +WM97XX_ATTR(aux3, WM97XX_AUX_ID3); +WM97XX_ATTR(aux4, WM97XX_AUX_ID4); + +static int wm97xx_aux_adc_add(struct class_device *dev) +{ + class_device_create_file(dev, &class_device_attr_aux1); + class_device_create_file(dev, &class_device_attr_aux2); + class_device_create_file(dev, &class_device_attr_aux3); + class_device_create_file(dev, &class_device_attr_aux4); + return 0; +} + +static void wm97xx_aux_adc_remove(struct class_device *dev) +{ + class_device_remove_file(dev, &class_device_attr_aux1); + class_device_remove_file(dev, &class_device_attr_aux2); + class_device_remove_file(dev, &class_device_attr_aux3); + class_device_remove_file(dev, &class_device_attr_aux4); +} + +static struct class_interface wm97xx_aux_adc_interface = { + .add = wm97xx_aux_adc_add, + .remove = wm97xx_aux_adc_remove, +}; + + +/* + * Get the status of a codec GPIO pin + */ +wm97xx_gpio_status_t wm97xx_get_codec_gpio (struct wm97xx *wm, u32 gpio) +{ + u16 status = mcp_reg_read(wm->mcp, AC97_GPIO_STATUS); + + if (status & gpio) + return WM97XX_GPIO_HIGH; + else + return WM97XX_GPIO_LOW; +} +EXPORT_SYMBOL_GPL(wm97xx_get_codec_gpio); + +/* + * Set the status of a codec GPIO pin + */ +void wm97xx_set_codec_gpio (struct wm97xx *wm, u32 gpio, wm97xx_gpio_status_t status) +{ + u16 reg; + + spin_lock(&wm->lock); + reg = mcp_reg_read(wm->mcp, AC97_GPIO_STATUS); + if (status & WM97XX_GPIO_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + if (wm->id == WM9712_ID2) { + /* small hack for wm9712 */ + //CODEC->id = 1; + mcp_reg_write(wm->mcp, AC97_GPIO_STATUS, reg << 1); + //CODEC->id = 0; + } else + mcp_reg_write(wm->mcp, AC97_GPIO_STATUS, reg); + + spin_unlock(&wm->lock); +} +EXPORT_SYMBOL_GPL(wm97xx_set_codec_gpio); + +/* + * Codec GPIO pin configuration, this set's pin direction, polarity, + * stickyness and wake up. + */ +void wm97xx_config_codec_gpio (struct wm97xx *wm, u32 gpio, + wm97xx_gpio_dir_t dir, wm97xx_gpio_pol_t pol, + wm97xx_gpio_sticky_t sticky, wm97xx_gpio_wake_t wake) +{ + u16 reg; + spin_lock(&wm->lock); + + reg = mcp_reg_read(wm->mcp, AC97_GPIO_POLARITY); + if (pol == WM97XX_GPIO_POL_HIGH) + reg |= gpio; + else + reg &= ~gpio; + mcp_reg_write(wm->mcp, AC97_GPIO_POLARITY, reg); + + reg = mcp_reg_read(wm->mcp, AC97_GPIO_STICKY); + if (sticky == WM97XX_GPIO_STICKY) + reg |= gpio; + else + reg &= ~gpio; + mcp_reg_write(wm->mcp, AC97_GPIO_STICKY, reg); + + reg = mcp_reg_read(wm->mcp, AC97_GPIO_WAKEUP); + if (wake == WM97XX_GPIO_WAKE) + reg |= gpio; + else + reg &= ~gpio; + mcp_reg_write(wm->mcp, AC97_GPIO_WAKEUP, reg); + + reg = mcp_reg_read(wm->mcp, AC97_GPIO_CFG); + if (dir == WM97XX_GPIO_IN) + reg |= gpio; + else + reg &= ~gpio; + mcp_reg_write(wm->mcp, AC97_GPIO_CFG, reg); + spin_unlock(&wm->lock); +} +EXPORT_SYMBOL_GPL(wm97xx_config_codec_gpio); + +/* + * Allow arch specific driver to use the codec PENDOWN gpio signal + * as a pen down interrupt source. The PENDOWN signal is usually + * routed via a cpu irq/gpio. + * + * NOTE: The wm9712 also allows the pen down signal to be sent over + * the AC97 link or over the codec irq pin. Use wm97xx_enable_codec_irq + * in this case. + */ +int wm97xx_set_pen_irq (struct wm97xx *wm, int irq) +{ + spin_lock(&wm->lock); + if (!wm->pen_irq) + wm->pen_irq = irq; + + spin_unlock(&wm->lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_set_pen_irq); + +/* + * Allow arch specific drivers to use the codec AC97 interrupt. This + * can either be over the AC97 link or using the codec irq pin. + * This interrupt can be used to notify arch specific drivers of + * numerous events i.e. battery level, ADC data, pen down, etc + */ +int wm97xx_set_codec_irq (struct wm97xx *wm, int irq, int ac97_link) +{ + spin_lock(&wm->lock); + wm->codec_irq_ref_count++; + if (!wm->codec_irq) { + wm->codec_irq = irq; + wm->ac97_link = ac97_link; + } + spin_unlock(&wm->lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_set_codec_irq); + +void wm97xx_clear_pen_irq (struct wm97xx *wm) +{ + spin_lock(&wm->lock); + if (wm->pen_irq) + wm->pen_irq = 0; + + spin_unlock(&wm->lock); +} +EXPORT_SYMBOL_GPL(wm97xx_clear_pen_irq); + +int wm97xx_clear_codec_irq (struct wm97xx *wm) +{ + spin_lock(&wm->lock); + if (wm->codec_irq_ref_count == 0) + goto out; + + /* last one ? */ + if (--wm->codec_irq_ref_count == 0) { + wm->codec_irq = 0; + } + +out: + spin_unlock(&wm->lock); + return wm->codec_irq_ref_count; +} +EXPORT_SYMBOL_GPL(wm97xx_clear_codec_irq); + +/* + * Register arch specific codec event handler. One handler per event. + */ +int wm97xx_request_codec_event (struct wm97xx *wm, int gpio, codec_event_t work) +{ + u16 reg; + int irq = generic_ffs(gpio) - 1; + + if (irq < 0 || irq > WM97XX_MAX_GPIO) + return -ENODEV; + + spin_lock(&wm->lock); + if (wm->id == WM9705_ID2) + goto err; + + /* cant use GPIO2 for irq and gpio at the same time */ + if (!wm->ac97_link && gpio == WM97XX_GPIO_2) + goto err; + + if (wm->codec_event[irq]) + goto err; + + wm->codec_event[irq] = work; + + /* handle pen down internally */ + if (gpio == WM97XX_GPIO_13) + wm->pen_irq = wm->codec_irq; + + /* set pin to GPIO function */ + reg= mcp_reg_read(wm->mcp, AC97_MISC_AFE); + mcp_reg_write(wm->mcp, AC97_MISC_AFE, reg | gpio); + spin_unlock(&wm->lock); + wm97xx_config_codec_gpio (wm, gpio, WM97XX_GPIO_IN, WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); + + return 0; + +err: + spin_unlock(&wm->lock); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(wm97xx_request_codec_event); + +/* + * Free codec event handler + */ +void wm97xx_free_codec_event (struct wm97xx *wm, int gpio) +{ + int irq = generic_ffs(gpio) - 1; + + spin_lock(&wm->lock); + if (wm->id == WM9705_ID2) + goto out; + + if (irq >= 0 && irq < WM97XX_MAX_GPIO) + wm->codec_event[irq] = NULL; + + /* handle pen down internally */ + if (gpio == WM97XX_GPIO_13) + wm->pen_irq = 0; + +out: + spin_unlock(&wm->lock); +} +EXPORT_SYMBOL_GPL(wm97xx_free_codec_event); + + +/* + * Handle a pen down interrupt. + */ +static void wm97xx_pen_irq_worker (void *ptr) +{ + u32 status = 0; + struct wm97xx* wm = (struct wm97xx*)ptr; + + /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + wm->pen_is_down++; + wake_up_interruptible (&wm->pen_irq_wait); + } else { + status = mcp_reg_read(wm->mcp, AC97_GPIO_STATUS); + if (status & WM97XX_GPIO_13) { + wm->pen_is_down++; + wake_up_interruptible (&wm->pen_irq_wait); + } + } +} + +/* + * Handle a codec event interrupt. + * Can also include pen down depending upon configuration. + */ +static void wm97xx_codec_irq_worker (void* ptr) +{ + int i = WM97XX_MAX_GPIO; + u16 status, sticky = 0; + struct wm97xx* wm = (struct wm97xx*)ptr; + + status = mcp_reg_read(wm->mcp, AC97_GPIO_STATUS); + + /* check for pen down as we handle it internally + * MASK pen down the unmask at pen up */ + if (status & WM97XX_GPIO_13) + wm97xx_pen_irq_worker(wm); + + /* reset codec slot 12 sticky bits and disable active interrupt + * sources by clearing the sticky status. + */ + if (wm->id != WM9705_ID2) { + sticky = mcp_reg_read(wm->mcp, AC97_GPIO_STICKY); + mcp_reg_write(wm->mcp, AC97_GPIO_STICKY, status ^ sticky); + if (wm->id == WM9712_ID2) { + /* small hack for wm9712 */ + ((ac97_t*)wm->mcp->me->platform_data)->num = 1; + mcp_reg_write(wm->mcp, AC97_GPIO_STATUS, status << 1); + ((ac97_t*)wm->mcp->me->platform_data)->num = 0; + } else + mcp_reg_write(wm->mcp, AC97_GPIO_STATUS, status); + } + + /* Iterate through archictecture specific codec workers (if they exist) in + * priority order. + */ + while(--i >= 0) { + if (wm->codec_event[i] && status & 1 << i) + wm->codec_event[i](status); + } + + /* turn sticky bits back on, except for pen down */ + if (wm->id != WM9705_ID2) + mcp_reg_write(wm->mcp, AC97_GPIO_STICKY, sticky & ~WM97XX_GPIO_13); + enable_irq(wm->codec_irq); +} + +/* + * Codec PENDOWN edge signal irq handler + */ +static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct wm97xx* wm =(struct wm97xx*) dev_id; + + wm->pen_irq_count++; + queue_work(wm->pen_irq_workq, &wm->pen_event_work); + + return IRQ_HANDLED; +} + +/* + * Codec PENDOWN/Power/GPIO/status signal irq handler + */ +static irqreturn_t wm97xx_codec_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct wm97xx* wm =(struct wm97xx*) dev_id; + + /* disable IRQ so we can reset codec IRQ status */ + disable_irq(irq); + wm->codec_irq_count++; + queue_work(wm->codec_event_workq, &wm->codec_event_work); + + return IRQ_HANDLED; +} + +/* + * initialise codec IRQ handler and workqueue + */ +static int wm97xx_init_codec_irq (struct wm97xx* wm) +{ + u16 reg; + + INIT_WORK(&wm->codec_event_work, wm97xx_codec_irq_worker, wm); + if ((wm->codec_event_workq = create_singlethread_workqueue("kwm97ev")) == NULL) { + err("could not create codec event work queue"); + if (wm->codec_irq == wm->pen_irq) + wm->pen_irq = 0; + wm->codec_irq = 0; + return -EINVAL; + } + + wm->codec_irq_count = 0; + if (request_irq(wm->codec_irq, wm97xx_codec_interrupt, SA_SHIRQ, "wm97xx-ac97", wm)) { + err("could not register codec interrupt"); + destroy_workqueue(wm->codec_event_workq); + if (wm->codec_irq == wm->pen_irq) + wm->pen_irq = 0; + wm->codec_irq = 0; + return -EINVAL; + } + + /* wm9712/13 can interrupt using it's IRQ pin or over the AC97 link */ + if (wm->id != WM9705_ID2) { + if (wm->ac97_link) { /* use link */ + if (wm->id == WM9712_ID2) { + reg = mcp_reg_read(wm->mcp, 0x58); + mcp_reg_write(wm->mcp, 0x58, reg |= 0x2); + } else { + reg = mcp_reg_read(wm->mcp, 0x5a); + mcp_reg_write(wm->mcp, 0x5a, reg |= 0x2); + } + } else { /* use pin */ + reg = mcp_reg_read(wm->mcp, AC97_MISC_AFE); + mcp_reg_write(wm->mcp, AC97_MISC_AFE, reg &= 0xfffb); + reg = mcp_reg_read(wm->mcp, AC97_GPIO_CFG); + mcp_reg_write(wm->mcp, AC97_GPIO_CFG, reg &= 0xfffb); + } + } + + return 0; +} + +/* + * initialise pen IRQ handler and workqueue + */ +static int wm97xx_init_pen_irq (struct wm97xx* wm) +{ + u16 reg; + + INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); + if ((wm->pen_irq_workq = create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + wm->pen_irq = 0; + return -EINVAL; + } + + wm->pen_irq_count = 0; + if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(wm->pen_irq_workq); + wm->pen_irq = 0; + return -EINVAL; + } + + /* enable PEN down on wm9712/13 */ + if (wm->id != WM9705_ID2) { + reg = mcp_reg_read(wm->mcp, AC97_MISC_AFE); + mcp_reg_write(wm->mcp, AC97_MISC_AFE, reg & 0xfff7); + } + + return 0; +} + +/* Private struct for communication between struct wm97xx_tshread and wm97xx_read_samples */ +struct ts_state { + int sleep_time; + int min_sleep_time; +}; + +/* + * When we use an irq to detect a pen down event, we must manually check + * for a pen up by checking the PDEN bit. If the pen goes up, we have to + * re-enable the codec pen down detection. + */ +static int wm97xx_irq_pen_check(struct wm97xx* wm, int rc) +{ + u16 pen_status, reg; + + pen_status = mcp_reg_read(wm->mcp, AC97_WM97XX_DIGITISER_RD); + if (!(pen_status & WM97XX_PEN_DOWN)) { + rc = RC_PENUP; + + /* Pen is now going to the up position */ + if (wm->pen_is_down && (wm->pen_irq == wm->codec_irq)) { + reg = mcp_reg_read(wm->mcp, AC97_GPIO_STICKY); + mcp_reg_write(wm->mcp, AC97_GPIO_STICKY, reg | WM97XX_GPIO_13); + } + } + return rc; +} + +static int wm97xx_read_samples(struct wm97xx* wm, struct ts_state *state) +{ + struct wm97xx_data data; + int rc = RC_PENUP; + + spin_lock(&wm->lock); + + if (wm->cont_read_sample) { + rc = wm->cont_read_sample (&wm->wm97xx_input, wm->pen_is_down, wm->pressure); + rc = wm97xx_irq_pen_check (wm, rc); + } else { + switch (wm->mode) { + case 0: + rc = wm->wm97xx_codec->poll_touch (wm, &data); + break; + case 1: + rc = wm->wm97xx_codec->poll_coord (wm, &data); + break; + } + } + + if (rc & RC_PENUP) { + if (wm->pen_is_down) { + wm->pen_is_down = 0; + dbg("pen up"); + input_report_abs(&wm->wm97xx_input, ABS_PRESSURE, 0); + input_sync(&wm->wm97xx_input); + } else if (!(rc & RC_AGAIN)) { + /* We need high frequency updates only while pen is down, + * the user never will be able to touch screen faster than + * a few times per second... On the other hand, when the + * user is actively working with the touchscreen we don't + * want to lose the quick response. So we will slowly + * increase sleep time after the pen is up and quicky + * restore it to ~one task switch when pen is down again. + */ + if (state->sleep_time < HZ/10) + state->sleep_time++; + } + } else if (rc & RC_VALID) { + printk("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", + data.x >> 12, data.x & 0xfff, + data.y >> 12, data.y & 0xfff, + data.p >> 12, data.p & 0xfff); + + input_report_abs (&wm->wm97xx_input, ABS_X, data.x & 0xfff); + input_report_abs (&wm->wm97xx_input, ABS_Y, data.y & 0xfff); + input_report_abs (&wm->wm97xx_input, ABS_PRESSURE, data.p & 0xfff); + input_sync (&wm->wm97xx_input); + wm->pen_is_down++; + state->sleep_time = state->min_sleep_time; + } else if (rc & RC_PENDOWN) { + dbg("pen down"); + wm->pen_is_down++; + state->sleep_time = state->min_sleep_time; + } + + spin_unlock(&wm->lock); + return rc; +} + +/* + * The touchscreen sample reader thread. + */ +static int wm97xx_ts_read(void* data) +{ + int rc; + struct ts_state state; + struct wm97xx* wm =(struct wm97xx*) data; + + /* set up thread context */ + wm->ts_task = current; + daemonize("kwm97xxts"); + + if(wm->wm97xx_codec == NULL) { + wm->ts_task = NULL; + printk(KERN_ERR "codec is NULL, bailing\n"); + } + complete(&wm->ts_init); + + wm->pen_is_down = 0; + state.min_sleep_time = HZ >= 100 ? HZ/100 : 1; + if (state.min_sleep_time < 1) + state.min_sleep_time = 1; + state.sleep_time = state.min_sleep_time; + + /* touch reader loop */ + while (wm->ts_task) { + do { + rc = wm97xx_read_samples (wm, &state); + } while (rc & RC_AGAIN); + if (!wm->pen_is_down && wm->pen_irq) { + /* Nice, we don't have to poll for pen down event */ + wait_event_interruptible (wm->pen_irq_wait, wm->pen_is_down); + } else { + set_task_state(current, TASK_INTERRUPTIBLE); + schedule_timeout(state.sleep_time); + } + } + complete_and_exit(&wm->ts_exit, 0); +} + +/* + * Start the touchscreen thread and + * the touch digitiser. + */ +int wm97xx_ts_input_open(struct input_dev *idev) +{ + int ret = 0; + struct wm97xx* wm =(struct wm97xx*) idev->private; + u16 reg = 0; + + if (wm->wm97xx_codec == NULL) { + printk("error: wm->wm97xx_codec == NULL\n"); + return -EINVAL; + } + + down(&wm->sem); + /* first time opened ? */ + if (wm->ts_use_count++ == 0) { + + /* start touchscreen thread */ + init_completion(&wm->ts_init); + init_completion(&wm->ts_exit); + + ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&wm->ts_init); + if (wm->ts_task == NULL) + ret = -EINVAL; + } + if (ret < 0) + goto out; + + /* start digitiser */ + spin_lock(&wm->lock); + wm->wm97xx_codec->digitiser_start(wm); + + /* init pen down/up irq handling */ + if (wm->codec_irq || wm->pen_irq) { + if (wm->codec_irq == wm->pen_irq) { + if (wm97xx_init_codec_irq(wm) == 0) { + reg = mcp_reg_read(wm->mcp, AC97_GPIO_STICKY); + mcp_reg_write(wm->mcp, AC97_GPIO_STICKY, reg | WM97XX_GPIO_13); + } + } else { + wm97xx_init_pen_irq(wm); + } + if (wm->pen_irq == 0 ) { + /* we failed to get an irq for pen down events, + * so we resort to polling. kickstart the reader */ + wm->pen_is_down = 1; + wake_up_interruptible (&wm->pen_irq_wait); + } + } + spin_unlock(&wm->lock); + + } + +out: + up(&wm->sem); + return ret; +} +EXPORT_SYMBOL_GPL(wm97xx_ts_input_open); + +/* + * Kill the touchscreen thread and stop + * the touch digitiser. + */ +void wm97xx_ts_input_close(struct input_dev *idev) +{ + struct wm97xx* wm =(struct wm97xx*) idev->private; + + down(&wm->sem); + if (--wm->ts_use_count == 0) { + /* destroy workqueue and free irq's */ + if (wm->codec_irq || wm->pen_irq) { + if (wm->pen_irq == wm->codec_irq) { + free_irq(wm->codec_irq, wm); + destroy_workqueue(wm->codec_event_workq); + } else { + free_irq(wm->pen_irq, wm); + destroy_workqueue(wm->pen_irq_workq); + } + } + + /* kill thread */ + wm->ts_task = NULL; + wm->pen_is_down = 1; + wake_up_interruptible (&wm->pen_irq_wait); + wait_for_completion(&wm->ts_exit); + wm->pen_is_down = 0; + + /* stop digitiser */ + wm->wm97xx_codec->digitiser_stop(wm); + + } + up(&wm->sem); +} +EXPORT_SYMBOL_GPL(wm97xx_ts_input_close); + +static int wm97xx_probe(struct mcp *mcp) +{ + struct wm97xx *wm = NULL; + unsigned int id; + int ret = -ENODEV; + dbg("probing..\n"); + + mcp_enable(mcp); + id = mcp_reg_read(mcp, AC97_VENDOR_ID1); + if (id != WM97XX_ID1) { + printk(KERN_WARNING "WM97xx ID not found: %04x\n", id); + goto err_disable; + } + + wm = kmalloc(sizeof(struct wm97xx), GFP_KERNEL); + ret = -ENOMEM; + if (!wm) + goto err_disable; + + memset(wm, 0, sizeof(struct wm97xx)); + init_MUTEX(&wm->sem); + spin_lock_init (&wm->lock); + init_waitqueue_head (&wm->pen_irq_wait); + + wm->cdev.class = &wm97xx_class; + wm->cdev.dev = mcp->attached_device; + wm->cdev.class_data = wm; + wm->id = mcp_reg_read(mcp, AC97_VENDOR_ID2); + strlcpy(wm->cdev.class_id, "wm97xx", sizeof(wm->cdev.class_id)); + + wm->mcp = mcp; + mcp_set_drvdata(wm->mcp, wm); + + ret = class_device_register(&wm->cdev); + if (ret) + kfree(wm); + + err_disable: + mcp_disable(wm->mcp); + return ret; +} + +static void wm97xx_remove(struct mcp *mcp) +{ + struct wm97xx *wm = mcp_get_drvdata(mcp); + + class_device_unregister(&wm->cdev); + + /* Stop touch read thread */ + if (wm->ts_task) { + wm->ts_task = NULL; + wm->pen_is_down = 1; + wake_up_interruptible (&wm->pen_irq_wait); + wait_for_completion(&wm->ts_exit); + } +} + +static void wm97xx_release(struct class_device *dev) +{ + struct wm97xx *wm = classdev_to_wm97xx(dev); + kfree(wm); +} + +static struct class wm97xx_class = { + .name = "wm97xx", + .release = wm97xx_release, +}; + +int wm97xx_register_interface(struct class_interface *intf) +{ + intf->class = &wm97xx_class; + return class_interface_register(intf); +} +EXPORT_SYMBOL_GPL(wm97xx_register_interface); + +void wm97xx_unregister_interface(struct class_interface *intf) +{ + class_interface_unregister(intf); +} +EXPORT_SYMBOL_GPL(wm97xx_unregister_interface); + +static struct mcp_driver wm97xx_driver = { + .drv = { + .name = "wm97xx", + }, + .probe = wm97xx_probe, + .remove = wm97xx_remove, +}; + +static int __init wm97xx_init(void) +{ + int ret; + + if ((ret = class_register(&wm97xx_class)) != 0) + goto err; + + if ((ret = mcp_driver_register(&wm97xx_driver)) != 0) { + dbg ("mcp_driver_register failed.\n"); + goto mcp_err; + } + + wm97xx_register_interface(&wm97xx_aux_adc_interface); + dbg ("initialized\n"); + return 0; + +mcp_err: + class_unregister(&wm97xx_class); +err: + return ret; +} + +static void __exit wm97xx_exit(void) +{ + wm97xx_unregister_interface(&wm97xx_aux_adc_interface); + mcp_driver_unregister(&wm97xx_driver); + class_unregister(&wm97xx_class); +} + +module_init(wm97xx_init); +module_exit(wm97xx_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/misc/wm97xx.h linux-2.6.9-intc1-mih1/drivers/misc/wm97xx.h --- linux-2.6.9-intc1/drivers/misc/wm97xx.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/wm97xx.h 2005-04-22 02:35:42.000000000 -0400 @@ -0,0 +1,308 @@ +/* + * Register bits for Wolfson WM97xx series of codecs + */ + +#ifndef _WM97XX_H_ +#define _WM97XX_H_ + +#include +#include +#include +#include +#include +#include /* Input device layer */ + +/* + * WM97xx AC97 Touchscreen registers + */ +#define AC97_WM97XX_DIGITISER1 0x76 +#define AC97_WM97XX_DIGITISER2 0x78 +#define AC97_WM97XX_DIGITISER_RD 0x7a +#define AC97_WM9713_DIG1 0x74 +#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 +#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 + +/* + * WM97xx register bits + */ +#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ +#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ +#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ +#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ +#define WM97XX_ADCSEL_MASK 0x7000 +#define WM97XX_COO 0x0800 /* enable coordinate mode */ +#define WM97XX_CTC 0x0400 /* enable continuous mode */ +#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ +#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ +#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ +#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ +#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ +#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ +#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ +#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ +#define WM97XX_CM_RATE_MASK 0x03f0 +#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) +#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ +#define WM97XX_DELAY_MASK 0x00f0 +#define WM97XX_SLEN 0x0008 /* slot read back enable */ +#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ +#define WM97XX_SLT_MASK 0x0007 +#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ +#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ +#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ +#define WM97XX_RPR 0x2000 /* wake up on pen down */ +#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ +#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ + +#define WM97XX_AUX_ID1 0x8001 +#define WM97XX_AUX_ID2 0x8002 +#define WM97XX_AUX_ID3 0x8003 +#define WM97XX_AUX_ID4 0x8004 + + +/* WM9712 Bits */ +#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ +#define WM9712_PDEN 0x0800 /* measure only when pen down */ +#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ +#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ +#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ +#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ +#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ +#define WM9712_PD(i) (0x1 << i) /* power management */ + +/* WM9712 Registers */ +#define AC97_WM9712_POWER 0x24 +#define AC97_WM9712_REV 0x58 + +/* WM9705 Bits */ +#define WM9705_PDEN 0x1000 /* measure only when pen is down */ +#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ +#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ +#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ +#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ +#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ +#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ +#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ +#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ +#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ + + +/* WM9713 Bits */ +#define WM9713_PDPOL 0x0400 /* Pen down polarity */ +#define WM9713_POLL 0x0200 /* initiate a polling measurement */ +#define WM9713_CTC 0x0100 /* enable continuous mode */ +#define WM9713_ADCSEL_X 0x0002 /* X measurement */ +#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ +#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ +#define WM9713_COO 0x0001 /* enable coordinate mode */ +#define WM9713_PDEN 0x0800 /* measure only when pen down */ +#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ + +/* AUX ADC ID's */ +#define TS_COMP1 0x0 +#define TS_COMP2 0x1 +#define TS_BMON 0x2 +#define TS_WIPER 0x3 + +/* ID numbers */ +#define WM97XX_ID1 0x574d +#define WM9712_ID2 0x4c12 +#define WM9705_ID2 0x4c05 +#define WM9713_ID2 0x4c13 + +/* Codec GPIO's */ +#define WM97XX_MAX_GPIO 16 +#define WM97XX_GPIO_1 (1 << 1) +#define WM97XX_GPIO_2 (1 << 2) +#define WM97XX_GPIO_3 (1 << 3) +#define WM97XX_GPIO_4 (1 << 4) +#define WM97XX_GPIO_5 (1 << 5) +#define WM97XX_GPIO_6 (1 << 6) +#define WM97XX_GPIO_7 (1 << 7) +#define WM97XX_GPIO_8 (1 << 8) +#define WM97XX_GPIO_9 (1 << 9) +#define WM97XX_GPIO_10 (1 << 10) +#define WM97XX_GPIO_11 (1 << 11) +#define WM97XX_GPIO_12 (1 << 12) +#define WM97XX_GPIO_13 (1 << 13) +#define WM97XX_GPIO_14 (1 << 14) +#define WM97XX_GPIO_15 (1 << 15) + + +#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ + + +/*---------------- Return codes from sample reading functions ---------------*/ + +/* More data is available; call the sample gathering function again */ +#define RC_AGAIN 0x00000001 +/* The returned sample is valid */ +#define RC_VALID 0x00000002 +/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ +#define RC_PENUP 0x00000004 +/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful + to tell the handler that the pen is down but we don't know yet his coords, + so the handler should not sleep or wait for pendown irq) */ +#define RC_PENDOWN 0x00000008 + +/* The wm97xx driver provides a private API for writing platform-specific + * drivers. + */ + +/* The structure used to return arch specific sampled data into */ +struct wm97xx_data { + int x; + int y; + int p; +}; + +/* Codec GPIO status + */ +typedef enum { + WM97XX_GPIO_HIGH, + WM97XX_GPIO_LOW +} wm97xx_gpio_status_t; + +/* Codec GPIO direction + */ +typedef enum { + WM97XX_GPIO_IN, + WM97XX_GPIO_OUT +} wm97xx_gpio_dir_t; + +/* Codec GPIO polarity + */ +typedef enum { + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_POL_LOW +} wm97xx_gpio_pol_t; + +/* Codec GPIO sticky + */ +typedef enum { + WM97XX_GPIO_STICKY, + WM97XX_GPIO_NOTSTICKY +} wm97xx_gpio_sticky_t; + +/* Codec GPIO wake + */ +typedef enum { + WM97XX_GPIO_WAKE, + WM97XX_GPIO_NOWAKE +} wm97xx_gpio_wake_t; + +/* + * codec event callback. + */ +typedef int (*codec_event_t)(int status); + +/* + * arch apecific continuous mode read callback + */ +typedef int (*codec_read_t)(struct input_dev* idev, int pen_down_time, int pressure); + +struct wm97xx; +struct wm97xx_codec; +struct wm97xx_auxconv_data; + +/* + * Codec interface + */ +struct wm97xx_codec{ + int (*poll_sample)(struct wm97xx*, int adcsel, int *sample); /* read 1 sample */ + int (*poll_coord)(struct wm97xx*, struct wm97xx_data*); /* read X,Y,[P} in coord */ + int (*poll_touch)(struct wm97xx*, struct wm97xx_data*); /* read X,Y,[P] in poll */ + void (*init_phy)(struct wm97xx*); + void (*codec_suspend)(struct wm97xx*); + void (*codec_resume)(struct wm97xx*); + void (*digitiser_save)(struct wm97xx*); + void (*digitiser_restore)(struct wm97xx*); + void (*aux_prepare)(struct wm97xx*); + void (*set_continuous_mode)(struct wm97xx*, int enable, int slot, int rate); + void (*digitiser_start)(struct wm97xx*); + void (*digitiser_stop)(struct wm97xx*); +}; + +struct wm97xx { + struct mcp *mcp; /* this needs to be first */ + u16 dig1, dig2, dig3, id; /* Cached codec registers */ + struct wm97xx_codec* wm97xx_codec; + struct input_dev wm97xx_input; + struct device* dev; + int pressure; + int mode; + struct completion ts_init; + struct completion ts_exit; + struct task_struct *ts_task; + codec_read_t cont_read_sample; /* continuous mode reader */ + unsigned int pen_irq; /* Pen IRQ number in use */ + unsigned int pen_irq_count; + unsigned int codec_irq; /* Codec IRQ number in use */ + unsigned int codec_irq_count; + wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ + int codec_irq_ref_count; /* Number of gpio irq users */ + struct workqueue_struct *pen_irq_workq; + struct work_struct pen_event_work; + struct workqueue_struct *codec_event_workq; + struct work_struct codec_event_work; + codec_event_t codec_event[WM97XX_MAX_GPIO]; /* codec event handlers */ + struct class_device cdev; + spinlock_t lock; /* Hardware lock */ + struct semaphore sem; + unsigned int pen_is_down; /* Pen down time */ + unsigned int ts_use_count; + unsigned aux_waiting:1; /* aux measurement waiting */ + unsigned ac97_link:1; /* interrupt over AC link */ + unsigned pen_probably_down:1; /* used in polling and coordinate modes */ + unsigned pen_is_up:1; /* pen is up */ +#if defined(CONFIG_PM) + unsigned phone_pga:5; /* PGA settings for PM */ + unsigned line_pga:16; + unsigned mic_pga:16; +#endif +}; + +#define classdev_to_wm97xx(cd) container_of(cd, struct wm97xx, cdev) + +int wm97xx_register_interface(struct class_interface *intf); +void wm97xx_unregister_interface(struct class_interface *intf); + +/* Enable or disable continuous mode. The driver itself doesn't + * support continuous mode since it is highly architecture-specific. + */ +extern void wm97xx_set_continuous_mode (struct wm97xx *wm, codec_read_t read, int enable, int slot, int rate); + +/* Codec IRQ access + * The codec can generate IRQ's (over AC97 link or IRQ pin) for + * numerous external or internal events. This is used to + * enable and disable codec IRQ's + */ +extern int wm97xx_set_codec_irq(struct wm97xx *wm, int irq, int link); +extern int wm97xx_clear_codec_irq(struct wm97xx *wm); + +/* Register interest in external/internal codec events */ +extern int wm97xx_request_codec_event(struct wm97xx *wm, int gpio, codec_event_t work); +extern void wm97xx_free_codec_event(struct wm97xx *wm, int gpio); + +/* Codec PEN IRQ access + * The codec can additionally generate an IRQ (using a dedicated pin) + * for Pen down events. This is used to enable and disable codec + * Pen down IRQ's + */ +extern int wm97xx_set_pen_irq(struct wm97xx *wm, int irq); +extern void wm97xx_clear_pen_irq(struct wm97xx *wm); + +/* Codec GPIO access (not supported on WM9705) + * This can be used to set/get codec GPIO and Virtual GPIO status. + */ +extern wm97xx_gpio_status_t wm97xx_get_codec_gpio (struct wm97xx *wm, u32 gpio); +extern void wm97xx_set_codec_gpio (struct wm97xx *wm, u32 gpio, wm97xx_gpio_status_t status); +extern void wm97xx_config_codec_gpio (struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, wm97xx_gpio_pol_t pol, + wm97xx_gpio_sticky_t sticky, wm97xx_gpio_wake_t wake); + +void wm97xx_ts_input_close(struct input_dev *idev); +int wm97xx_ts_input_open(struct input_dev *idev); + +#endif diff -Nur linux-2.6.9-intc1/drivers/misc/xircom_gem.c linux-2.6.9-intc1-mih1/drivers/misc/xircom_gem.c --- linux-2.6.9-intc1/drivers/misc/xircom_gem.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/misc/xircom_gem.c 2005-03-17 18:46:22.000000000 -0500 @@ -0,0 +1,157 @@ +/* + * xircom_gem.c: adds entries to /proc fs to control Xircom/Intel modem + * - write 1 to assert, delay 250ms, and deassert pin + * - write 2 to assert pin, write 0 to deassert pin + * + * Copyright (c) 2005 Matthias Ihmig + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MODULE_VERS "1.0" +#define MODULE_NAME "xircom_gem" + +static struct proc_dir_entry *gem_dir, *asleep, *power_down, *wake_up, *reset; + +static int proc_read_gem(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "%s\n", (GPIO_lev( (int)data) ? "0" : "1")); // active low signal -> invert +} + +static int proc_write_gem(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int len=0; + char value='0'; + + if(count >= 1) + len = 1; + else + return -EFAULT; + + if(copy_from_user(&value, buffer, len)) + return -EFAULT; + + if (value == '1' || value == '2') + GPIO_clr( (int)data ); // assert pin (active low) + if (value == '1') + mdelay(250); // wait 250ms when value=1 + if (value == '1' || value == '0') + GPIO_set( (int)data ); // deassert pin + return len; +} + +static int __init init_xircom_gem(void) +{ + int rv = 0; + + // Initialize GPIO registers + pxa_gpio_mode(GLN_GPIO_nGEM_WAKE | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_nGEM_SLEEP | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nGEM_RST | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_nGEM_PWR_DWN|GPIO_OUT); + + /* create directory */ + gem_dir = proc_mkdir("driver/" MODULE_NAME, NULL); + if(gem_dir == NULL) { + rv = -ENOMEM; goto out; + } + gem_dir->owner = THIS_MODULE; + + asleep = create_proc_read_entry("asleep", 0444, gem_dir, proc_read_gem, (void*)GLN_GPIO_nGEM_SLEEP); + if(asleep == NULL) { + rv = -ENOMEM; goto no_asleep; + } + asleep->owner = THIS_MODULE; + + + power_down = create_proc_entry("power_down", 0644, gem_dir); + if(power_down == NULL) { + rv = -ENOMEM; + goto no_power_down; + } + power_down->data = (void*)GLN_GPIO_nGEM_PWR_DWN; + power_down->read_proc = proc_read_gem; + power_down->write_proc = proc_write_gem; + power_down->owner = THIS_MODULE; + + + wake_up = create_proc_entry("wake_up", 0644, gem_dir); + if(wake_up == NULL) { + rv = -ENOMEM; + goto no_wake_up; + } + wake_up->data = (void*)GLN_GPIO_nGEM_WAKE; + wake_up->read_proc = proc_read_gem; + wake_up->write_proc = proc_write_gem; + wake_up->owner = THIS_MODULE; + + + reset = create_proc_entry("reset", 0644, gem_dir); + if(reset == NULL) { + rv = -ENOMEM; + goto no_reset; + } + reset->data = (void*)GLN_GPIO_nGEM_RST; + reset->read_proc = proc_read_gem; + reset->write_proc = proc_write_gem; + reset->owner = THIS_MODULE; + + /* everything OK */ + printk(KERN_INFO "%s %s proc interface initialised\n", + MODULE_NAME, MODULE_VERS); + return 0; + +no_reset: + remove_proc_entry("wake_up", gem_dir); +no_wake_up: + remove_proc_entry("power_down", gem_dir); +no_power_down: + remove_proc_entry("asleep", gem_dir); +no_asleep: + remove_proc_entry(MODULE_NAME, NULL); +out: + return rv; +} + +static void __exit cleanup_xircom_gem(void) +{ + remove_proc_entry("power_down", gem_dir); + remove_proc_entry("wake_up", gem_dir); + remove_proc_entry("reset", gem_dir); + remove_proc_entry("asleep", gem_dir); + remove_proc_entry(MODULE_NAME, NULL); + + printk(KERN_INFO "%s %s interface removed\n", + MODULE_NAME, MODULE_VERS); +} + +module_init(init_xircom_gem); +module_exit(cleanup_xircom_gem); + +MODULE_AUTHOR("Matthias Ihmig "); +MODULE_DESCRIPTION("Xircom/Intel GEM module /proc interface"); +MODULE_LICENSE("GPL"); + diff -Nur linux-2.6.9-intc1/drivers/mmc/mmc_block.c linux-2.6.9-intc1-mih1/drivers/mmc/mmc_block.c --- linux-2.6.9-intc1/drivers/mmc/mmc_block.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/mmc_block.c 2005-02-26 18:32:06.000000000 -0500 @@ -180,7 +180,7 @@ brq.cmd.arg = req->sector << 9; brq.cmd.flags = MMC_RSP_R1; - brq.data.req = req; +// brq.data.req = req; // needed? brq.data.timeout_ns = card->csd.tacc_ns * 10; brq.data.timeout_clks = card->csd.tacc_clks * 10; brq.data.blksz_bits = md->block_bits; @@ -200,6 +200,9 @@ } brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL; + brq.data.sg = mq->sg; + brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); + mmc_wait_for_req(card->host, &brq.mrq); if (brq.cmd.error) { printk(KERN_ERR "%s: error %d sending read/write command\n", @@ -330,6 +333,18 @@ md->disk->queue = md->queue.queue; md->disk->driverfs_dev = &card->dev; + /* + * As discussed on lkml, GENHD_FL_REMOVABLE should: + * + * - be set for removable media with permanent block devices + * - be unset for removable block devices with permanent media + * + * Since MMC block devices clearly fall under the second + * case, we do not set GENHD_FL_REMOVABLE. Userspace + * should use the block device creation/destruction hotplug + * messages to tell when the card is present. + */ + sprintf(md->disk->disk_name, "mmcblk%d", devidx); sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); diff -Nur linux-2.6.9-intc1/drivers/mmc/mmc.c linux-2.6.9-intc1-mih1/drivers/mmc/mmc.c --- linux-2.6.9-intc1/drivers/mmc/mmc.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/mmc.c 2005-05-01 01:52:39.000000000 -0400 @@ -1,11 +1,14 @@ /* - * linux/drivers/mmc/mmc.c + * linux/drivers/mmc/mmc.c (with parts backported from 2.6.11) * * Copyright (C) 2003-2004 Russell King, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * SD support (c) 2004 Ian Molton. + * */ #include #include @@ -216,8 +219,33 @@ cmd.flags = MMC_RSP_R1; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + + if(err != MMC_ERR_NONE) + goto out; + + /* Select 4 bit mode on SD cards note: a 4 bit mmc standard is + * on the table... watch this space. + */ + if(host->flags & HOST_CAP_SD_4_BIT && + card->state & MMC_STATE_IS_SD) { + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if(err != MMC_ERR_NONE) { + goto out; + } + cmd.opcode = SD_APP_SET_BUS_WIDTH; + cmd.arg = 2; + cmd.flags = MMC_RSP_R1 | MMC_FLAG_ACMD; + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if(err != MMC_ERR_NONE) + DBG("Error selecting 4 bit bus mode\n"); + } + } +out: return err; } @@ -299,19 +327,20 @@ return ocr; } -#define UNSTUFF_BITS(resp,start,size) \ + #define UNSTUFF_BITS(resp,start,size) \ ({ \ - const u32 __mask = (1 << (size)) - 1; \ + const int __size = size; \ + const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ const int __off = 3 - ((start) / 32); \ const int __shft = (start) & 31; \ u32 __res; \ \ __res = resp[__off] >> __shft; \ - if ((size) + __shft >= 32) \ - __res |= resp[__off-1] << (32 - __shft); \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ __res & __mask; \ }) - + /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ @@ -327,6 +356,38 @@ */ switch (card->csd.mmca_vsn) { case 0: /* MMC v1.? */ + /* + * Based on what I know, this is a SD card -Ian Molton + * + * Unknown card (OEM card 'Traveller') + * CID: 01504153 31323842 4134758d d0003400 + * CSD: 005d0132 135983d3 f6da4fff 16400000 + * Sandisk 32MB SD + * CID: 03534453 44303332 318e01af 22002b00 + * CSD: 00260032 1f5981d2 ecb5cfff 92404000 + * Lexar 256MB SD (40x) + * CID: 154d4353 4d492020 00120456 69003a00 + * CSD: 007f0032 1f5983cf f6dabf9f 96600000 + * Sandisk 128MB card: + * CID: 03534453 44313238 31100b03 9e003500 + * CSD: 00260032 1f5983c0 ecb64fff 92404000 + */ + 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); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); + card->cid.serial = UNSTUFF_BITS(resp, 24, 32); + card->cid.year = UNSTUFF_BITS(resp, 12, 8); + card->cid.month = UNSTUFF_BITS(resp, 8, 4); + + card->cid.year += 2000; /* SD cards year offset */ + break; + case 1: /* MMC v1.4 */ card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); @@ -369,7 +430,7 @@ /* * Given a 128-bit response, decode to our card CSD structure. */ -static void mmc_decode_csd(struct mmc_card *card) +static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; unsigned int e, m, csd_struct; @@ -380,11 +441,11 @@ * v2 has extra information in bits 15, 11 and 10. */ csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { + if (csd_struct !=0 && csd_struct != 1 && csd_struct != 2) { printk("%s: unrecognised CSD structure version %d\n", card->host->host_name, csd_struct); mmc_card_set_bad(card); - return; + return 1; } csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); @@ -403,6 +464,8 @@ csd->capacity = (1 + m) << (e + 2); csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + + return 0; } /* @@ -423,10 +486,9 @@ * Allocate a new MMC card, and assign a unique RCA. */ static struct mmc_card * -mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca) +mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) { - struct mmc_card *card, *c; - unsigned int rca = *frca; + struct mmc_card *card; card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); if (!card) @@ -435,18 +497,24 @@ mmc_init_card(card, host); memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); + return card; +} + +/* + * allocate a unique RCA + */ +static unsigned int mmc_alloc_rca(struct mmc_host *host, unsigned short *frca) { + unsigned short rca = *frca; + struct mmc_card *card; again: - list_for_each_entry(c, &host->cards, node) - if (c->rca == rca) { + list_for_each_entry(card, &host->cards, node) { + if (card->rca == rca) { rca++; goto again; } - - card->rca = rca; - - *frca = rca; - - return card; + } + printk("Allocated rca: %04x\n", rca); + return rca; } /* @@ -495,31 +563,50 @@ host->ops->set_ios(host, &host->ios); } -static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr, int init_opcode, int *is_sd) { struct mmc_command cmd; int i, err = 0; - - cmd.opcode = MMC_SEND_OP_COND; - cmd.arg = ocr; - cmd.flags = MMC_RSP_R3; + int init_flags = 0; for (i = 100; i; i--) { + if(init_opcode == SD_APP_OP_COND) { + cmd.opcode = MMC_APP_CMD; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1; + err = mmc_wait_for_cmd(host, &cmd, 0); + + if(err != MMC_ERR_NONE) + init_opcode = MMC_SEND_OP_COND; // fallback to MMC + else + init_flags = MMC_FLAG_ACMD; + } + + cmd.opcode = init_opcode; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | init_flags; err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) break; - if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) { + if(rocr) + *rocr = cmd.resp[0]; + if(is_sd) { + if(init_opcode == SD_APP_OP_COND) + *is_sd = 1; + else + *is_sd = 0; + } break; + } err = MMC_ERR_TIMEOUT; mmc_delay(10); } - if (rocr) - *rocr = cmd.resp[0]; - return err; } @@ -531,10 +618,11 @@ * Create a mmc_card entry for each discovered card, assigning * it an RCA, and save the raw CID for decoding later. */ -static void mmc_discover_cards(struct mmc_host *host) +static void mmc_discover_cards(struct mmc_host *host, int is_sd) { struct mmc_card *card; - unsigned int first_rca = 1, err; + unsigned int err; + unsigned short frca = 1; while (1) { struct mmc_command cmd; @@ -556,7 +644,7 @@ card = mmc_find_card(host, cmd.resp); if (!card) { - card = mmc_alloc_card(host, cmd.resp, &first_rca); + card = mmc_alloc_card(host, cmd.resp); if (IS_ERR(card)) { err = PTR_ERR(card); break; @@ -564,15 +652,38 @@ list_add(&card->node, &host->cards); } + /* SD cards allocate their own RCA. MMC cards have one allocated + * for them by us. + */ card->state &= ~MMC_STATE_DEAD; + if(is_sd){ + cmd.opcode = MMC_SEND_RELATIVE_ADDR; + cmd.arg = 0; // ignored + cmd.flags = MMC_RSP_R6; + card->state |= MMC_STATE_IS_SD; + } + else{ + card->rca = mmc_alloc_rca(host, &frca); + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + } - cmd.opcode = MMC_SET_RELATIVE_ADDR; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1; - + /* Sandisk say: should ignore ILLEGAL_COMMAND errors + * as CMD{0,1,2} dont clear status and an SD probe can + * upset MMC cards. + */ err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) - mmc_card_set_dead(card); + if(err != MMC_ERR_INVALID) + mmc_card_set_dead(card); + + if(is_sd){ + card->rca = cmd.resp[0] >> 16; + printk("Card wants rca: %08x\n", cmd.resp[0]); + frca = card->rca; + } } } @@ -599,7 +710,10 @@ memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd)); - mmc_decode_csd(card); + if (mmc_decode_csd(card)) { + card->state |= MMC_STATE_DEAD; + continue; + } mmc_decode_cid(card); } } @@ -652,28 +766,11 @@ static void mmc_setup(struct mmc_host *host) { - if (host->ios.power_mode != MMC_POWER_ON) { - int err; - u32 ocr; - - mmc_power_up(host); - mmc_idle_cards(host); - - err = mmc_send_op_cond(host, 0, &ocr); - if (err != MMC_ERR_NONE) - return; - - host->ocr = mmc_select_voltage(host, ocr); - - /* - * Since we're changing the OCR value, we seem to - * need to tell some cards to go back to the idle - * state. We wait 1ms to give cards time to - * respond. - */ - if (host->ocr) - mmc_idle_cards(host); - } else { + int err, is_sd; + u32 ocr; + struct mmc_command cmd; + /* Enter idle mode from whatever state we are in */ + if (host->ios.power_mode == MMC_POWER_ON) { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.clock = host->f_min; host->ops->set_ios(host, &host->ios); @@ -692,8 +789,15 @@ * they drop off the bus. */ } + else { + mmc_power_up(host); + err = mmc_send_op_cond(host, 0, &ocr, SD_APP_OP_COND, &is_sd); + if(err != MMC_ERR_NONE) + return; + host->ocr = mmc_select_voltage(host, ocr); + } - if (host->ocr == 0) + if (!host->ocr) return; /* @@ -701,9 +805,22 @@ * all get the idea that they should be ready for CMD2. * (My SanDisk card seems to need this.) */ - mmc_send_op_cond(host, host->ocr, NULL); - mmc_discover_cards(host); + /* Try SD cards first */ + if(is_sd){ + err = mmc_send_op_cond(host, host->ocr, NULL, SD_APP_OP_COND, NULL); + if(err == MMC_ERR_NONE) + mmc_discover_cards(host, 1); + } + + err = mmc_send_op_cond(host, host->ocr, NULL, MMC_SEND_OP_COND, NULL); + if(err == MMC_ERR_NONE) + mmc_discover_cards(host, 0); + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE; + mmc_wait_for_cmd(host, &cmd, 0); /* * Ok, now switch to push-pull mode. diff -Nur linux-2.6.9-intc1/drivers/mmc/mmci.c linux-2.6.9-intc1-mih1/drivers/mmc/mmci.c --- linux-2.6.9-intc1/drivers/mmc/mmci.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/mmci.c 2005-05-01 01:52:54.000000000 -0400 @@ -14,14 +14,15 @@ #include #include #include -#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -69,7 +70,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) { unsigned int datactrl, timeout, irqmask; - void *base; + void __iomem *base; DBG(host, "blksz %04x blks %04x flags %08x\n", 1 << data->blksz_bits, data->blocks, data->flags); @@ -108,7 +109,7 @@ static void mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) { - void *base = host->base; + void __iomem *base = host->base; DBG(host, "op %02x arg %08x flags %08x\n", cmd->opcode, cmd->arg, cmd->flags); @@ -169,7 +170,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { - void *base = host->base; + void __iomem *base = host->base; host->cmd = NULL; @@ -193,7 +194,7 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) { - void *base = host->base; + void __iomem *base = host->base; char *ptr = buffer; u32 status; @@ -222,7 +223,7 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status) { - void *base = host->base; + void __iomem *base = host->base; char *ptr = buffer; do { @@ -251,7 +252,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) { struct mmci_host *host = dev_id; - void *base = host->base; + void __iomem *base = host->base; u32 status; status = readl(base + MMCISTATUS); @@ -501,7 +502,7 @@ * We can do SGIO */ mmc->max_hw_segs = 16; - mmc->max_phys_segs = 16; + mmc->max_phys_segs = NR_SG; /* * Since we only have a 16-bit data length register, we must diff -Nur linux-2.6.9-intc1/drivers/mmc/mmci.h linux-2.6.9-intc1-mih1/drivers/mmc/mmci.h --- linux-2.6.9-intc1/drivers/mmc/mmci.h 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/mmci.h 2005-02-22 18:30:18.000000000 -0500 @@ -120,7 +120,7 @@ struct clk; struct mmci_host { - void *base; + void __iomem *base; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; @@ -139,7 +139,6 @@ struct timer_list timer; unsigned int oldstat; - struct scatterlist sg[NR_SG]; unsigned int sg_len; /* pio stuff */ @@ -156,8 +155,8 @@ /* * Ideally, we want the higher levels to pass us a scatter list. */ - host->sg_len = blk_rq_map_sg(req->q, req, sg); - host->sg_ptr = sg; + host->sg_len = data->sg_len; + host->sg_ptr = data->sg; host->sg_off = 0; } diff -Nur linux-2.6.9-intc1/drivers/mmc/mmc_queue.c linux-2.6.9-intc1-mih1/drivers/mmc/mmc_queue.c --- linux-2.6.9-intc1/drivers/mmc/mmc_queue.c 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/mmc_queue.c 2005-02-22 18:27:58.000000000 -0500 @@ -80,7 +80,7 @@ set_current_state(TASK_INTERRUPTIBLE); if (!blk_queue_plugged(q)) mq->req = req = elv_next_request(q); - spin_unlock(q->queue_lock); + spin_unlock_irq(q->queue_lock); if (!req) { if (mq->flags & MMC_QUEUE_EXIT) @@ -147,19 +147,31 @@ mq->queue->queuedata = mq; mq->req = NULL; + mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, + GFP_KERNEL); + if (!mq->sg) { + ret = -ENOMEM; + goto cleanup; + } + init_completion(&mq->thread_complete); init_waitqueue_head(&mq->thread_wq); init_MUTEX(&mq->thread_sem); ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL); - if (ret < 0) { - blk_cleanup_queue(mq->queue); - } else { + if (ret >= 0) { wait_for_completion(&mq->thread_complete); init_completion(&mq->thread_complete); ret = 0; + goto out; } + cleanup: + kfree(mq->sg); + mq->sg = NULL; + + blk_cleanup_queue(mq->queue); + out: return ret; } EXPORT_SYMBOL(mmc_init_queue); @@ -169,6 +181,10 @@ mq->flags |= MMC_QUEUE_EXIT; wake_up(&mq->thread_wq); wait_for_completion(&mq->thread_complete); + + kfree(mq->sg); + mq->sg = NULL; + blk_cleanup_queue(mq->queue); mq->card = NULL; diff -Nur linux-2.6.9-intc1/drivers/mmc/mmc_queue.h linux-2.6.9-intc1-mih1/drivers/mmc/mmc_queue.h --- linux-2.6.9-intc1/drivers/mmc/mmc_queue.h 2004-10-18 17:53:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/mmc_queue.h 2005-02-22 18:28:13.000000000 -0500 @@ -15,6 +15,7 @@ int (*issue_fn)(struct mmc_queue *, struct request *); void *data; struct request_queue *queue; + struct scatterlist *sg; }; struct mmc_io_request { diff -Nur linux-2.6.9-intc1/drivers/mmc/pxamci.c linux-2.6.9-intc1-mih1/drivers/mmc/pxamci.c --- linux-2.6.9-intc1/drivers/mmc/pxamci.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/pxamci.c 2005-05-02 15:03:10.000000000 -0400 @@ -1,5 +1,5 @@ /* - * linux/drivers/mmc/pxa.c - PXA MMCI driver + * linux/drivers/mmc/pxa.c - PXA MMCI driver (backport) * * Copyright (C) 2003 Russell King, All Rights Reserved. * @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include #include #include @@ -44,6 +44,10 @@ #define DBG(x...) do { } while (0) #endif +#define DRIVER_NAME "pxa2xx-mci" + +#define NR_SG 1 + struct pxamci_host { struct mmc_host *mmc; spinlock_t lock; @@ -63,17 +67,10 @@ dma_addr_t sg_dma; struct pxa_dma_desc *sg_cpu; - - dma_addr_t dma_buf; - unsigned int dma_size; + unsigned int dma_len; unsigned int dma_dir; }; -/* - * The base MMC clock rate - */ -#define CLOCKRATE 20000000 - static inline unsigned int ns_to_clocks(unsigned int ns) { return (ns * (CLOCKRATE / 1000000) + 999) / 1000; @@ -122,8 +119,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) { unsigned int nob = data->blocks; - unsigned int timeout, size; - dma_addr_t dma; + unsigned int timeout; u32 dcmd; int i; @@ -152,35 +148,22 @@ dcmd |= DCMD_BURST32 | DCMD_WIDTH1; - host->dma_size = data->blocks << data->blksz_bits; - host->dma_buf = dma_map_single(mmc_dev(host->mmc), data->req->buffer, - host->dma_size, host->dma_dir); - - for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) { - u32 len = size; - - if (len > DCMD_LENGTH) - len = 0x1000; + host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma_dir); + for (i = 0; i < host->dma_len; i++) { if (data->flags & MMC_DATA_READ) { host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; - host->sg_cpu[i].dtadr = dma; + host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); } else { - host->sg_cpu[i].dsadr = dma; + host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; } - host->sg_cpu[i].dcmd = dcmd | len; - - dma += len; - size -= len; - - if (size) { - host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * - sizeof(struct pxa_dma_desc); - } else { - host->sg_cpu[i].ddadr = DDADR_STOP; - } + host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]); + host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * + sizeof(struct pxa_dma_desc); } + host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; wmb(); DDADR(host->dma) = host->sg_dma; @@ -189,25 +172,35 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) { + struct mmc_data *data = host->data; WARN_ON(host->cmd != NULL); host->cmd = cmd; if (cmd->flags & MMC_RSP_BUSY) cmdat |= CMDAT_BUSY; - switch (cmd->flags & (MMC_RSP_MASK | MMC_RSP_CRC)) { - case MMC_RSP_SHORT | MMC_RSP_CRC: - cmdat |= CMDAT_RESP_SHORT; - break; - case MMC_RSP_SHORT: - cmdat |= CMDAT_RESP_R3; - break; - case MMC_RSP_LONG | MMC_RSP_CRC: - cmdat |= CMDAT_RESP_R2; - break; - default: - break; - } + switch (cmd->flags & MMC_RSP_MASK) { + case MMC_RSP_NONE: + cmdat |= CMDAT_RESP_NONE; break; + case MMC_RSP_R1: + case MMC_RSP_R1B: + case MMC_RSP_R6: + cmdat |= CMDAT_RESP_SHORT; + break; + case MMC_RSP_R2: + cmdat |= CMDAT_RESP_R2; break; + case MMC_RSP_R3: + cmdat |= CMDAT_RESP_R3; break; + default: printk("Unknown cmd->flags & MMC_RSP_MASK: %02x\n", cmdat & MMC_RSP_MASK); break; + } + + if (data) { + cmdat |= CMDAT_DATAEN; + if(cmd->flags & MMC_DATA_WRITE) + cmdat |= CMDAT_WRITE; + if(cmd->flags & MMC_DATA_STREAM) + cmdat |= CMDAT_STREAM; + } writel(cmd->opcode, host->base + MMC_CMD); writel(cmd->arg >> 16, host->base + MMC_ARGH); @@ -255,7 +248,24 @@ if (stat & STAT_TIME_OUT_RESPONSE) { cmd->error = MMC_ERR_TIMEOUT; } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { +#ifdef CONFIG_PXA27x + /* + * workaround for erratum #42: + * Intel PXA27x Family Processor Specification Update Rev 001 + */ + if (cmd->opcode == MMC_ALL_SEND_CID || + cmd->opcode == MMC_SEND_CSD || + cmd->opcode == MMC_SEND_CID) { + /* a bogus CRC error can appear if the msb of + the 15 byte response is a one */ + if ((cmd->resp[0] & 0x80000000) == 0) + cmd->error = MMC_ERR_BADCRC; + } else { + DBG("ignoring CRC from command %d - *risky*\n",cmd->opcode); + } +#else cmd->error = MMC_ERR_BADCRC; +#endif } pxamci_disable_irq(host, END_CMD_RES); @@ -276,8 +286,8 @@ return 0; DCSR(host->dma) = 0; - dma_unmap_single(mmc_dev(host->mmc), host->dma_buf, host->dma_size, - host->dma_dir); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, + host->dma_dir); if (stat & STAT_READ_TIME_OUT) data->error = MMC_ERR_TIMEOUT; @@ -374,15 +384,14 @@ if (CLOCKRATE / clk > ios->clock) clk <<= 1; host->clkrt = fls(clk) - 1; + pxa_set_cken(CKEN12_MMC, 1); /* * we write clkrt on the next command */ - } else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { - /* - * Ensure that the clock is off. - */ - writel(STOP_CLOCK, host->base + MMC_STRPCL); + } else { + pxamci_stop_clock(host); + pxa_set_cken(CKEN12_MMC, 0); } if (host->power_mode != ios->power_mode) { @@ -429,7 +438,7 @@ if (!r || irq == NO_IRQ) return -ENXIO; - r = request_mem_region(r->start, SZ_4K, "PXAMCI"); + r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); if (!r) return -EBUSY; @@ -440,8 +449,19 @@ } mmc->ops = &pxamci_ops; - mmc->f_min = 312500; - mmc->f_max = 20000000; + mmc->f_min = CLOCKRATE_MIN; + mmc->f_max = CLOCKRATE_MAX; + + /* + * We can do SG-DMA, but we don't because we never know how much + * data we successfully wrote to the card. + */ + mmc->max_phys_segs = NR_SG; + + /* + * Our hardware DMA can handle a maximum of one page per SG entry. + */ + mmc->max_seg_size = PAGE_SIZE; host = mmc_priv(mmc); host->mmc = mmc; @@ -460,8 +480,7 @@ spin_lock_init(&host->lock); host->res = r; host->irq = irq; - host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| - END_CMD_RES|PRG_DONE|DATA_TRAN_DONE; + host->imask = MMC_I_MASK_ALL; host->base = ioremap(r->start, SZ_4K); if (!host->base) { @@ -478,17 +497,14 @@ writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - pxa_gpio_mode(GPIO6_MMCCLK_MD); - pxa_gpio_mode(GPIO8_MMCCS0_MD); - pxa_set_cken(CKEN12_MMC, 1); - - host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host); + host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, + pxamci_dma_irq, host); if (host->dma < 0) { ret = -EBUSY; goto out; } - ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host); + ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; @@ -535,15 +551,14 @@ END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, host->base + MMC_I_MASK); - pxa_set_cken(CKEN12_MMC, 0); + DRCMRRXMMC = 0; + DRCMRTXMMC = 0; free_irq(host->irq, host); pxa_free_dma(host->dma); iounmap(host->base); dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - pxa_set_cken(CKEN12_MMC, 0); - release_resource(host->res); mmc_free_host(mmc); @@ -579,7 +594,7 @@ #endif static struct device_driver pxamci_driver = { - .name = "pxa2xx-mci", + .name = DRIVER_NAME, .bus = &platform_bus_type, .probe = pxamci_probe, .remove = pxamci_remove, diff -Nur linux-2.6.9-intc1/drivers/mmc/pxamci.h linux-2.6.9-intc1-mih1/drivers/mmc/pxamci.h --- linux-2.6.9-intc1/drivers/mmc/pxamci.h 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mmc/pxamci.h 2005-02-22 18:51:08.000000000 -0500 @@ -70,6 +70,16 @@ #define BUF_PART_FULL (1 << 0) #define MMC_I_MASK 0x0028 + +/*PXA27x MMC interrupts*/ +#define SDIO_SUSPEND_ACK (1 << 12) +#define SDIO_INT (1 << 11) +#define RD_STALLED (1 << 10) +#define RES_ERR (1 << 9) +#define DAT_ERR (1 << 8) +#define TINT (1 << 7) + +/*PXA2xx MMC interrupts*/ #define TXFIFO_WR_REQ (1 << 6) #define RXFIFO_RD_REQ (1 << 5) #define CLK_IS_OFF (1 << 4) @@ -78,6 +88,12 @@ #define PRG_DONE (1 << 1) #define DATA_TRAN_DONE (1 << 0) +#ifdef CONFIG_PXA27x +#define MMC_I_MASK_ALL 0x00001fff +#else +#define MMC_I_MASK_ALL 0x0000007f +#endif + #define MMC_I_REG 0x002c /* same as MMC_I_MASK */ @@ -92,3 +108,17 @@ #define MMC_RXFIFO 0x0040 /* 8 bit */ #define MMC_TXFIFO 0x0044 /* 8 bit */ + +/* + * The base MMC clock rate + */ +#ifdef CONFIG_PXA27x +#define CLOCKRATE_MIN 304688 +#define CLOCKRATE_MAX 19500000 +#else +#define CLOCKRATE_MIN 312500 +#define CLOCKRATE_MAX 20000000 +#endif + +#define CLOCKRATE CLOCKRATE_MAX + diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/amd_flash.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/amd_flash.c --- linux-2.6.9-intc1/drivers/mtd/chips/amd_flash.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/amd_flash.c 2005-03-21 21:28:42.000000000 -0500 @@ -3,7 +3,7 @@ * * Author: Jonas Holmberg * - * $Id: amd_flash.c,v 1.25 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $ * * Copyright (c) 2001 Axis Communications AB * @@ -67,7 +67,6 @@ #define AM29LV160DT 0x22C4 #define AM29LV160DB 0x2249 #define AM29BDS323D 0x22D1 -#define AM29BDS643D 0x227E /* Atmel */ #define AT49xV16x 0x00C0 @@ -618,17 +617,6 @@ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, } }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29BDS643D, - .name = "AMD AM29BDS643D", - .size = 0x00800000, - .numeraseregions = 3, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, - { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, - } - }, { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49xV16x, .name = "Atmel AT49xV16x", @@ -1122,7 +1110,7 @@ timeo = jiffies + (HZ * 20); spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); while (flash_is_busy(map, adr, private->interleave)) { diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/cfi_cmdset_0001.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_cmdset_0001.c --- linux-2.6.9-intc1/drivers/mtd/chips/cfi_cmdset_0001.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_cmdset_0001.c 2005-03-31 21:31:20.000000000 -0500 @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.154 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.174 2005/04/01 01:59:52 nico Exp $ * * * 10/10/2000 Nicolas Pitre @@ -29,40 +29,63 @@ #include #include #include +#include +#include #include #include #include #include /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ +/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */ // debugging, turns off buffer write mode if set to 1 #define FORCE_WORD_WRITE 0 +#define MANUFACTURER_INTEL 0x0089 +#define I82802AB 0x00ad +#define I82802AC 0x00ac +#define MANUFACTURER_ST 0x0020 +#define M50LPW080 0x002F + static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -//static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_intelext_sync (struct mtd_info *); static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +#ifdef CONFIG_MTD_OTP +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); +static int cfi_intelext_get_fact_prot_info (struct mtd_info *, + struct otp_info *, size_t); +static int cfi_intelext_get_user_prot_info (struct mtd_info *, + struct otp_info *, size_t); +#endif static int cfi_intelext_suspend (struct mtd_info *); static void cfi_intelext_resume (struct mtd_info *); +static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *); static void cfi_intelext_destroy(struct mtd_info *); struct mtd_info *cfi_cmdset_0001(struct map_info *, int); -static struct mtd_info *cfi_intelext_setup (struct map_info *); -static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **); +static struct mtd_info *cfi_intelext_setup (struct mtd_info *); +static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **); static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len); +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); +#include "fwh_lock.h" + + /* * *********** SETUP AND PROBE BITS *********** @@ -123,8 +146,9 @@ #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE /* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ -static void fixup_intel_strataflash(struct map_info *map, void* param) +static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_amdstd *extp = cfi->cmdset_priv; @@ -134,16 +158,32 @@ } #endif -static void fixup_st_m28w320ct(struct map_info *map, void* param) +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND +static void fixup_no_write_suspend(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + if (cfip && (cfip->FeatureSupport&4)) { + cfip->FeatureSupport &= ~4; + printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); + } +} +#endif + +static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ } -static void fixup_st_m28w320cb(struct map_info *map, void* param) +static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; /* Note this is done after the region info is endian swapped */ @@ -151,26 +191,117 @@ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; }; -static struct cfi_fixup fixup_table[] = { +static void fixup_use_point(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + if (!mtd->point && map_is_linear(map)) { + mtd->point = cfi_intelext_point; + mtd->unpoint = cfi_intelext_unpoint; + } +} + +static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if (cfi->cfiq->BufWriteTimeoutTyp) { + printk(KERN_INFO "Using buffer write method\n" ); + mtd->write = cfi_intelext_write_buffers; + } +} + +static struct cfi_fixup cfi_fixup_table[] = { #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE - { - CFI_MFR_ANY, CFI_ID_ANY, - fixup_intel_strataflash, NULL - }, + { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif - { - 0x0020, /* STMicroelectronics */ - 0x00ba, /* M28W320CT */ - fixup_st_m28w320ct, NULL - }, { - 0x0020, /* STMicroelectronics */ - 0x00bb, /* M28W320CB */ - fixup_st_m28w320cb, NULL - }, { - 0, 0, NULL, NULL - } +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND + { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL }, +#endif +#if !FORCE_WORD_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL }, +#endif + { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, + { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, + { 0, 0, NULL, NULL } +}; + +static struct cfi_fixup jedec_fixup_table[] = { + { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, + { 0, 0, NULL, NULL } +}; +static struct cfi_fixup fixup_table[] = { + /* The CFI vendor ids and the JEDEC vendor IDs appear + * to be common. It is like the devices id's are as + * well. This table is to pick all cases where + * we know that is the case. + */ + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL }, + { 0, 0, NULL, NULL } }; +static inline struct cfi_pri_intelext * +read_pri_intelext(struct map_info *map, __u16 adr) +{ + struct cfi_pri_intelext *extp; + unsigned int extp_size = sizeof(*extp); + + again: + extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp"); + if (!extp) + return NULL; + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); + extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + + if (extp->MajorVersion == '1' && extp->MinorVersion == '3') { + unsigned int extra_size = 0; + int nb_parts, i; + + /* Protection Register info */ + extra_size += (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); + + /* Burst Read info */ + extra_size += 6; + + /* Number of hardware-partitions */ + extra_size += 1; + if (extp_size < sizeof(*extp) + extra_size) + goto need_more; + nb_parts = extp->extra[extra_size - 1]; + + for (i = 0; i < nb_parts; i++) { + struct cfi_intelext_regioninfo *rinfo; + rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size]; + extra_size += sizeof(*rinfo); + if (extp_size < sizeof(*extp) + extra_size) + goto need_more; + rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions); + extra_size += (rinfo->NumBlockTypes - 1) + * sizeof(struct cfi_intelext_blockinfo); + } + + if (extp_size < sizeof(*extp) + extra_size) { + need_more: + extp_size = sizeof(*extp) + extra_size; + kfree(extp); + if (extp_size > 4096) { + printk(KERN_ERR + "%s: cfi_pri_intelext is too fat\n", + __FUNCTION__); + return NULL; + } + goto again; + } + } + + return extp; +} + /* This routine is made available to other mtd code via * inter_module_register. It must only be accessed through * inter_module_get which will bump the use count of this module. The @@ -181,8 +312,32 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; int i; + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->erase = cfi_intelext_erase_varsize; + mtd->read = cfi_intelext_read; + mtd->write = cfi_intelext_write_words; + mtd->sync = cfi_intelext_sync; + mtd->lock = cfi_intelext_lock; + mtd->unlock = cfi_intelext_unlock; + mtd->suspend = cfi_intelext_suspend; + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + + mtd->reboot_notifier.notifier_call = cfi_intelext_reboot; + if (cfi->cfi_mode == CFI_MODE_CFI) { /* * It's a real CFI chip, not one for which the probe @@ -192,20 +347,17 @@ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_intelext *extp; - extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp"); - if (!extp) + extp = read_pri_intelext(map, adr); + if (!extp) { + kfree(mtd); return NULL; - - /* Do some byteswapping if necessary */ - extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); - extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); - extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + } /* Install our own private info structure */ cfi->cmdset_priv = extp; - cfi_fixup(map, fixup_table); - + cfi_fixup(mtd, cfi_fixup_table); + #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ cfi_tell_features(extp); @@ -215,6 +367,12 @@ printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); } } + else if (cfi->cfi_mode == CFI_MODE_JEDEC) { + /* Apply jedec specific fixups */ + cfi_fixup(mtd, jedec_fixup_table); + } + /* Apply generic fixups */ + cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; @@ -225,28 +383,19 @@ map->fldrv = &cfi_intelext_chipdrv; - return cfi_intelext_setup(map); + return cfi_intelext_setup(mtd); } -static struct mtd_info *cfi_intelext_setup(struct map_info *map) +static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; unsigned long offset = 0; int i,j; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); - if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); - goto setup_err; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; @@ -286,41 +435,22 @@ mtd->eraseregions[i].numblocks); } - /* Also select the correct geometry setup too */ - mtd->erase = cfi_intelext_erase_varsize; - mtd->read = cfi_intelext_read; - - if (map_is_linear(map)) { - mtd->point = cfi_intelext_point; - mtd->unpoint = cfi_intelext_unpoint; - } - - if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { - printk(KERN_INFO "Using buffer write method\n" ); - mtd->write = cfi_intelext_write_buffers; - } else { - printk(KERN_INFO "Using word write method\n" ); - mtd->write = cfi_intelext_write_words; - } -#if 0 - mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; +#ifdef CONFIG_MTD_OTP mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; + mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg; + mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg; + mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info; + mtd->get_user_prot_info = cfi_intelext_get_user_prot_info; #endif - mtd->sync = cfi_intelext_sync; - mtd->lock = cfi_intelext_lock; - mtd->unlock = cfi_intelext_unlock; - mtd->suspend = cfi_intelext_suspend; - mtd->resume = cfi_intelext_resume; - mtd->flags = MTD_CAP_NORFLASH; - map->fldrv = &cfi_intelext_chipdrv; - mtd->name = map->name; /* This function has the potential to distort the reality a bit and therefore should be called last. */ - if (cfi_intelext_partition_fixup(map, &cfi) != 0) + if (cfi_intelext_partition_fixup(mtd, &cfi) != 0) goto setup_err; __module_get(THIS_MODULE); + register_reboot_notifier(&mtd->reboot_notifier); return mtd; setup_err: @@ -333,20 +463,16 @@ return NULL; } -static int cfi_intelext_partition_fixup(struct map_info *map, +static int cfi_intelext_partition_fixup(struct mtd_info *mtd, struct cfi_private **pcfi) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = *pcfi; struct cfi_pri_intelext *extp = cfi->cmdset_priv; /* * Probing of multi-partition flash ships. * - * This is extremely crude at the moment and should probably be - * extracted entirely from the Intel extended query data instead. - * Right now a L18 flash is assumed if multiple operations is - * detected. - * * To support multiple partitions when available, we simply arrange * for each of them to have their own flchip structure even if they * are on the same physical chip. This means completely recreating @@ -355,25 +481,50 @@ * arrangement at this point. This can be rearranged in the future * if someone feels motivated enough. --nico */ - if (extp && extp->FeatureSupport & (1 << 9)) { + if (extp && extp->MajorVersion == '1' && extp->MinorVersion == '3' + && extp->FeatureSupport & (1 << 9)) { struct cfi_private *newcfi; struct flchip *chip; struct flchip_shared *shared; - int numparts, partshift, numvirtchips, i, j; + int offs, numregions, numparts, partshift, numvirtchips, i, j; - /* - * The L18 flash memory array is divided - * into multiple 8-Mbit partitions. - */ + /* Protection Register info */ + offs = (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); + + /* Burst Read info */ + offs += 6; + + /* Number of partition regions */ + numregions = extp->extra[offs]; + offs += 1; + + /* Number of hardware partitions */ + numparts = 0; + for (i = 0; i < numregions; i++) { + struct cfi_intelext_regioninfo *rinfo; + rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[offs]; + numparts += rinfo->NumIdentPartitions; + offs += sizeof(*rinfo) + + (rinfo->NumBlockTypes - 1) * + sizeof(struct cfi_intelext_blockinfo); + } /* - * FIXME: Use 16-Mbit partitions for 2x32Mb flash; won't work - * for L18 with 8-Mbit partitions + * All functions below currently rely on all chips having + * the same geometry so we'll just assume that all hardware + * partitions are of the same size too. */ - numparts = 1 << (cfi->cfiq->DevSize - 21); - partshift = 21 + __ffs(cfi->interleave); - numvirtchips = cfi->numchips * numparts; + partshift = cfi->chipshift - __ffs(numparts); + + if ((1 << partshift) < mtd->erasesize) { + printk( KERN_ERR + "%s: bad number of hw partitions (%d)\n", + __FUNCTION__, numparts); + return -EINVAL; + } + numvirtchips = cfi->numchips * numparts; newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); if (!newcfi) return -ENOMEM; @@ -403,10 +554,10 @@ } } - printk(KERN_DEBUG "%s: %d sets of %d interleaved chips " - "--> %d partitions of %#x bytes\n", + printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips " + "--> %d partitions of %d KiB\n", map->name, cfi->numchips, cfi->interleave, - newcfi->numchips, 1<chipshift); + newcfi->numchips, 1<<(newcfi->chipshift-10)); map->fldrv_priv = newcfi; *pcfi = newcfi; @@ -431,7 +582,7 @@ resettime: timeo = jiffies + HZ; retry: - if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) { + if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { /* * OK. We have possibility for contension on the write/erase * operations which are global to the real chip and not per @@ -520,7 +671,8 @@ return 0; case FL_ERASING: - if (!(cfip->FeatureSupport & 2) || + if (!cfip || + !(cfip->FeatureSupport & 2) || !(mode == FL_READY || mode == FL_POINT || (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) goto sleep; @@ -564,6 +716,14 @@ chip->state = FL_STATUS; return 0; + case FL_XIP_WHILE_ERASING: + if (mode != FL_READY && mode != FL_POINT && + (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1))) + goto sleep; + chip->oldstate = chip->state; + chip->state = FL_READY; + return 0; + case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) @@ -588,7 +748,7 @@ if (chip->priv) { struct flchip_shared *shared = chip->priv; spin_lock(&shared->lock); - if (shared->writing == chip) { + if (shared->writing == chip && chip->oldstate == FL_READY) { /* We own the ability to write, but we're done */ shared->writing = shared->erasing; if (shared->writing && shared->writing != chip) { @@ -600,17 +760,24 @@ put_chip(map, loaner, loaner->start); spin_lock(chip->mutex); spin_unlock(loaner->mutex); - } else { - if (chip->oldstate != FL_ERASING) { - shared->erasing = NULL; - if (chip->oldstate != FL_WRITING) - shared->writing = NULL; - } - spin_unlock(&shared->lock); + wake_up(&chip->wq); + return; } - } else { + shared->erasing = NULL; + shared->writing = NULL; + } else if (shared->erasing == chip && shared->writing != chip) { + /* + * We own the ability to erase without the ability + * to write, which means the erase was suspended + * and some other partition is currently writing. + * Don't let the switch below mess things up since + * we don't have ownership to resume anything. + */ spin_unlock(&shared->lock); + wake_up(&chip->wq); + return; } + spin_unlock(&shared->lock); } switch(chip->oldstate) { @@ -631,6 +798,11 @@ chip->state = FL_ERASING; break; + case FL_XIP_WHILE_ERASING: + chip->state = chip->oldstate; + chip->oldstate = FL_READY; + break; + case FL_READY: case FL_STATUS: case FL_JEDEC_QUERY: @@ -643,6 +815,201 @@ wake_up(&chip->wq); } +#ifdef CONFIG_MTD_XIP + +/* + * No interrupt what so ever can be serviced while the flash isn't in array + * mode. This is ensured by the xip_disable() and xip_enable() functions + * enclosing any code path where the flash is known not to be in array mode. + * And within a XIP disabled code path, only functions marked with __xipram + * may be called and nothing else (it's a good thing to inspect generated + * assembly to make sure inline functions were actually inlined and that gcc + * didn't emit calls to its own support functions). Also configuring MTD CFI + * support to a single buswidth and a single interleave is also recommended. + * Note that not only IRQs are disabled but the preemption count is also + * increased to prevent other locking primitives (namely spin_unlock) from + * decrementing the preempt count to zero and scheduling the CPU away while + * not in array mode. + */ + +static void xip_disable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + /* TODO: chips with no XIP use should ignore and return */ + (void) map_read(map, adr); /* ensure mmu mapping is up to date */ + preempt_disable(); + local_irq_disable(); +} + +static void __xipram xip_enable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xff), adr); + chip->state = FL_READY; + } + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ + local_irq_enable(); + preempt_enable(); +} + +/* + * When a delay is required for the flash operation to complete, the + * xip_udelay() function is polling for both the given timeout and pending + * (but still masked) hardware interrupts. Whenever there is an interrupt + * pending then the flash erase or write operation is suspended, array mode + * restored and interrupts unmasked. Task scheduling might also happen at that + * point. The CPU eventually returns from the interrupt or the call to + * schedule() and the suspended flash operation is resumed for the remaining + * of the delay period. + * + * Warning: this function _will_ fool interrupt latency tracing tools. + */ + +static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, + unsigned long adr, int usec) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + map_word status, OK = CMD(0x80); + unsigned long suspended, start = xip_currtime(); + flstate_t oldstate, newstate; + + do { + cpu_relax(); + if (xip_irqpending() && cfip && + ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || + (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && + (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { + /* + * Let's suspend the erase or write operation when + * supported. Note that we currently don't try to + * suspend interleaved chips if there is already + * another operation suspended (imagine what happens + * when one chip was already done with the current + * operation while another chip suspended it, then + * we resume the whole thing at once). Yes, it + * can happen! + */ + map_write(map, CMD(0xb0), adr); + map_write(map, CMD(0x70), adr); + usec -= xip_elapsed_since(start); + suspended = xip_currtime(); + do { + if (xip_elapsed_since(suspended) > 100000) { + /* + * The chip doesn't want to suspend + * after waiting for 100 msecs. + * This is a critical error but there + * is not much we can do here. + */ + return; + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK)); + + /* Suspend succeeded */ + oldstate = chip->state; + if (oldstate == FL_ERASING) { + if (!map_word_bitsset(map, status, CMD(0x40))) + break; + newstate = FL_XIP_WHILE_ERASING; + chip->erase_suspended = 1; + } else { + if (!map_word_bitsset(map, status, CMD(0x04))) + break; + newstate = FL_XIP_WHILE_WRITING; + chip->write_suspended = 1; + } + chip->state = newstate; + map_write(map, CMD(0xff), adr); + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); + preempt_enable(); + asm volatile (".rep 8; nop; .endr"); + cond_resched(); + + /* + * We're back. However someone else might have + * decided to go write to the chip if we are in + * a suspended erase state. If so let's wait + * until it's done. + */ + preempt_disable(); + while (chip->state != newstate) { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + preempt_enable(); + schedule(); + remove_wait_queue(&chip->wq, &wait); + preempt_disable(); + } + /* Disallow XIP again */ + local_irq_disable(); + + /* Resume the write or erase operation */ + map_write(map, CMD(0xd0), adr); + map_write(map, CMD(0x70), adr); + chip->state = oldstate; + start = xip_currtime(); + } else if (usec >= 1000000/HZ) { + /* + * Try to save on CPU power when waiting delay + * is at least a system timer tick period. + * No need to be extremely accurate here. + */ + xip_cpu_idle(); + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK) + && xip_elapsed_since(start) < usec); +} + +#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec) + +/* + * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while + * the flash is actively programming or erasing since we have to poll for + * the operation to complete anyway. We can't do that in a generic way with + * a XIP setup so do it before the actual flash operation in this case. + */ +#undef INVALIDATE_CACHED_RANGE +#define INVALIDATE_CACHED_RANGE(x...) +#define XIP_INVAL_CACHED_RANGE(map, from, size) \ + do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) + +/* + * Extra notes: + * + * Activating this XIP support changes the way the code works a bit. For + * example the code to suspend the current process when concurrent access + * happens is never executed because xip_udelay() will always return with the + * same chip state as it was entered with. This is why there is no care for + * the presence of add_wait_queue() or schedule() calls from within a couple + * xip_disable()'d areas of code, like in do_erase_oneblock for example. + * The queueing and scheduling are always happening within xip_udelay(). + * + * Similarly, get_chip() and put_chip() just happen to always be executed + * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state + * is in array mode, therefore never executing many cases therein and not + * causing any problem with XIP. + */ + +#else + +#define xip_disable(map, chip, adr) +#define xip_enable(map, chip, adr) + +#define UDELAY(map, chip, adr, usec) cfi_udelay(usec) + +#define XIP_INVAL_CACHED_RANGE(x...) + +#endif + static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) { unsigned long cmd_addr; @@ -827,103 +1194,12 @@ } return ret; } -#if 0 -static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp = cfi->cmdset_priv; - struct flchip *chip; - int ofs_factor = cfi->interleave * cfi->device_type; - int count = len; - int chip_num, offst; - int ret; - chip_num = ((unsigned int)from/reg_sz); - offst = from - (reg_sz*chip_num)+base_offst; - - while (count) { - /* Calculate which chip & protection register offset we need */ - - if (chip_num >= cfi->numchips) - goto out; - - chip = &cfi->chips[chip_num]; - - spin_lock(chip->mutex); - ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); - if (ret) { - spin_unlock(chip->mutex); - return (len-count)?:ret; - } - - if (chip->state != FL_JEDEC_QUERY) { - map_write(map, CMD(0x90), chip->start); - chip->state = FL_JEDEC_QUERY; - } - - while (count && ((offst-base_offst) < reg_sz)) { - *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); - buf++; - offst++; - count--; - } - - put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); - - /* Move on to the next chip */ - chip_num++; - offst = base_offst; - } - - out: - return len-count; -} - -static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, + unsigned long adr, map_word datum, int mode) { - struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int base_offst,reg_sz; - - /* Check that we actually have some protection registers */ - if(!(extp->FeatureSupport&64)){ - printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); - return 0; - } - - base_offst=(1<FactProtRegSize); - reg_sz=(1<UserProtRegSize); - - return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); -} - -static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int base_offst,reg_sz; - - /* Check that we actually have some protection registers */ - if(!(extp->FeatureSupport&64)){ - printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); - return 0; - } - - base_offst=0; - reg_sz=(1<FactProtRegSize); - - return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); -} -#endif - -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) -{ - struct cfi_private *cfi = map->fldrv_priv; - map_word status, status_OK; + map_word status, status_OK, write_cmd; unsigned long timeo; int z, ret=0; @@ -931,28 +1207,35 @@ /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); + switch (mode) { + case FL_WRITING: write_cmd = CMD(0x40); break; + case FL_OTP_WRITE: write_cmd = CMD(0xc0); break; + default: return -EINVAL; + } spin_lock(chip->mutex); - ret = get_chip(map, chip, adr, FL_WRITING); + ret = get_chip(map, chip, adr, mode); if (ret) { spin_unlock(chip->mutex); return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); ENABLE_VPP(map); - map_write(map, CMD(0x40), adr); + xip_disable(map, chip, adr); + map_write(map, write_cmd, adr); map_write(map, datum, adr); - chip->state = FL_WRITING; + chip->state = mode; spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); - cfi_udelay(chip->word_write_time); + UDELAY(map, chip, adr, chip->word_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); z = 0; for (;;) { - if (chip->state != FL_WRITING) { + if (chip->state != mode) { /* Someone's suspended the write. Sleep */ DECLARE_WAITQUEUE(wait, current); @@ -973,6 +1256,7 @@ /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); ret = -EIO; goto out; @@ -981,7 +1265,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); z++; - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } if (!z) { @@ -994,6 +1278,7 @@ /* Done and happy. */ chip->state = FL_STATUS; + /* check for lock bit */ if (map_word_bitsset(map, status, CMD(0x02))) { /* clear status */ @@ -1002,8 +1287,9 @@ map_write(map, CMD(0x70), adr); ret = -EROFS; } - out: - put_chip(map, chip, adr); + + xip_enable(map, chip, adr); + out: put_chip(map, chip, adr); spin_unlock(chip->mutex); return ret; @@ -1037,7 +1323,7 @@ datum = map_word_load_partial(map, datum, buf, gap, n); ret = do_write_oneword(map, &cfi->chips[chipnum], - bus_ofs, datum); + bus_ofs, datum, FL_WRITING); if (ret) return ret; @@ -1058,7 +1344,7 @@ map_word datum = map_word_load(map, buf); ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum); + ofs, datum, FL_WRITING); if (ret) return ret; @@ -1082,7 +1368,7 @@ datum = map_word_load_partial(map, datum, buf, 0, len); ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum); + ofs, datum, FL_WRITING); if (ret) return ret; @@ -1093,8 +1379,8 @@ } -static inline int do_write_buffer(struct map_info *map, struct flchip *chip, - unsigned long adr, const u_char *buf, int len) +static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; map_word status, status_OK; @@ -1115,6 +1401,10 @@ return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); + ENABLE_VPP(map); + xip_disable(map, chip, cmd_adr); + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise @@ -1123,12 +1413,13 @@ map_write(map, CMD(0x70), cmd_adr); status = map_read(map, cmd_adr); if (map_word_bitsset(map, status, CMD(0x30))) { + xip_enable(map, chip, cmd_adr); printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); + xip_disable(map, chip, cmd_adr); map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); } - ENABLE_VPP(map); chip->state = FL_WRITING_TO_BUFFER; z = 0; @@ -1140,18 +1431,21 @@ break; spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); spin_lock(chip->mutex); if (++z > 20) { /* Argh. Not ready for write to buffer */ + map_word Xstatus; map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx, status = %lx\n", - status.x[0], map_read(map, cmd_adr).x[0]); + Xstatus = map_read(map, cmd_adr); /* Odd. Clear status bits */ map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); + xip_enable(map, chip, cmd_adr); + printk(KERN_ERR "Chip not ready for buffer write. status = %lx, Xstatus = %lx\n", + status.x[0], Xstatus.x[0]); ret = -EIO; goto out; } @@ -1186,7 +1480,7 @@ spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - cfi_udelay(chip->buffer_write_time); + UDELAY(map, chip, cmd_adr, chip->buffer_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); @@ -1212,6 +1506,7 @@ /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; + xip_enable(map, chip, cmd_adr); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); ret = -EIO; goto out; @@ -1219,7 +1514,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); z++; spin_lock(chip->mutex); } @@ -1243,8 +1538,8 @@ ret = -EROFS; } - out: - put_chip(map, chip, cmd_adr); + xip_enable(map, chip, cmd_adr); + out: put_chip(map, chip, cmd_adr); spin_unlock(chip->mutex); return ret; } @@ -1313,104 +1608,8 @@ return 0; } -typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk); - -static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, - loff_t ofs, size_t len, void *thunk) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (ofs > mtd->size) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while (i < mtd->numeraseregions && ofs >= regions[i].offset) - i++; - i--; - - /* OK, now i is pointing at the erase region in which this - erase request starts. Check the start of the requested - erase range is aligned with the erase size which is in - effect here. - */ - - if (ofs & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (inumeraseregions && (ofs + len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((ofs + len) & (regions[i].erasesize-1)) - return -EINVAL; - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - i=first; - - while(len) { - unsigned long chipmask; - int size = regions[i].erasesize; - - ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); - - if (ret) - return ret; - - adr += size; - len -= size; - - chipmask = (1 << cfi->chipshift) - 1; - if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask)) - i++; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - - return 0; -} - - -static int do_erase_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk) +static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; map_word status, status_OK; @@ -1432,7 +1631,10 @@ return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); + xip_disable(map, chip, adr); + /* Clear the status register first */ map_write(map, CMD(0x50), adr); @@ -1444,7 +1646,7 @@ spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - msleep(chip->erase_time / 2); + UDELAY(map, chip, adr, chip->erase_time*1000/2); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1475,27 +1677,25 @@ /* OK Still waiting */ if (time_after(jiffies, timeo)) { + map_word Xstatus; map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %lx, status = %lx.\n", - adr, status.x[0], map_read(map, adr).x[0]); + Xstatus = map_read(map, adr); /* Clear status bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); - DISABLE_VPP(map); - spin_unlock(chip->mutex); - return -EIO; + xip_enable(map, chip, adr); + printk(KERN_ERR "waiting for erase at %08lx to complete timed out. status = %lx, Xstatus = %lx.\n", + adr, status.x[0], Xstatus.x[0]); + ret = -EIO; + goto out; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(chip->mutex); } - - DISABLE_VPP(map); - ret = 0; /* We've broken this before. It doesn't hurt to be safe */ map_write(map, CMD(0x70), adr); @@ -1504,45 +1704,42 @@ /* check for lock bit */ if (map_word_bitsset(map, status, CMD(0x3a))) { - unsigned char chipstatus = status.x[0]; - if (!map_word_equal(map, status, CMD(chipstatus))) { - int i, w; - for (w=0; w> (cfi->device_type * 8); - } - } - printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", - status.x[0], chipstatus); - } + unsigned long chipstatus; + /* Reset the error bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); - + xip_enable(map, chip, adr); + + chipstatus = MERGESTATUS(status); + if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%lx\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ - printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%lx\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; - chip->state = FL_STATUS; + put_chip(map, chip, adr); spin_unlock(chip->mutex); goto retry; } - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx\n", adr, chipstatus); ret = -EIO; } + } else { + xip_enable(map, chip, adr); + ret = 0; } - wake_up(&chip->wq); + out: put_chip(map, chip, adr); spin_unlock(chip->mutex); return ret; } @@ -1555,7 +1752,7 @@ ofs = instr->addr; len = instr->len; - ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); + ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); if (ret) return ret; @@ -1599,6 +1796,7 @@ if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; + chip->oldstate = FL_READY; wake_up(&chip->wq); } spin_unlock(chip->mutex); @@ -1606,16 +1804,21 @@ } #ifdef DEBUG_LOCK_BITS -static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk) +static int __xipram do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - int ofs_factor = cfi->interleave * cfi->device_type; + int status, ofs_factor = cfi->interleave * cfi->device_type; + xip_disable(map, chip, adr+(2*ofs_factor)); cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", - adr, cfi_read_query(map, adr+(2*ofs_factor))); chip->state = FL_JEDEC_QUERY; + status = cfi_read_query(map, adr+(2*ofs_factor)); + xip_enable(map, chip, 0); + printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", + adr, status); return 0; } #endif @@ -1623,10 +1826,11 @@ #define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk) +static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; map_word status, status_OK; unsigned long timeo = jiffies + HZ; int ret; @@ -1644,8 +1848,9 @@ } ENABLE_VPP(map); + xip_disable(map, chip, adr); + map_write(map, CMD(0x60), adr); - if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; @@ -1655,9 +1860,16 @@ } else BUG(); - spin_unlock(chip->mutex); - schedule_timeout(HZ); - spin_lock(chip->mutex); + /* + * If Instant Individual Block Locking supported then no need + * to delay. + */ + + if (!extp || !(extp->FeatureSupport & (1 << 5))) { + spin_unlock(chip->mutex); + UDELAY(map, chip, adr, 1000000/HZ); + spin_lock(chip->mutex); + } /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1671,23 +1883,27 @@ /* OK Still waiting */ if (time_after(jiffies, timeo)) { + map_word Xstatus; map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", - status.x[0], map_read(map, adr).x[0]); - DISABLE_VPP(map); + Xstatus = map_read(map, adr); + xip_enable(map, chip, adr); + printk(KERN_ERR "waiting for unlock to complete timed out. status = %lx, Xstatus = %lx.\n", + status.x[0], Xstatus.x[0]); + put_chip(map, chip, adr); spin_unlock(chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; + xip_enable(map, chip, adr); put_chip(map, chip, adr); spin_unlock(chip->mutex); return 0; @@ -1700,18 +1916,18 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif - ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, - ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); + ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, + ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif return ret; @@ -1724,23 +1940,281 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif - ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, - ofs, len, 0); + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif return ret; } +#ifdef CONFIG_MTD_OTP + +typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, + u_long data_offset, u_char *buf, u_int size, + u_long prot_offset, u_int groupno, u_int groupsize); + +static int __xipram +do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + struct cfi_private *cfi = map->fldrv_priv; + int ret; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + /* let's ensure we're not reading back cached data from array mode */ + if (map->inval_cache) + map->inval_cache(map, chip->start + offset, size); + + xip_disable(map, chip, chip->start); + if (chip->state != FL_JEDEC_QUERY) { + map_write(map, CMD(0x90), chip->start); + chip->state = FL_JEDEC_QUERY; + } + map_copy_from(map, buf, chip->start + offset, size); + xip_enable(map, chip, chip->start); + + /* then ensure we don't keep OTP data in the cache */ + if (map->inval_cache) + map->inval_cache(map, chip->start + offset, size); + + put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + return 0; +} + +static int +do_otp_write(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + int ret; + + while (size) { + unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1); + int gap = offset - bus_ofs; + int n = min_t(int, size, map_bankwidth(map)-gap); + map_word datum = map_word_ff(map); + + datum = map_word_load_partial(map, datum, buf, gap, n); + ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); + if (ret) + return ret; + + offset += n; + buf += n; + size -= n; + } + + return 0; +} + +static int +do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + struct cfi_private *cfi = map->fldrv_priv; + map_word datum; + + /* make sure area matches group boundaries */ + if (size != grpsz) + return -EXDEV; + + datum = map_word_ff(map); + datum = map_word_clr(map, datum, CMD(1 << grpno)); + return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE); +} + +static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, + otp_op_t action, int user_regs) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct flchip *chip; + struct cfi_intelext_otpinfo *otp; + u_long devsize, reg_prot_offset, data_offset; + u_int chip_num, chip_step, field, reg_fact_size, reg_user_size; + u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups; + int ret; + + *retlen = 0; + + /* Check that we actually have some OTP registers */ + if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields) + return -ENODATA; + + /* we need real chips here not virtual ones */ + devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave; + chip_step = devsize >> cfi->chipshift; + + for (chip_num = 0; chip_num < cfi->numchips; chip_num += chip_step) { + chip = &cfi->chips[chip_num]; + otp = (struct cfi_intelext_otpinfo *)&extp->extra[0]; + + /* first OTP region */ + field = 0; + reg_prot_offset = extp->ProtRegAddr; + reg_fact_groups = 1; + reg_fact_size = 1 << extp->FactProtRegSize; + reg_user_groups = 1; + reg_user_size = 1 << extp->UserProtRegSize; + + while (len > 0) { + /* flash geometry fixup */ + data_offset = reg_prot_offset + 1; + data_offset *= cfi->interleave * cfi->device_type; + reg_prot_offset *= cfi->interleave * cfi->device_type; + reg_fact_size *= cfi->interleave; + reg_user_size *= cfi->interleave; + + if (user_regs) { + groups = reg_user_groups; + groupsize = reg_user_size; + /* skip over factory reg area */ + groupno = reg_fact_groups; + data_offset += reg_fact_groups * reg_fact_size; + } else { + groups = reg_fact_groups; + groupsize = reg_fact_size; + groupno = 0; + } + + while (len > 0 && groups > 0) { + if (!action) { + /* + * Special case: if action is NULL + * we fill buf with otp_info records. + */ + struct otp_info *otpinfo; + map_word lockword; + len -= sizeof(struct otp_info); + if (len <= 0) + return -ENOSPC; + ret = do_otp_read(map, chip, + reg_prot_offset, + (u_char *)&lockword, + map_bankwidth(map), + 0, 0, 0); + if (ret) + return ret; + otpinfo = (struct otp_info *)buf; + otpinfo->start = from; + otpinfo->length = groupsize; + otpinfo->locked = + !map_word_bitsset(map, lockword, + CMD(1 << groupno)); + from += groupsize; + buf += sizeof(*otpinfo); + *retlen += sizeof(*otpinfo); + } else if (from >= groupsize) { + from -= groupsize; + data_offset += groupsize; + } else { + int size = groupsize; + data_offset += from; + size -= from; + from = 0; + if (size > len) + size = len; + ret = action(map, chip, data_offset, + buf, size, reg_prot_offset, + groupno, groupsize); + if (ret < 0) + return ret; + buf += size; + len -= size; + *retlen += size; + data_offset += size; + } + groupno++; + groups--; + } + + /* next OTP region */ + if (++field == extp->NumProtectionFields) + break; + reg_prot_offset = otp->ProtRegAddr; + reg_fact_groups = otp->FactGroups; + reg_fact_size = 1 << otp->FactProtRegSize; + reg_user_groups = otp->UserGroups; + reg_user_size = 1 << otp->UserProtRegSize; + otp++; + } + } + + return 0; +} + +static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_read, 0); +} + +static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_read, 1); +} + +static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_write, 1); +} + +static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, + loff_t from, size_t len) +{ + size_t retlen; + return cfi_intelext_otp_walk(mtd, from, len, &retlen, + NULL, do_otp_lock, 1); +} + +static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); + return ret ? : retlen; +} + +static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); + return ret ? : retlen; +} + +#endif + static int cfi_intelext_suspend(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1759,14 +2233,25 @@ case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY: - chip->oldstate = chip->state; - chip->state = FL_PM_SUSPENDED; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ + if (chip->oldstate == FL_READY) { + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + } else { + /* There seems to be an operation pending. We must wait for it. */ + printk(KERN_NOTICE "Flash device refused suspend due to pending operation (oldstate %d)\n", chip->oldstate); + ret = -EAGAIN; + } break; default: + /* Should we actually wait? Once upon a time these routines weren't + allowed to. Or should we return -EAGAIN, because the upper layers + ought to have already shut down anything which was using the device + anyway? The latter for now. */ + printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate); ret = -EAGAIN; case FL_PM_SUSPENDED: break; @@ -1787,6 +2272,7 @@ because we're returning failure, and it didn't get power cycled */ chip->state = chip->oldstate; + chip->oldstate = FL_READY; wake_up(&chip->wq); } spin_unlock(chip->mutex); @@ -1812,7 +2298,7 @@ /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { map_write(map, CMD(0xFF), cfi->chips[i].start); - chip->state = FL_READY; + chip->oldstate = chip->state = FL_READY; wake_up(&chip->wq); } @@ -1820,10 +2306,46 @@ } } +static int cfi_intelext_reset(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i, ret; + + for (i=0; i < cfi->numchips; i++) { + struct flchip *chip = &cfi->chips[i]; + + /* force the completion of any ongoing operation + and switch to array mode so any bootloader in + flash is accessible for soft reboot. */ + spin_lock(chip->mutex); + ret = get_chip(map, chip, chip->start, FL_SYNCING); + if (!ret) { + map_write(map, CMD(0xff), chip->start); + chip->state = FL_READY; + } + spin_unlock(chip->mutex); + } + + return 0; +} + +static int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct mtd_info *mtd; + + mtd = container_of(nb, struct mtd_info, reboot_notifier); + cfi_intelext_reset(mtd); + return NOTIFY_DONE; +} + static void cfi_intelext_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + cfi_intelext_reset(mtd); + unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi->chips[0].priv); @@ -1834,7 +2356,7 @@ static char im_name_1[]="cfi_cmdset_0001"; static char im_name_3[]="cfi_cmdset_0003"; -int __init cfi_intelext_init(void) +static int __init cfi_intelext_init(void) { inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001); inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001); diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/cfi_cmdset_0002.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_cmdset_0002.c --- linux-2.6.9-intc1/drivers/mtd/chips/cfi_cmdset_0002.c 2004-10-18 17:53:51.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_cmdset_0002.c 2005-03-21 21:28:42.000000000 -0500 @@ -13,7 +13,7 @@ * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.106 2004/08/09 14:02:32 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $ * */ @@ -40,13 +40,15 @@ #define MAX_WORD_RETRIES 3 +#define MANUFACTURER_AMD 0x0001 +#define MANUFACTURER_SST 0x00BF +#define SST49LF004B 0x0060 + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); -static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t); -static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t); static void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); @@ -55,8 +57,11 @@ static void cfi_amdstd_destroy(struct mtd_info *); struct mtd_info *cfi_cmdset_0002(struct map_info *, int); -static struct mtd_info *cfi_amdstd_setup (struct map_info *); +static struct mtd_info *cfi_amdstd_setup (struct mtd_info *); +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); +#include "fwh_lock.h" static struct mtd_chip_driver cfi_amdstd_chipdrv = { .probe = NULL, /* Not usable directly */ @@ -66,7 +71,6 @@ }; -/* #define DEBUG_LOCK_BITS */ /* #define DEBUG_CFI_FEATURES */ @@ -122,8 +126,9 @@ #ifdef AMD_BOOTLOC_BUG /* Wheee. Bring me the head of someone at AMD. */ -static void fixup_amd_bootblock(struct map_info *map, void* param) +static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_amdstd *extp = cfi->cmdset_priv; __u8 major = extp->MajorVersion; @@ -141,25 +146,92 @@ } #endif -static struct cfi_fixup fixup_table[] = { +static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if (cfi->cfiq->BufWriteTimeoutTyp) { + DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); + mtd->write = cfi_amdstd_write_buffers; + } +} + +static void fixup_use_secsi(struct mtd_info *mtd, void *param) +{ + /* Setup for chips with a secsi area */ + mtd->read_user_prot_reg = cfi_amdstd_secsi_read; + mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; +} + +static void fixup_use_erase_chip(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if ((cfi->cfiq->NumEraseRegions == 1) && + ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) { + mtd->erase = cfi_amdstd_erase_chip; + } + +} + +static struct cfi_fixup cfi_fixup_table[] = { #ifdef AMD_BOOTLOC_BUG - { - 0x0001, /* AMD */ - CFI_ID_ANY, - fixup_amd_bootblock, NULL - }, + { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, +#endif + { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, +#if !FORCE_WORD_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif { 0, 0, NULL, NULL } }; +static struct cfi_fixup jedec_fixup_table[] = { + { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { 0, 0, NULL, NULL } +}; + +static struct cfi_fixup fixup_table[] = { + /* The CFI vendor ids and the JEDEC vendor IDs appear + * to be common. It is like the devices id's are as + * well. This table is to pick all cases where + * we know that is the case. + */ + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL }, + { 0, 0, NULL, NULL } +}; struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; - unsigned char bootloc; + struct mtd_info *mtd; int i; + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->erase = cfi_amdstd_erase_varsize; + mtd->write = cfi_amdstd_write_words; + mtd->read = cfi_amdstd_read; + mtd->sync = cfi_amdstd_sync; + mtd->suspend = cfi_amdstd_suspend; + mtd->resume = cfi_amdstd_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + if (cfi->cfi_mode==CFI_MODE_CFI){ + unsigned char bootloc; /* * It's a real CFI chip, not one for which the probe * routine faked a CFI structure. So we read the feature @@ -169,13 +241,16 @@ struct cfi_pri_amdstd *extp; extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); - if (!extp) + if (!extp) { + kfree(mtd); return NULL; + } /* Install our own private info structure */ cfi->cmdset_priv = extp; - cfi_fixup(map, fixup_table); + /* Apply cfi device specific fixups */ + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ @@ -201,43 +276,28 @@ cfi->cfiq->EraseRegionInfo[j] = swap; } } - /* - * These might already be setup (more correctly) by - * jedec_probe.c - still need it for cfi_probe.c path. - */ - if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) { - switch (cfi->device_type) { - case CFI_DEVICETYPE_X8: - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; - break; - case CFI_DEVICETYPE_X16: - cfi->addr_unlock1 = 0xaaa; - if (map_bankwidth(map) == cfi_interleave(cfi)) { - /* X16 chip(s) in X8 mode */ - cfi->addr_unlock2 = 0x555; - } else { - cfi->addr_unlock2 = 0x554; - } - break; - case CFI_DEVICETYPE_X32: - cfi->addr_unlock1 = 0x1554; - if (map_bankwidth(map) == cfi_interleave(cfi)*2) { - /* X32 chip(s) in X16 mode */ - cfi->addr_unlock1 = 0xaaa; - } else { - cfi->addr_unlock2 = 0xaa8; - } - break; - default: - printk(KERN_WARNING - "MTD %s(): Unsupported device type %d\n", - __func__, cfi->device_type); - return NULL; - } + /* Set the default CFI lock/unlock addresses */ + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; + /* Modify the unlock address if we are in compatibility mode */ + if ( /* x16 in x8 mode */ + ((cfi->device_type == CFI_DEVICETYPE_X8) && + (cfi->cfiq->InterfaceDesc == 2)) || + /* x32 in x16 mode */ + ((cfi->device_type == CFI_DEVICETYPE_X16) && + (cfi->cfiq->InterfaceDesc == 4))) + { + cfi->addr_unlock1 = 0xaaa; + cfi->addr_unlock2 = 0x555; } } /* CFI mode */ + else if (cfi->cfi_mode == CFI_MODE_JEDEC) { + /* Apply jedec specific fixups */ + cfi_fixup(mtd, jedec_fixup_table); + } + /* Apply generic fixups */ + cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; @@ -246,32 +306,22 @@ } map->fldrv = &cfi_amdstd_chipdrv; - - return cfi_amdstd_setup(map); + + return cfi_amdstd_setup(mtd); } -static struct mtd_info *cfi_amdstd_setup(struct map_info *map) +static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; unsigned long offset = 0; int i,j; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); printk(KERN_NOTICE "number of %s chips: %d\n", (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); - - if (!mtd) { - printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); - goto setup_err; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; - /* Also select the correct geometry setup too */ + /* Select the correct geometry setup */ mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; @@ -312,54 +362,10 @@ } #endif - if (mtd->numeraseregions == 1 - && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) { - mtd->erase = cfi_amdstd_erase_chip; - } else { - mtd->erase = cfi_amdstd_erase_varsize; - mtd->lock = cfi_amdstd_lock_varsize; - mtd->unlock = cfi_amdstd_unlock_varsize; - } - - if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { - DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); - mtd->write = cfi_amdstd_write_buffers; - } else { - DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" ); - mtd->write = cfi_amdstd_write_words; - } - - mtd->read = cfi_amdstd_read; - /* FIXME: erase-suspend-program is broken. See http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); - /* does this chip have a secsi area? */ - if(cfi->mfr==1){ - - switch(cfi->id){ - case 0x50: - case 0x53: - case 0x55: - case 0x56: - case 0x5C: - case 0x5F: - /* Yes */ - mtd->read_user_prot_reg = cfi_amdstd_secsi_read; - mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; - default: - ; - } - } - - - mtd->sync = cfi_amdstd_sync; - mtd->suspend = cfi_amdstd_suspend; - mtd->resume = cfi_amdstd_resume; - mtd->flags = MTD_CAP_NORFLASH; - map->fldrv = &cfi_amdstd_chipdrv; - mtd->name = map->name; __module_get(THIS_MODULE); return mtd; @@ -434,6 +440,7 @@ goto sleep; if (!(mode == FL_READY || mode == FL_POINT + || !cfip || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) goto sleep; @@ -624,14 +631,12 @@ chip->state = FL_READY; - /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); map_copy_from(map, buf, adr, len); - /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -702,7 +707,7 @@ */ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; - map_word oldd, curd; + map_word oldd; int retry_cnt = 0; adr += chip->start; @@ -732,17 +737,9 @@ ENABLE_VPP(map); retry: - /* - * The CFI_DEVICETYPE_X8 argument is needed even when - * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for - * command sequences don't scale even when the device is - * wider. This is the case for many of the cfi_send_gen_cmd() - * below. I'm not sure, however, why some use - * cfi->device_type. - */ - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); map_write(map, datum, adr); chip->state = FL_WRITING; @@ -767,23 +764,11 @@ continue; } - /* Test to see if toggling has stopped. */ - oldd = map_read(map, adr); - curd = map_read(map, adr); - if (map_word_equal(map, curd, oldd)) { - /* Do we have the correct value? */ - if (map_word_equal(map, curd, datum)) { - goto op_done; - } - /* Nope something has gone wrong. */ - break; - } + if (chip_ready(map, adr)) + goto op_done; - if (time_after(jiffies, timeo)) { - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); - break; - } + if (time_after(jiffies, timeo)) + break; /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); @@ -791,6 +776,8 @@ cfi_spin_lock(chip->mutex); } + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); + /* reset on all failures. */ map_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ @@ -958,7 +945,7 @@ struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; /* see comments in do_write_oneword() regarding uWriteTimeo. */ - static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = -EIO; unsigned long cmd_adr; int z, words; @@ -980,9 +967,9 @@ __func__, adr, datum.x[0] ); ENABLE_VPP(map); - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); /* Write Buffer Load */ map_write(map, CMD(0x25), cmd_adr); @@ -1081,8 +1068,8 @@ size_t local_len = (-ofs)&(map_bankwidth(map)-1); if (local_len > len) local_len = len; - ret = cfi_amdstd_write_words(mtd, to, local_len, - retlen, buf); + ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<chipshift), + local_len, retlen, buf); if (ret) return ret; ofs += local_len; @@ -1128,7 +1115,8 @@ if (len) { size_t retlen_dregs = 0; - ret = cfi_amdstd_write_words(mtd, to, len, &retlen_dregs, buf); + ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<chipshift), + len, &retlen_dregs, buf); *retlen += retlen_dregs; return ret; @@ -1163,20 +1151,19 @@ __func__, chip->start ); ENABLE_VPP(map); - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); chip->state = FL_ERASING; chip->erase_suspended = 0; chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); @@ -1229,100 +1216,7 @@ } -typedef int (*frob_t)(struct map_info *map, struct flchip *chip, - unsigned long adr, void *thunk); - - -static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob, - loff_t ofs, size_t len, void *thunk) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (ofs > mtd->size) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while (i < mtd->numeraseregions && ofs >= regions[i].offset) - i++; - i--; - - /* OK, now i is pointing at the erase region in which this - erase request starts. Check the start of the requested - erase range is aligned with the erase size which is in - effect here. - */ - - if (ofs & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (inumeraseregions && (ofs + len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((ofs + len) & (regions[i].erasesize-1)) - return -EINVAL; - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - i=first; - - while (len) { - ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); - - if (ret) - return ret; - - adr += regions[i].erasesize; - len -= regions[i].erasesize; - - if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) - i++; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - - return 0; -} - - -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; @@ -1342,11 +1236,11 @@ __func__, adr ); ENABLE_VPP(map); - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); map_write(map, CMD(0x30), adr); chip->state = FL_ERASING; @@ -1354,8 +1248,7 @@ chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); @@ -1415,7 +1308,7 @@ ofs = instr->addr; len = instr->len; - ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); + ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); if (ret) return ret; @@ -1588,137 +1481,6 @@ } } - -#ifdef DEBUG_LOCK_BITS - -static int do_printlockstatus_oneblock(struct map_info *map, - struct flchip *chip, - unsigned long adr, - void *thunk) -{ - struct cfi_private *cfi = map->fldrv_priv; - int ofs_factor = cfi->interleave * cfi->device_type; - - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", - adr, cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); - - return 0; -} - - -#define debug_dump_locks(mtd, frob, ofs, len, thunk) \ - cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk)) - -#else - -#define debug_dump_locks(...) - -#endif /* DEBUG_LOCK_BITS */ - - -struct xxlock_thunk { - uint8_t val; - flstate_t state; -}; - - -#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING}) -#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING}) - - -/* - * FIXME - this is *very* specific to a particular chip. It likely won't - * work for all chips that require unlock. It also hasn't been tested - * with interleaved chips. - */ -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) -{ - struct cfi_private *cfi = map->fldrv_priv; - struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk; - int ret; - - /* - * This is easy because these are writes to registers and not writes - * to flash memory - that means that we don't have to check status - * and timeout. - */ - - adr += chip->start; - /* - * lock block registers: - * - on 64k boundariesand - * - bit 1 set high - * - block lock registers are 4MiB lower - overflow subtract (danger) - */ - adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff; - - cfi_spin_lock(chip->mutex); - ret = get_chip(map, chip, adr, FL_LOCKING); - if (ret) { - cfi_spin_unlock(chip->mutex); - return ret; - } - - chip->state = xxlt->state; - map_write(map, CMD(xxlt->val), adr); - - /* Done and happy. */ - chip->state = FL_READY; - put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); - return 0; -} - - -static int cfi_amdstd_lock_varsize(struct mtd_info *mtd, - loff_t ofs, - size_t len) -{ - int ret; - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status before, ofs=0x%08llx, len=0x%08zX\n", - __func__, ofs, len); - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, - (void *)&DO_XXLOCK_ONEBLOCK_LOCK); - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status after, ret=%d\n", - __func__, ret); - - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - return ret; -} - - -static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd, - loff_t ofs, - size_t len) -{ - int ret; - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status before, ofs=0x%08llx, len=0x%08zX\n", - __func__, ofs, len); - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, - (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK); - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status after, ret=%d\n", - __func__, ret); - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - return ret; -} - - static void cfi_amdstd_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1732,7 +1494,7 @@ static char im_name[]="cfi_cmdset_0002"; -int __init cfi_amdstd_init(void) +static int __init cfi_amdstd_init(void) { inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/cfi_cmdset_0020.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_cmdset_0020.c --- linux-2.6.9-intc1/drivers/mtd/chips/cfi_cmdset_0020.c 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_cmdset_0020.c 2005-03-21 21:28:42.000000000 -0500 @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0020.c,v 1.15 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: cfi_cmdset_0020.c,v 1.17 2004/11/20 12:49:04 dwmw2 Exp $ * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and @@ -788,7 +788,7 @@ chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1087,7 +1087,7 @@ chip->state = FL_LOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1236,7 +1236,7 @@ chip->state = FL_UNLOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1401,7 +1401,7 @@ static char im_name[]="cfi_cmdset_0020"; -int __init cfi_staa_init(void) +static int __init cfi_staa_init(void) { inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020); return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/cfi_probe.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_probe.c --- linux-2.6.9-intc1/drivers/mtd/chips/cfi_probe.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_probe.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.77 2004/07/14 08:38:44 dwmw2 Exp $ + $Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $ */ #include @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -31,40 +32,75 @@ struct mtd_info *cfi_probe(struct map_info *map); +#ifdef CONFIG_MTD_XIP + +/* only needed for short periods, so this is rather simple */ +#define xip_disable() local_irq_disable() + +#define xip_allowed(base, map) \ +do { \ + (void) map_read(map, base); \ + asm volatile (".rep 8; nop; .endr"); \ + local_irq_enable(); \ +} while (0) + +#define xip_enable(base, map, cfi) \ +do { \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + xip_allowed(base, map); \ +} while (0) + +#define xip_disable_qry(base, map, cfi) \ +do { \ + xip_disable(); \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \ +} while (0) + +#else + +#define xip_disable() do { } while (0) +#define xip_allowed(base, map) do { } while (0) +#define xip_enable(base, map, cfi) do { } while (0) +#define xip_disable_qry(base, map, cfi) do { } while (0) + +#endif + /* check for QRY. in: interleave,type,mode ret: table index, <0 for error */ -static int qry_present(struct map_info *map, __u32 base, +static int __xipram qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf = cfi->interleave * cfi->device_type; // scale factor - map_word val; - map_word qry; + map_word val[3]; + map_word qry[3]; - qry = cfi_build_cmd('Q', map, cfi); - val = map_read(map, base + osf*0x10); + qry[0] = cfi_build_cmd('Q', map, cfi); + qry[1] = cfi_build_cmd('R', map, cfi); + qry[2] = cfi_build_cmd('Y', map, cfi); + + val[0] = map_read(map, base + osf*0x10); + val[1] = map_read(map, base + osf*0x11); + val[2] = map_read(map, base + osf*0x12); - if (!map_word_equal(map, qry, val)) + if (!map_word_equal(map, qry[0], val[0])) return 0; - qry = cfi_build_cmd('R', map, cfi); - val = map_read(map, base + osf*0x11); - - if (!map_word_equal(map, qry, val)) + if (!map_word_equal(map, qry[1], val[1])) return 0; - qry = cfi_build_cmd('Y', map, cfi); - val = map_read(map, base + osf*0x12); - - if (!map_word_equal(map, qry, val)) + if (!map_word_equal(map, qry[2], val[2])) return 0; - return 1; // nothing found + return 1; // "QRY" found } -static int cfi_probe_chip(struct map_info *map, __u32 base, - unsigned long *chip_map, struct cfi_private *cfi) +static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, + unsigned long *chip_map, struct cfi_private *cfi) { int i; @@ -80,12 +116,16 @@ (unsigned long)base + 0x55, map->size -1); return 0; } + + xip_disable(); cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - if (!qry_present(map,base,cfi)) + if (!qry_present(map,base,cfi)) { + xip_enable(base, map, cfi); return 0; + } if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI @@ -111,6 +151,7 @@ /* If the QRY marker goes away, it's an alias */ if (!qry_present(map, start, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", map->name, base, start); return 0; @@ -123,6 +164,7 @@ cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); if (qry_present(map, base, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", map->name, base, start); return 0; @@ -138,6 +180,7 @@ /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, @@ -146,14 +189,15 @@ return 1; } -static int cfi_chip_setup(struct map_info *map, - struct cfi_private *cfi) +static int __xipram cfi_chip_setup(struct map_info *map, + struct cfi_private *cfi) { int ofs_factor = cfi->interleave*cfi->device_type; __u32 base = 0; int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + xip_enable(base, map, cfi); #ifdef DEBUG_CFI printk("Number of erase regions: %d\n", num_erase_regions); #endif @@ -171,13 +215,33 @@ cfi->cfi_mode = CFI_MODE_CFI; /* Read the CFI info structure */ - for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { + xip_disable_qry(base, map, cfi); + for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); - } - + + /* Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query(map, base); + cfi->id = cfi_read_query(map, base + ofs_factor); + + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* ... even if it's an Intel chip */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); + /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); - + cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR); cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID); cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR); @@ -199,25 +263,6 @@ #endif } - /* Note we put the device back into Read Mode BEFORE going into Auto - * Select Mode, as some devices support nesting of modes, others - * don't. This way should always work. - * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and - * so should be treated as nops or illegal (and so put the device - * back into Read Mode, which is a nop in this case). - */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi->mfr = cfi_read_query(map, base); - cfi->id = cfi_read_query(map, base + ofs_factor); - - /* Put it back into Read Mode */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - /* ... even if it's an Intel chip */ - cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8); @@ -243,12 +288,27 @@ case P_ID_AMD_EXT: return "AMD/Fujitsu Extended"; + + case P_ID_WINBOND: + return "Winbond Standard"; + case P_ID_ST_ADV: + return "ST Advanced"; + case P_ID_MITSUBISHI_STD: return "Mitsubishi Standard"; case P_ID_MITSUBISHI_EXT: return "Mitsubishi Extended"; + + case P_ID_SST_PAGE: + return "SST Page Write"; + + case P_ID_INTEL_PERFORMANCE: + return "Intel Performance Code"; + + case P_ID_INTEL_DATA: + return "Intel Data"; case P_ID_RESERVED: return "Not Allowed / Reserved for Future Use"; @@ -327,6 +387,10 @@ printk(" - x32-only asynchronous interface\n"); break; + case 4: + printk(" - supports x16 and x32 via Word# with asynchronous interface\n"); + break; + case 65535: printk(" - Not Allowed / Reserved\n"); break; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/cfi_util.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_util.c --- linux-2.6.9-intc1/drivers/mtd/chips/cfi_util.c 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/cfi_util.c 2005-03-21 21:28:42.000000000 -0500 @@ -7,7 +7,7 @@ * * This code is covered by the GPL. * - * $Id: cfi_util.c,v 1.4 2004/07/14 08:38:44 dwmw2 Exp $ + * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $ * */ @@ -22,12 +22,14 @@ #include #include #include +#include +#include #include #include #include struct cfi_extquery * -cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) +__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) { struct cfi_private *cfi = map->fldrv_priv; __u32 base = 0; // cfi->chips[0].start; @@ -39,21 +41,35 @@ if (!adr) goto out; - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); goto out; } - + +#ifdef CONFIG_MTD_XIP + local_irq_disable(); +#endif + + /* Switch it into Query Mode */ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + /* Read in the Extended Query Table */ for (i=0; idevice_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + +#ifdef CONFIG_MTD_XIP + (void) map_read(map, base); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); +#endif + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { printk(KERN_WARNING " Unknown %s Extended Query " @@ -61,32 +77,120 @@ extp->MinorVersion); kfree(extp); extp = NULL; - goto out; } -out: - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); - - return extp; + out: return extp; } EXPORT_SYMBOL(cfi_read_pri); -void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups) +void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_fixup *f; for (f=fixups; f->fixup; f++) { if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { - f->fixup(map, f->param); + f->fixup(mtd, f->param); } } } EXPORT_SYMBOL(cfi_fixup); +int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, + loff_t ofs, size_t len, void *thunk) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (ofs > mtd->size) + return -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && ofs >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (inumeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + i=first; + + while(len) { + int size = regions[i].erasesize; + + ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); + + if (ret) + return ret; + + adr += size; + ofs += size; + len -= size; + + if (ofs == regions[i].offset + size * regions[i].numblocks) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + return 0; +} + +EXPORT_SYMBOL(cfi_varsize_frob); + MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/chipreg.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/chipreg.c --- linux-2.6.9-intc1/drivers/mtd/chips/chipreg.c 2004-10-18 17:53:50.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/chipreg.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $ + * $Id: chipreg.c,v 1.18 2005/01/12 22:34:34 gleixner Exp $ * * Registration for chip drivers * @@ -15,7 +15,7 @@ #include #include -spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(chip_drvs_lock); static LIST_HEAD(chip_drvs_list); void register_mtd_chip_driver(struct mtd_chip_driver *drv) diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/fwh_lock.h linux-2.6.9-intc1-mih1/drivers/mtd/chips/fwh_lock.h --- linux-2.6.9-intc1/drivers/mtd/chips/fwh_lock.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/fwh_lock.h 2005-03-21 21:28:42.000000000 -0500 @@ -0,0 +1,107 @@ +#ifndef FWH_LOCK_H +#define FWH_LOCK_H + + +enum fwh_lock_state { + FWH_UNLOCKED = 0, + FWH_DENY_WRITE = 1, + FWH_IMMUTABLE = 2, + FWH_DENY_READ = 4, +}; + +struct fwh_xxlock_thunk { + enum fwh_lock_state val; + flstate_t state; +}; + + +#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) +#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) + +/* + * This locking/unlock is specific to firmware hub parts. Only one + * is known that supports the Intel command set. Firmware + * hub parts cannot be interleaved as they are on the LPC bus + * so this code has not been tested with interleaved chips, + * and will likely fail in that context. + */ +static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; + int ret; + + /* Refuse the operation if the we cannot look behind the chip */ + if (chip->start < 0x400000) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): chip->start: %lx wanted >= 0x400000\n", + __func__, chip->start ); + return -EIO; + } + /* + * lock block registers: + * - on 64k boundariesand + * - bit 1 set high + * - block lock registers are 4MiB lower - overflow subtract (danger) + * + * The address manipulation is first done on the logical address + * which is 0 at the start of the chip, and then the offset of + * the individual chip is addted to it. Any other order a weird + * map offset could cause problems. + */ + adr = (adr & ~0xffffUL) | 0x2; + adr += chip->start - 0x400000; + + /* + * This is easy because these are writes to registers and not writes + * to flash memory - that means that we don't have to check status + * and timeout. + */ + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_LOCKING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } + + chip->state = xxlt->state; + map_write(map, CMD(xxlt->val), adr); + + /* Done and happy. */ + chip->state = FL_READY; + put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + return 0; +} + + +static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, + (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); + + return ret; +} + + +static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, + (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); + + return ret; +} + +static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param) +{ + printk(KERN_NOTICE "using fwh lock/unlock method\n"); + /* Setup for the chips with the fwh lock method */ + mtd->lock = fwh_lock_varsize; + mtd->unlock = fwh_unlock_varsize; +} +#endif /* FWH_LOCK_H */ diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/gen_probe.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/gen_probe.c --- linux-2.6.9-intc1/drivers/mtd/chips/gen_probe.c 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/gen_probe.c 2005-03-21 21:28:42.000000000 -0500 @@ -2,7 +2,7 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.19 2004/07/13 22:33:32 dwmw2 Exp $ + * $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $ */ #include @@ -64,7 +64,7 @@ interleave and device type, etc. */ if (!genprobe_new_chip(map, cp, &cfi)) { /* The probe didn't like it */ - printk(KERN_WARNING "%s: Found no %s device at location zero\n", + printk(KERN_DEBUG "%s: Found no %s device at location zero\n", cp->name, map->name); return NULL; } @@ -162,15 +162,19 @@ int max_chips = map_bankwidth(map); /* And minimum 1 */ int nr_chips, type; - for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) { + for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { if (!cfi_interleave_supported(nr_chips)) continue; cfi->interleave = nr_chips; - for (type = 0; type < 3; type++) { - cfi->device_type = 1<device_type = type; if (cp->probe_chip(map, 0, NULL, cfi)) return 1; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/jedec.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/jedec.c --- linux-2.6.9-intc1/drivers/mtd/chips/jedec.c 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/jedec.c 2005-03-21 21:28:42.000000000 -0500 @@ -11,7 +11,7 @@ * not going to guess how to send commands to them, plus I expect they will * all speak CFI.. * - * $Id: jedec.c,v 1.21 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $ */ #include @@ -529,7 +529,7 @@ static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -541,8 +541,8 @@ static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; *retlen = 0; while (len > 0) @@ -593,8 +593,8 @@ unsigned long NoTime = 0; unsigned long start = instr->addr, len = instr->len; unsigned int I; - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; // Verify the arguments.. if (start + len > mtd->size || @@ -800,8 +800,8 @@ #define flread(x) map_read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; unsigned long base; unsigned long off; size_t save_len = len; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/jedec_probe.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/jedec_probe.c --- linux-2.6.9-intc1/drivers/mtd/chips/jedec_probe.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/jedec_probe.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.51 2004/07/14 14:44:30 thayne Exp $ + $Id: jedec_probe.c,v 1.65 2005/03/17 20:09:51 gleixner Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. @@ -32,6 +32,7 @@ #define MANUFACTURER_HYUNDAI 0x00AD #define MANUFACTURER_INTEL 0x0089 #define MANUFACTURER_MACRONIX 0x00C2 +#define MANUFACTURER_NEC 0x0010 #define MANUFACTURER_PMC 0x009D #define MANUFACTURER_SST 0x00BF #define MANUFACTURER_ST 0x0020 @@ -107,6 +108,7 @@ #define I82802AC 0x00ac /* Macronix */ +#define MX29LV040C 0x004F #define MX29LV160T 0x22C4 #define MX29LV160B 0x2249 #define MX29F016 0x00AD @@ -114,6 +116,9 @@ #define MX29F004T 0x0045 #define MX29F004B 0x0046 +/* NEC */ +#define UPD29F064115 0x221C + /* PMC */ #define PM49FL002 0x006D #define PM49FL004 0x006E @@ -128,6 +133,7 @@ #define M50FW040 0x002C #define M50FW080 0x002D #define M50FW016 0x002E +#define M50LPW080 0x002F /* SST */ #define SST29EE020 0x0010 @@ -136,6 +142,7 @@ #define SST29LE512 0x003d #define SST39LF800 0x2781 #define SST39LF160 0x2782 +#define SST39VF1601 0x234b #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 @@ -181,8 +188,8 @@ struct unlock_addr { - int addr1; - int addr2; + u32 addr1; + u32 addr2; }; @@ -225,6 +232,11 @@ [MTD_UADDR_DONT_CARE] = { .addr1 = 0x0000, /* Doesn't matter which address */ .addr2 = 0x0000 /* is used - must be last entry */ + }, + + [MTD_UADDR_UNNECESSARY] = { + .addr1 = 0x0000, + .addr2 = 0x0000 } }; @@ -512,15 +524,20 @@ ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29F002T, - .name = "AMD AM29F002T", - .DevSize = SIZE_256KiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,3), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F002T, + .name = "AMD AM29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_ATMEL, @@ -768,15 +785,20 @@ ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_HYUNDAI, - .dev_id = HY29F002T, - .name = "Hyundai HY29F002T", - .DevSize = SIZE_256KiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,3), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + .mfr_id = MANUFACTURER_HYUNDAI, + .dev_id = HY29F002T, + .name = "Hyundai HY29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_INTEL, @@ -1084,6 +1106,19 @@ } }, { .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29LV040C, + .name = "Macronix MX29LV040C", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), + } + }, { + .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29LV160T, .name = "MXIC MX29LV160T", .uaddr = { @@ -1100,6 +1135,22 @@ ERASEINFO(0x04000,1) } }, { + .mfr_id = MANUFACTURER_NEC, + .dev_id = UPD29F064115, + .name = "NEC uPD29F064115", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 3, + .regions = { + ERASEINFO(0x2000,8), + ERASEINFO(0x10000,126), + ERASEINFO(0x2000,8), + } + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29LV160B, .name = "MXIC MX29LV160B", @@ -1162,15 +1213,20 @@ ERASEINFO(0x10000,7), } }, { - .mfr_id = MANUFACTURER_MACRONIX, - .dev_id = MX29F002T, - .name = "Macronix MX29F002T", - .DevSize = SIZE_256KiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,3), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F002T, + .name = "Macronix MX29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_PMC, @@ -1247,7 +1303,8 @@ .DevSize = SIZE_256KiB, .CmdSet = P_ID_SST_PAGE, .NumEraseRegions= 1, - .regions = {ERASEINFO(0x01000,64), + .regions = { + ERASEINFO(0x01000,64), } }, { .mfr_id = MANUFACTURER_SST, @@ -1259,7 +1316,8 @@ .DevSize = SIZE_256KiB, .CmdSet = P_ID_SST_PAGE, .NumEraseRegions= 1, - .regions = {ERASEINFO(0x01000,64), + .regions = { + ERASEINFO(0x01000,64), } }, { .mfr_id = MANUFACTURER_SST, @@ -1379,6 +1437,37 @@ ERASEINFO(0x01000,256), } }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39LF160, + .name = "SST 39LF160", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39VF1601, + .name = "SST 39VF1601", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + + }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", @@ -1499,6 +1588,19 @@ ERASEINFO(0x10000,32), } }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M50LPW080, + .name = "ST M50LPW080", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,16), + } + }, { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", @@ -1617,27 +1719,27 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, unsigned long *chip_map, struct cfi_private *cfi); -struct mtd_info *jedec_probe(struct map_info *map); +static struct mtd_info *jedec_probe(struct map_info *map); static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, struct cfi_private *cfi) { map_word result; unsigned long mask; + u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type); mask = (1 << (cfi->device_type * 8)) -1; - result = map_read(map, base); + result = map_read(map, base + ofs); return result.x[0] & mask; } static inline u32 jedec_read_id(struct map_info *map, __u32 base, struct cfi_private *cfi) { - int osf; map_word result; unsigned long mask; - osf = cfi->interleave *cfi->device_type; + u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type); mask = (1 << (cfi->device_type * 8)) -1; - result = map_read(map, base + osf); + result = map_read(map, base + ofs); return result.x[0] & mask; } @@ -1653,9 +1755,11 @@ * as they will ignore the writes and dont care what address * the F0 is written to */ if(cfi->addr_unlock1) { - /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/ - cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + DEBUG( MTD_DEBUG_LEVEL3, + "reset unlock called %x %x \n", + cfi->addr_unlock1,cfi->addr_unlock2); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); @@ -1700,7 +1804,6 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) { int i,num_erase_regions; - unsigned long mask; __u8 uaddr; printk("Found: %s\n",jedec_table[index].name); @@ -1735,10 +1838,8 @@ return 0; } - /* Mask out address bits which are smaller than the device type */ - mask = ~(p_cfi->device_type-1); - p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask; - p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask; + p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; + p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; return 1; /* ok */ } @@ -1759,7 +1860,6 @@ int rc = 0; /* failure until all tests pass */ u32 mfr, id; __u8 uaddr; - unsigned long mask; /* * The IDs must match. For X16 and X32 devices operating in @@ -1774,6 +1874,16 @@ case CFI_DEVICETYPE_X8: mfr = (__u8)finfo->mfr_id; id = (__u8)finfo->dev_id; + + /* bjd: it seems that if we do this, we can end up + * detecting 16bit flashes as an 8bit device, even though + * there aren't. + */ + if (finfo->dev_id > 0xff) { + DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n", + __func__); + goto match_done; + } break; case CFI_DEVICETYPE_X16: mfr = (__u16)finfo->mfr_id; @@ -1797,7 +1907,7 @@ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); - if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) { + if ( base + cfi_interleave(cfi) * ( 1 << finfo->DevSize ) > map->size ) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", __func__, finfo->mfr_id, finfo->dev_id, @@ -1810,18 +1920,16 @@ goto match_done; } - mask = ~(cfi->device_type-1); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr - && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 || - (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) { + && ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 || + unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { DEBUG( MTD_DEBUG_LEVEL3, - "MTD %s(): 0x%.4lx 0x%.4lx did not match\n", - __func__, - unlock_addrs[uaddr].addr1 & mask, - unlock_addrs[uaddr].addr2 & mask); + "MTD %s(): 0x%.4x 0x%.4x did not match\n", + __func__, + unlock_addrs[uaddr].addr1, + unlock_addrs[uaddr].addr2); goto match_done; } @@ -1857,10 +1965,10 @@ */ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); if(cfi->addr_unlock1) { - cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } - cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* FIXME - should have a delay before continuing */ match_done: @@ -1873,19 +1981,17 @@ { int i; enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED; + u32 probe_offset1, probe_offset2; retry: if (!cfi->numchips) { - unsigned long mask = ~(cfi->device_type-1); - uaddr_idx++; if (MTD_UADDR_UNNECESSARY == uaddr_idx) return 0; - /* Mask out address bits which are smaller than the device type */ - cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask; - cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask; + cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; + cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; } /* Make certain we aren't probing past the end of map */ @@ -1896,30 +2002,30 @@ return 0; } - if ((base + cfi->addr_unlock1) >= map->size) { - printk(KERN_NOTICE - "Probe at addr_unlock1(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", - base, cfi->addr_unlock1, map->size -1); - - return 0; + /* Ensure the unlock addresses we try stay inside the map */ + probe_offset1 = cfi_build_cmd_addr( + cfi->addr_unlock1, + cfi_interleave(cfi), + cfi->device_type); + probe_offset2 = cfi_build_cmd_addr( + cfi->addr_unlock1, + cfi_interleave(cfi), + cfi->device_type); + if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) || + ((base + probe_offset2 + map_bankwidth(map)) >= map->size)) + { + goto retry; } - if ((base + cfi->addr_unlock2) >= map->size) { - printk(KERN_NOTICE - "Probe at addr_unlock2(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", - base, cfi->addr_unlock2, map->size -1); - return 0; - } - /* Reset */ jedec_reset(base, map, cfi); /* Autoselect Mode */ if(cfi->addr_unlock1) { - cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } - cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* FIXME - should have a delay before continuing */ if (!cfi->numchips) { @@ -1930,7 +2036,7 @@ cfi->id = jedec_read_id(map, base, cfi); DEBUG(MTD_DEBUG_LEVEL3, "Search for id:(%02x %02x) interleave(%d) type(%d)\n", - cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); + cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type); for (i=0; iname, cfi->interleave, cfi->device_type*8, base, + map->name, cfi_interleave(cfi), cfi->device_type*8, base, map->bankwidth*8); return 1; @@ -2015,7 +2121,7 @@ .probe_chip = jedec_probe_chip }; -struct mtd_info *jedec_probe(struct map_info *map) +static struct mtd_info *jedec_probe(struct map_info *map) { /* * Just use the generic probe stuff to call our CFI-specific @@ -2030,7 +2136,7 @@ .module = THIS_MODULE }; -int __init jedec_probe_init(void) +static int __init jedec_probe_init(void) { register_mtd_chip_driver(&jedec_chipdrv); return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/Kconfig linux-2.6.9-intc1-mih1/drivers/mtd/chips/Kconfig --- linux-2.6.9-intc1/drivers/mtd/chips/Kconfig 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/Kconfig 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ # drivers/mtd/chips/Kconfig -# $Id: Kconfig,v 1.9 2004/07/16 15:32:14 dwmw2 Exp $ +# $Id: Kconfig,v 1.14 2005/02/08 17:11:15 nico Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -7,6 +7,7 @@ config MTD_CFI tristate "Detect flash chips by Common Flash Interface (CFI) probe" depends on MTD + select MTD_GEN_PROBE help The Common Flash Interface specification was developed by Intel, AMD and other flash manufactures that provides a universal method @@ -18,6 +19,7 @@ config MTD_JEDECPROBE tristate "Detect non-CFI AMD/JEDEC-compatible flash chips" depends on MTD + select MTD_GEN_PROBE help This option enables JEDEC-style probing of flash chips which are not compatible with the Common Flash Interface, but will use the common @@ -29,8 +31,6 @@ config MTD_GEN_PROBE tristate - default m if MTD_CFI!=y && !MTD_INTELPROBE && MTD_JEDECPROBE!=y && (MTD_CFI=m || MTD_JEDECPROBE=m) - default y if MTD_CFI=y || MTD_INTELPROBE || MTD_JEDECPROBE=y config MTD_CFI_ADV_OPTIONS bool "Flash chip driver advanced configuration options" @@ -155,9 +155,35 @@ If your flash chips are interleaved in eights - i.e. you have eight flash chips addressed by each bus cycle, then say 'Y'. +config MTD_OTP + bool "Protection Registers aka one-time programmable (OTP) bits" + depends on MTD_CFI_ADV_OPTIONS + default n + help + This enables support for reading, writing and locking so called + "Protection Registers" present on some flash chips. + A subset of them are pre-programmed at the factory with a + unique set of values. The rest is user-programmable. + + The user-programmable Protection Registers contain one-time + programmable (OTP) bits; when programmed, register bits cannot be + erased. Each Protection Register can be accessed multiple times to + program individual bits, as long as the register remains unlocked. + + Each Protection Register has an associated Lock Register bit. When a + Lock Register bit is programmed, the associated Protection Register + can only be read; it can no longer be programmed. Additionally, + because the Lock Register bits themselves are OTP, when programmed, + Lock Register bits cannot be erased. Therefore, when a Protection + Register is locked, it cannot be unlocked. + + This feature should therefore be used with extreme care. Any mistake + in the programming of OTP bits will waste them. + config MTD_CFI_INTELEXT tristate "Support for Intel/Sharp flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -167,6 +193,7 @@ config MTD_CFI_AMDSTD tristate "Support for AMD/Fujitsu flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -197,6 +224,7 @@ config MTD_CFI_STAA tristate "Support for ST (Advanced Architecture) flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -204,8 +232,6 @@ config MTD_CFI_UTIL tristate - default y if MTD_CFI_INTELEXT=y || MTD_CFI_AMDSTD=y || MTD_CFI_STAA=y - default m if MTD_CFI_INTELEXT=m || MTD_CFI_AMDSTD=m || MTD_CFI_STAA=m config MTD_RAM tristate "Support for RAM chips in bus mapping" @@ -272,5 +298,14 @@ distributes the identification codes for the chips. +config MTD_XIP + bool "XIP aware MTD support" + depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL + default y if XIP_KERNEL + help + This allows MTD support to work with flash memory which is also + used for XIP purposes. If you're not sure what this is all about + then say N. + endmenu diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/map_absent.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/map_absent.c --- linux-2.6.9-intc1/drivers/mtd/chips/map_absent.c 2004-10-18 17:54:40.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/map_absent.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* * Common code to handle absent "placeholder" devices * Copyright 2001 Resilience Corporation - * $Id: map_absent.c,v 1.4 2003/05/28 12:51:49 dwmw2 Exp $ + * $Id: map_absent.c,v 1.5 2004/11/16 18:29:00 dwmw2 Exp $ * * This map driver is used to allocate "placeholder" MTD * devices on systems that have socketed/removable media. @@ -98,7 +98,7 @@ /* nop */ } -int __init map_absent_init(void) +static int __init map_absent_init(void) { register_mtd_chip_driver(&map_absent_chipdrv); return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/map_ram.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/map_ram.c --- linux-2.6.9-intc1/drivers/mtd/chips/map_ram.c 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/map_ram.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.20 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -83,7 +83,7 @@ static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -92,7 +92,7 @@ static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_to(map, to, buf, len); *retlen = len; @@ -103,7 +103,7 @@ { /* Yeah, it's inefficient. Who cares? It's faster than a _real_ flash erase. */ - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_word allff; unsigned long i; @@ -124,7 +124,7 @@ /* Nothing to see here */ } -int __init map_ram_init(void) +static int __init map_ram_init(void) { register_mtd_chip_driver(&mapram_chipdrv); return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/chips/map_rom.c linux-2.6.9-intc1-mih1/drivers/mtd/chips/map_rom.c --- linux-2.6.9-intc1/drivers/mtd/chips/map_rom.c 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/chips/map_rom.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.21 2004/07/12 14:06:01 dwmw2 Exp $ + * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -19,7 +19,7 @@ static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static void maprom_nop (struct mtd_info *); -struct mtd_info *map_rom_probe(struct map_info *map); +static struct mtd_info *map_rom_probe(struct map_info *map); static struct mtd_chip_driver maprom_chipdrv = { .probe = map_rom_probe, @@ -27,7 +27,7 @@ .module = THIS_MODULE }; -struct mtd_info *map_rom_probe(struct map_info *map) +static struct mtd_info *map_rom_probe(struct map_info *map) { struct mtd_info *mtd; @@ -57,7 +57,7 @@ static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -75,7 +75,7 @@ return -EIO; } -int __init map_rom_init(void) +static int __init map_rom_init(void) { register_mtd_chip_driver(&maprom_chipdrv); return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/cmdlinepart.c linux-2.6.9-intc1-mih1/drivers/mtd/cmdlinepart.c --- linux-2.6.9-intc1/drivers/mtd/cmdlinepart.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/cmdlinepart.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: cmdlinepart.c,v 1.14 2004/07/12 12:34:23 dwmw2 Exp $ + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * * Read flash partition table from command line * @@ -338,8 +338,10 @@ * This is the handler for our kernel parameter, called from * main.c::checksetup(). Note that we can not yet kmalloc() anything, * so we only save the commandline for later processing. + * + * This function needs to be visible for bootloaders. */ -static int __init mtdpart_setup(char *s) +int mtdpart_setup(char *s) { cmdline = s; return 1; diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/blkmtd.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/blkmtd.c --- linux-2.6.9-intc1/drivers/mtd/devices/blkmtd.c 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/blkmtd.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: blkmtd.c,v 1.23 2004/08/09 14:03:19 dwmw2 Exp $ + * $Id: blkmtd.c,v 1.26 2005/03/18 01:37:31 gleixner Exp $ * * blkmtd.c - use a block device as a fake MTD * @@ -39,7 +39,7 @@ /* Default erase size in K, always make it a multiple of PAGE_SIZE */ #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ -#define VERSION "$Revision: 1.23 $" +#define VERSION "$Revision: 1.26 $" /* Info for the block device */ struct blkmtd_dev { @@ -59,22 +59,22 @@ #define MAX_DEVICES 4 /* Module parameters passed by insmod/modprobe */ -char *device[MAX_DEVICES]; /* the block device to use */ -int erasesz[MAX_DEVICES]; /* optional default erase size */ -int ro[MAX_DEVICES]; /* optional read only flag */ -int sync; +static char *device[MAX_DEVICES]; /* the block device to use */ +static int erasesz[MAX_DEVICES]; /* optional default erase size */ +static int ro[MAX_DEVICES]; /* optional read only flag */ +static int sync; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Simon Evans "); MODULE_DESCRIPTION("Emulate an MTD using a block device"); -MODULE_PARM(device, "1-4s"); +module_param_array(device, charp, NULL, 0); MODULE_PARM_DESC(device, "block device to use"); -MODULE_PARM(erasesz, "1-4i"); +module_param_array(erasesz, int, NULL, 0); MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); -MODULE_PARM(ro, "1-4i"); +module_param_array(ro, bool, NULL, 0); MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); -MODULE_PARM(sync, "i"); +module_param(sync, bool, 0); MODULE_PARM_DESC(sync, "1=Synchronous writes"); @@ -661,7 +661,6 @@ memset(dev, 0, sizeof(struct blkmtd_dev)); dev->blkdev = bdev; - atomic_set(&(dev->blkdev->bd_inode->i_mapping->truncate_count), 0); if(!readonly) { init_MUTEX(&dev->wrbuf_mutex); } diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/block2mtd.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/block2mtd.c --- linux-2.6.9-intc1/drivers/mtd/devices/block2mtd.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/block2mtd.c 2005-03-21 21:28:42.000000000 -0500 @@ -0,0 +1,493 @@ +/* + * $Id: block2mtd.c,v 1.28 2005/03/19 22:40:44 gleixner Exp $ + * + * block2mtd.c - create an mtd from a block device + * + * Copyright (C) 2001,2002 Simon Evans + * Copyright (C) 2004,2005 Jörn Engel + * + * Licence: GPL + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "$Revision: 1.28 $" + + +#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) +#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) + + +/* Info for the block device */ +struct block2mtd_dev { + struct list_head list; + struct block_device *blkdev; + struct mtd_info mtd; + struct semaphore write_mutex; +}; + + +/* Static info about the MTD, used in cleanup_module */ +static LIST_HEAD(blkmtd_device_list); + + +#define PAGE_READAHEAD 64 +void cache_readahead(struct address_space *mapping, int index) +{ + filler_t *filler = (filler_t*)mapping->a_ops->readpage; + int i, pagei; + unsigned ret = 0; + unsigned long end_index; + struct page *page; + LIST_HEAD(page_pool); + struct inode *inode = mapping->host; + loff_t isize = i_size_read(inode); + + if (!isize) { + INFO("iSize=0 in cache_readahead\n"); + return; + } + + end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); + + read_lock_irq(&mapping->tree_lock); + for (i = 0; i < PAGE_READAHEAD; i++) { + pagei = index + i; + if (pagei > end_index) { + INFO("Overrun end of disk in cache readahead\n"); + break; + } + page = radix_tree_lookup(&mapping->page_tree, pagei); + if (page && (!i)) + break; + if (page) + continue; + read_unlock_irq(&mapping->tree_lock); + page = page_cache_alloc_cold(mapping); + read_lock_irq(&mapping->tree_lock); + if (!page) + break; + page->index = pagei; + list_add(&page->lru, &page_pool); + ret++; + } + read_unlock_irq(&mapping->tree_lock); + if (ret) + read_cache_pages(mapping, &page_pool, filler, NULL); +} + + +static struct page* page_readahead(struct address_space *mapping, int index) +{ + filler_t *filler = (filler_t*)mapping->a_ops->readpage; + cache_readahead(mapping, index); + return read_cache_page(mapping, index, filler, NULL); +} + + +/* erase a specified part of the device */ +static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) +{ + struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; + struct page *page; + int index = to >> PAGE_SHIFT; // page index + int pages = len >> PAGE_SHIFT; + u_long *p; + u_long *max; + + while (pages) { + page = page_readahead(mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + max = (u_long*)page_address(page) + PAGE_SIZE; + for (p=(u_long*)page_address(page); ppriv; + size_t from = instr->addr; + size_t len = instr->len; + int err; + + instr->state = MTD_ERASING; + down(&dev->write_mutex); + err = _block2mtd_erase(dev, from, len); + up(&dev->write_mutex); + if (err) { + ERROR("erase failed err = %d", err); + instr->state = MTD_ERASE_FAILED; + } else + instr->state = MTD_ERASE_DONE; + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return err; +} + + +static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct block2mtd_dev *dev = mtd->priv; + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SIZE-1); + int cpylen; + + if (from > mtd->size) + return -EINVAL; + if (from + len > mtd->size) + len = mtd->size - from; + + if (retlen) + *retlen = 0; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; // multiple pages + else + cpylen = len; // this page + len = len - cpylen; + + // Get page + page = page_readahead(dev->blkdev->bd_inode->i_mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + memcpy(buf, page_address(page) + offset, cpylen); + page_cache_release(page); + + if (retlen) + *retlen += cpylen; + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + + +/* write data to the underlying device */ +static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, + loff_t to, size_t len, size_t *retlen) +{ + struct page *page; + struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; + int index = to >> PAGE_SHIFT; // page index + int offset = to & ~PAGE_MASK; // page offset + int cpylen; + + if (retlen) + *retlen = 0; + while (len) { + if ((offset+len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; // multiple pages + else + cpylen = len; // this page + len = len - cpylen; + + // Get page + page = page_readahead(mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + if (memcmp(page_address(page)+offset, buf, cpylen)) { + lock_page(page); + memcpy(page_address(page) + offset, buf, cpylen); + set_page_dirty(page); + unlock_page(page); + } + page_cache_release(page); + + if (retlen) + *retlen += cpylen; + + buf += cpylen; + offset = 0; + index++; + } + return 0; +} +static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct block2mtd_dev *dev = mtd->priv; + int err; + + if (!len) + return 0; + if (to >= mtd->size) + return -ENOSPC; + if (to + len > mtd->size) + len = mtd->size - to; + + down(&dev->write_mutex); + err = _block2mtd_write(dev, buf, to, len, retlen); + up(&dev->write_mutex); + if (err > 0) + err = 0; + return err; +} + + +/* sync the device - wait until the write queue is empty */ +static void block2mtd_sync(struct mtd_info *mtd) +{ + struct block2mtd_dev *dev = mtd->priv; + sync_blockdev(dev->blkdev); + return; +} + + +static void block2mtd_free_device(struct block2mtd_dev *dev) +{ + if (!dev) + return; + + kfree(dev->mtd.name); + + if (dev->blkdev) { + invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); + close_bdev_excl(dev->blkdev); + } + + kfree(dev); +} + + +/* FIXME: ensure that mtd->size % erase_size == 0 */ +static struct block2mtd_dev *add_device(char *devname, int erase_size) +{ + struct block_device *bdev; + struct block2mtd_dev *dev; + + if (!devname) + return NULL; + + dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); + + /* Get a handle on the device */ + bdev = open_bdev_excl(devname, O_RDWR, NULL); + if (IS_ERR(bdev)) { + ERROR("error: cannot open device %s", devname); + goto devinit_err; + } + dev->blkdev = bdev; + + if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { + ERROR("attempting to use an MTD device as a block device"); + goto devinit_err; + } + + init_MUTEX(&dev->write_mutex); + + /* Setup the MTD structure */ + /* make the name contain the block device in */ + dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), + GFP_KERNEL); + if (!dev->mtd.name) + goto devinit_err; + + sprintf(dev->mtd.name, "block2mtd: %s", devname); + + dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; + dev->mtd.erasesize = erase_size; + dev->mtd.type = MTD_RAM; + dev->mtd.flags = MTD_CAP_RAM; + dev->mtd.erase = block2mtd_erase; + dev->mtd.write = block2mtd_write; + dev->mtd.writev = default_mtd_writev; + dev->mtd.sync = block2mtd_sync; + dev->mtd.read = block2mtd_read; + dev->mtd.readv = default_mtd_readv; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; + + if (add_mtd_device(&dev->mtd)) { + /* Device didnt get added, so free the entry */ + goto devinit_err; + } + list_add(&dev->list, &blkmtd_device_list); + INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, + dev->mtd.name + strlen("blkmtd: "), + dev->mtd.erasesize >> 10, dev->mtd.erasesize); + return dev; + +devinit_err: + block2mtd_free_device(dev); + return NULL; +} + + +static int ustrtoul(const char *cp, char **endp, unsigned int base) +{ + unsigned long result = simple_strtoul(cp, endp, base); + switch (**endp) { + case 'G' : + result *= 1024; + case 'M': + result *= 1024; + case 'k': + result *= 1024; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; + } + return result; +} + + +static int parse_num(size_t *num, const char *token) +{ + char *endp; + size_t n; + + n = (size_t) ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + + *num = n; + return 0; +} + + +static int parse_name(char **pname, const char *token, size_t limit) +{ + size_t len; + char *name; + + len = strlen(token) + 1; + if (len > limit) + return -ENOSPC; + + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, token); + + *pname = name; + return 0; +} + + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + +#define parse_err(fmt, args...) do { \ + ERROR("block2mtd: " fmt "\n", ## args); \ + return 0; \ +} while (0) + +static int block2mtd_setup(const char *val, struct kernel_param *kp) +{ + char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ + char *token[2]; + char *name; + size_t erase_size = PAGE_SIZE; + int i, ret; + + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) + parse_err("parameter too long"); + + strcpy(str, val); + kill_final_newline(str); + + for (i=0; i<2; i++) + token[i] = strsep(&str, ","); + + if (str) + parse_err("too many arguments"); + + if (!token[0]) + parse_err("no argument"); + + ret = parse_name(&name, token[0], 80); + if (ret == -ENOMEM) + parse_err("out of memory"); + if (ret == -ENOSPC) + parse_err("name too long"); + if (ret) + return 0; + + if (token[1]) { + ret = parse_num(&erase_size, token[1]); + if (ret) + parse_err("illegal erase size"); + } + + add_device(name, erase_size); + + return 0; +} + + +module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); +MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); + +static int __init block2mtd_init(void) +{ + INFO("version " VERSION); + return 0; +} + + +static void __devexit block2mtd_exit(void) +{ + struct list_head *pos, *next; + + /* Remove the MTD devices */ + list_for_each_safe(pos, next, &blkmtd_device_list) { + struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); + block2mtd_sync(&dev->mtd); + del_mtd_device(&dev->mtd); + INFO("mtd%d: [%s] removed", dev->mtd.index, + dev->mtd.name + strlen("blkmtd: ")); + list_del(&dev->list); + block2mtd_free_device(dev); + } +} + + +module_init(block2mtd_init); +module_exit(block2mtd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans and others"); +MODULE_DESCRIPTION("Emulate an MTD using a block device"); diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/doc2000.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/doc2000.c --- linux-2.6.9-intc1/drivers/mtd/devices/doc2000.c 2004-10-18 17:53:05.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/doc2000.c 2005-03-21 21:28:42.000000000 -0500 @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2000.c,v 1.62 2004/08/09 14:04:02 dwmw2 Exp $ + * $Id: doc2000.c,v 1.66 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -89,7 +89,7 @@ /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ static int _DoC_WaitReady(struct DiskOnChip *doc) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; unsigned long timeo = jiffies + (HZ * 10); DEBUG(MTD_DEBUG_LEVEL3, @@ -114,7 +114,8 @@ static inline int DoC_WaitReady(struct DiskOnChip *doc) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; + /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0; @@ -140,7 +141,7 @@ static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, unsigned char xtraflags) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; if (DoC_is_2000(doc)) xtraflags |= CDSN_CTRL_FLASH_IO; @@ -172,10 +173,8 @@ static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, unsigned char xtraflags1, unsigned char xtraflags2) { - unsigned long docptr; int i; - - docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; if (DoC_is_2000(doc)) xtraflags1 |= CDSN_CTRL_FLASH_IO; @@ -240,11 +239,9 @@ { volatile int dummy; int modulus = 0xffff; - unsigned long docptr; + void __iomem *docptr = doc->virtadr; int i; - docptr = doc->virtadr; - if (len <= 0) return; @@ -271,11 +268,9 @@ /* Write a buffer to DoC, taking care of Millennium odditys */ static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len) { - unsigned long docptr; + void __iomem *docptr = doc->virtadr; int i; - docptr = doc->virtadr; - if (len <= 0) return; @@ -292,7 +287,7 @@ static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; /* Software requirement 11.4.4 before writing DeviceSelect */ /* Deassert the CE line to eliminate glitches on the FCE# outputs */ @@ -316,7 +311,7 @@ static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; /* Select the floor (bank) of chips required */ WriteDOC(floor, docptr, FloorSelect); @@ -532,26 +527,26 @@ */ static void DoC2k_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; int maxchips; /* We must avoid being called twice for the same device. */ if (doc2klist) - old = (struct DiskOnChip *) doc2klist->priv; + old = doc2klist->priv; while (old) { if (DoC2k_is_alias(old, this)) { printk(KERN_NOTICE "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", this->physadr); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *) old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -578,7 +573,7 @@ default: printk("Unknown ChipID 0x%02x\n", this->ChipID); kfree(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); return; } @@ -617,7 +612,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = doc2klist; doc2klist = mtd; @@ -638,16 +633,14 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; - unsigned long docptr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip; unsigned char syndrome[6]; volatile char dummy; int i, len256 = 0, ret=0; size_t left = len; - docptr = this->virtadr; - /* Don't allow read past end of device */ if (from >= this->totlen) return -EINVAL; @@ -797,17 +790,15 @@ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ - unsigned long docptr; + void __iomem *docptr = this->virtadr; volatile char dummy; int len256 = 0; struct Nand *mychip; size_t left = len; int status; - docptr = this->virtadr; - /* Don't allow write past end of device */ if (to >= this->totlen) return -EINVAL; @@ -1042,15 +1033,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0, ret; - unsigned long docptr; struct Nand *mychip; down(&this->lock); - docptr = this->virtadr; - mychip = &this->chips[ofs >> this->chipshift]; if (this->curfloor != mychip->floor) { @@ -1103,9 +1091,9 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0; - unsigned long docptr = this->virtadr; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; volatile int dummy; int status; @@ -1206,7 +1194,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int ret; down(&this->lock); @@ -1218,11 +1206,11 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; volatile int dummy; - unsigned long docptr; + void __iomem *docptr = this->virtadr; struct Nand *mychip; int status; @@ -1235,8 +1223,6 @@ instr->state = MTD_ERASING; - docptr = this->virtadr; - /* FIXME: Do this in the background. Use timers or schedule_task() */ while(len) { mychip = &this->chips[ofs >> this->chipshift]; @@ -1290,7 +1276,7 @@ * ****************************************************************************/ -int __init init_doc2000(void) +static int __init init_doc2000(void) { inter_module_register(im_name, THIS_MODULE, &DoC2k_init); return 0; @@ -1302,12 +1288,12 @@ struct DiskOnChip *this; while ((mtd = doc2klist)) { - this = (struct DiskOnChip *) mtd->priv; + this = mtd->priv; doc2klist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/doc2001.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/doc2001.c --- linux-2.6.9-intc1/drivers/mtd/devices/doc2001.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/doc2001.c 2005-03-21 21:28:42.000000000 -0500 @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001.c,v 1.44 2004/08/09 14:04:24 dwmw2 Exp $ + * $Id: doc2001.c,v 1.48 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -52,7 +52,7 @@ static struct mtd_info *docmillist = NULL; /* Perform the required delay cycles by reading from the NOP register */ -static void DoC_Delay(unsigned long docptr, unsigned short cycles) +static void DoC_Delay(void __iomem * docptr, unsigned short cycles) { volatile char dummy; int i; @@ -62,7 +62,7 @@ } /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(unsigned long docptr) +static int _DoC_WaitReady(void __iomem * docptr) { unsigned short c = 0xffff; @@ -79,7 +79,7 @@ return (c == 0); } -static inline int DoC_WaitReady(unsigned long docptr) +static inline int DoC_WaitReady(void __iomem * docptr) { /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0; @@ -103,7 +103,7 @@ with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static inline void DoC_Command(unsigned long docptr, unsigned char command, +static inline void DoC_Command(void __iomem * docptr, unsigned char command, unsigned char xtraflags) { /* Assert the CLE (Command Latch Enable) line to the flash chip */ @@ -123,7 +123,7 @@ with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs, +static inline void DoC_Address(void __iomem * docptr, int numbytes, unsigned long ofs, unsigned char xtraflags1, unsigned char xtraflags2) { /* Assert the ALE (Address Latch Enable) line to the flash chip */ @@ -161,7 +161,7 @@ } /* DoC_SelectChip: Select a given flash chip within the current floor */ -static int DoC_SelectChip(unsigned long docptr, int chip) +static int DoC_SelectChip(void __iomem * docptr, int chip) { /* Select the individual flash chip requested */ WriteDOC(chip, docptr, CDSNDeviceSelect); @@ -172,7 +172,7 @@ } /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ -static int DoC_SelectFloor(unsigned long docptr, int floor) +static int DoC_SelectFloor(void __iomem * docptr, int floor) { /* Select the floor (bank) of chips required */ WriteDOC(floor, docptr, FloorSelect); @@ -335,23 +335,23 @@ */ static void DoCMil_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmillist) - old = (struct DiskOnChip *)docmillist->priv; + old = docmillist->priv; while (old) { if (DoCMil_is_alias(this, old)) { printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at " "0x%lX - already configured\n", this->physadr); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -392,7 +392,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = docmillist; docmillist = mtd; @@ -416,8 +416,8 @@ int i, ret; volatile char dummy; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; /* Don't allow read past end of device */ @@ -542,8 +542,8 @@ { int i,ret = 0; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; /* Don't allow write past end of device */ @@ -677,8 +677,8 @@ int i; #endif volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; /* Find the chip which is to be used and select it */ @@ -729,8 +729,8 @@ #endif volatile char dummy; int ret = 0; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; /* Find the chip which is to be used and select it */ @@ -796,10 +796,10 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; - unsigned long docptr = this->virtadr; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; if (len != mtd->erasesize) @@ -856,7 +856,7 @@ * ****************************************************************************/ -int __init init_doc2001(void) +static int __init init_doc2001(void) { inter_module_register(im_name, THIS_MODULE, &DoCMil_init); return 0; @@ -868,12 +868,12 @@ struct DiskOnChip *this; while ((mtd=docmillist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmillist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/doc2001plus.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/doc2001plus.c --- linux-2.6.9-intc1/drivers/mtd/devices/doc2001plus.c 2004-10-18 17:53:05.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/doc2001plus.c 2005-03-21 21:28:42.000000000 -0500 @@ -6,7 +6,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001plus.c,v 1.9 2004/08/09 13:19:44 dwmw2 Exp $ + * $Id: doc2001plus.c,v 1.13 2005/01/05 18:05:12 dwmw2 Exp $ * * Released under GPL */ @@ -57,7 +57,7 @@ /* Perform the required delay cycles by writing to the NOP register */ -static void DoC_Delay(unsigned long docptr, int cycles) +static void DoC_Delay(void __iomem * docptr, int cycles) { int i; @@ -68,7 +68,7 @@ #define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(unsigned long docptr) +static int _DoC_WaitReady(void __iomem * docptr) { unsigned int c = 0xffff; @@ -85,7 +85,7 @@ return (c == 0); } -static inline int DoC_WaitReady(unsigned long docptr) +static inline int DoC_WaitReady(void __iomem * docptr) { /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0; @@ -106,7 +106,7 @@ * can detect. M-systems suggest always check this on any block level * operation and setting to normal mode if in reset mode. */ -static inline void DoC_CheckASIC(unsigned long docptr) +static inline void DoC_CheckASIC(void __iomem * docptr) { /* Make sure the DoC is in normal mode */ if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { @@ -118,7 +118,7 @@ /* DoC_Command: Send a flash command to the flash chip through the Flash * command register. Need 2 Write Pipeline Terminates to complete send. */ -static inline void DoC_Command(unsigned long docptr, unsigned char command, +static inline void DoC_Command(void __iomem * docptr, unsigned char command, unsigned char xtraflags) { WriteDOC(command, docptr, Mplus_FlashCmd); @@ -133,7 +133,7 @@ unsigned long ofs, unsigned char xtraflags1, unsigned char xtraflags2) { - unsigned long docptr = doc->virtadr; + void __iomem * docptr = doc->virtadr; /* Allow for possible Mill Plus internal flash interleaving */ ofs >>= doc->interleave; @@ -163,14 +163,14 @@ } /* DoC_SelectChip: Select a given flash chip within the current floor */ -static int DoC_SelectChip(unsigned long docptr, int chip) +static int DoC_SelectChip(void __iomem * docptr, int chip) { /* No choice for flash chip on Millennium Plus */ return 0; } /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ -static int DoC_SelectFloor(unsigned long docptr, int floor) +static int DoC_SelectFloor(void __iomem * docptr, int floor) { WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); return 0; @@ -190,7 +190,7 @@ may not want it */ static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; if (this->interleave) { unsigned int ofs = *from & 0x3ff; @@ -253,7 +253,7 @@ return cmd; } -static inline void MemReadDOC(unsigned long docptr, unsigned char *buf, int len) +static inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) { #ifndef USE_MEMCPY int i; @@ -264,7 +264,7 @@ #endif } -static inline void MemWriteDOC(unsigned long docptr, unsigned char *buf, int len) +static inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) { #ifndef USE_MEMCPY int i; @@ -280,7 +280,7 @@ { int mfr, id, i, j; volatile char dummy; - unsigned long docptr = doc->virtadr; + void __iomem * docptr = doc->virtadr; /* Page in the required floor/chip */ DoC_SelectFloor(docptr, floor); @@ -371,7 +371,7 @@ printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", this->interleave?"on (16-bit)":"off (8-bit)"); conf ^= 4; - WriteDOC(this->virtadr, conf, Mplus_Configuration); + WriteDOC(conf, this->virtadr, Mplus_Configuration); } /* For each floor, find the number of valid chips it contains */ @@ -458,24 +458,24 @@ */ static void DoCMilPlus_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmilpluslist) - old = (struct DiskOnChip *)docmilpluslist->priv; + old = docmilpluslist->priv; while (old) { if (DoCMilPlus_is_alias(this, old)) { printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " "Plus at 0x%lX - already configured\n", this->physadr); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -514,7 +514,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = docmilpluslist; docmilpluslist = mtd; @@ -530,8 +530,8 @@ { int i; loff_t fofs; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; unsigned char *bp, buf[1056]; char c[32]; @@ -615,8 +615,8 @@ volatile char dummy; loff_t fofs; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; /* Don't allow read past end of device */ @@ -754,8 +754,8 @@ int i, before, ret = 0; loff_t fto; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; /* Don't allow write past end of device */ @@ -880,8 +880,8 @@ size_t *retlen, u_char *buf) { loff_t fofs, base; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; @@ -958,8 +958,8 @@ { volatile char dummy; loff_t fofs, base; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; int ret = 0; @@ -1058,10 +1058,10 @@ int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; - unsigned long docptr = this->virtadr; + void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; DoC_CheckASIC(docptr); @@ -1122,7 +1122,7 @@ * ****************************************************************************/ -int __init init_doc2001plus(void) +static int __init init_doc2001plus(void) { inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); return 0; @@ -1134,12 +1134,12 @@ struct DiskOnChip *this; while ((mtd=docmilpluslist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmilpluslist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/docprobe.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/docprobe.c --- linux-2.6.9-intc1/drivers/mtd/devices/docprobe.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/docprobe.c 2005-03-21 21:28:42.000000000 -0500 @@ -4,7 +4,7 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse */ -/* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $ */ +/* $Id: docprobe.c,v 1.45 2005/03/17 19:44:27 gleixner Exp $ */ @@ -62,7 +62,7 @@ static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS; -MODULE_PARM(doc_config_location, "l"); +module_param(doc_config_location, ulong, 0); MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); static unsigned long __initdata doc_locations[] = { @@ -94,9 +94,9 @@ /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ -static inline int __init doccheck(unsigned long potential, unsigned long physadr) +static inline int __init doccheck(void __iomem *potential, unsigned long physadr) { - unsigned long window=potential; + void __iomem *window=potential; unsigned char tmp, tmpb, tmpc, ChipID; #ifndef DOC_PASSIVE_PROBE unsigned char tmp2; @@ -233,7 +233,7 @@ static void __init DoC_Probe(unsigned long physadr) { - unsigned long docptr; + void __iomem *docptr; struct DiskOnChip *this; struct mtd_info *mtd; int ChipID; @@ -243,7 +243,7 @@ char *im_modname = NULL; void (*initroutine)(struct mtd_info *) = NULL; - docptr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN); + docptr = ioremap(physadr, DOC_IOREMAP_LEN); if (!docptr) return; @@ -252,7 +252,7 @@ if (ChipID == DOC_ChipID_Doc2kTSOP) { /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); - iounmap((void *)docptr); + iounmap(docptr); return; } docfound = 1; @@ -260,7 +260,7 @@ if (!mtd) { printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n"); - iounmap((void *)docptr); + iounmap(docptr); return; } @@ -318,7 +318,7 @@ printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); kfree(mtd); } - iounmap((void *)docptr); + iounmap(docptr); } @@ -328,7 +328,7 @@ * ****************************************************************************/ -int __init init_doc(void) +static int __init init_doc(void) { int i; diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/Kconfig linux-2.6.9-intc1-mih1/drivers/mtd/devices/Kconfig --- linux-2.6.9-intc1/drivers/mtd/devices/Kconfig 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/Kconfig 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.12 2004/08/10 13:12:18 dwmw2 Exp $ +# $Id: Kconfig,v 1.16 2005/01/06 15:15:47 dwmw2 Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n @@ -125,11 +125,22 @@ Testing MTD users (eg JFFS2) on large media and media that might be removed during a write (using the floppy drive). +config MTD_BLOCK2MTD + tristate "MTD using block device (rewrite)" + depends on MTD && EXPERIMENTAL + help + This driver is basically the same at MTD_BLKMTD above, but + experienced some interface changes plus serious speedups. In + the long term, it should replace MTD_BLKMTD. Right now, you + shouldn't entrust important data to it yet. + comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip 2000 and Millennium devices. Originally designed for the DiskOnChip @@ -151,6 +162,8 @@ config MTD_DOC2001 tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an alternative MTD device driver for the M-Systems DiskOnChip Millennium devices. Use this if you have problems with @@ -171,6 +184,8 @@ config MTD_DOC2001PLUS tristate "M-Systems Disk-On-Chip Millennium Plus" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip Millennium Plus devices. @@ -186,17 +201,10 @@ config MTD_DOCPROBE tristate - default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MTD_DOC2001PLUS=m) - default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y - help - This isn't a real config option; it's derived. + select MTD_DOCECC config MTD_DOCECC tristate - default m if MTD_DOCPROBE!=y && MTD_NAND_DISKONCHIP!=y && (MTD_DOCPROBE=m || MTD_NAND_DISKONCHIP=m) - default y if MTD_DOCPROBE=y || MTD_NAND_DISKONCHIP=y - help - This isn't a real config option; it's derived. config MTD_DOCPROBE_ADVANCED bool "Advanced detection options for DiskOnChip" diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/Makefile linux-2.6.9-intc1-mih1/drivers/mtd/devices/Makefile --- linux-2.6.9-intc1/drivers/mtd/devices/Makefile 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/Makefile 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $ +# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ # *** BIG UGLY NOTE *** # @@ -22,3 +22,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o +obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/ms02-nv.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/ms02-nv.c --- linux-2.6.9-intc1/drivers/mtd/devices/ms02-nv.c 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/ms02-nv.c 2005-03-21 21:28:42.000000000 -0500 @@ -6,7 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: ms02-nv.c,v 1.7 2004/07/29 14:16:45 macro Exp $ + * $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -59,7 +59,7 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -73,7 +73,7 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (to + len > mtd->size) return -EINVAL; @@ -265,7 +265,7 @@ static void __exit ms02nv_remove_one(void) { struct mtd_info *mtd = root_ms02nv_mtd; - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; root_ms02nv_mtd = mp->next; diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/mtdram.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/mtdram.c --- linux-2.6.9-intc1/drivers/mtd/devices/mtdram.c 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/mtdram.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,6 +1,6 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.33 2004/08/09 13:19:44 dwmw2 Exp $ + * $Id: mtdram.c,v 1.36 2005/03/17 19:44:27 gleixner Exp $ * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson @@ -29,9 +29,9 @@ #ifdef MODULE static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; -MODULE_PARM(total_size,"l"); +module_param(total_size,ulong,0); MODULE_PARM_DESC(total_size, "Total device size in KiB"); -MODULE_PARM(erase_size,"l"); +module_param(erase_size,ulong,0); MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); #define MTDRAM_TOTAL_SIZE (total_size * 1024) #define MTDRAM_ERASE_SIZE (erase_size * 1024) @@ -153,12 +153,12 @@ #if CONFIG_MTDRAM_TOTAL_SIZE > 0 #if CONFIG_MTDRAM_ABS_POS > 0 -int __init init_mtdram(void) +static int __init init_mtdram(void) { void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; @@ -186,12 +186,12 @@ #else /* CONFIG_MTDRAM_ABS_POS > 0 */ -int __init init_mtdram(void) +static int __init init_mtdram(void) { void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; @@ -220,7 +220,7 @@ #else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */ -int __init init_mtdram(void) +static int __init init_mtdram(void) { return 0; } diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/phram.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/phram.c --- linux-2.6.9-intc1/drivers/mtd/devices/phram.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/phram.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,13 +1,8 @@ /** + * $Id: phram.c,v 1.14 2005/03/07 21:43:38 joern Exp $ * - * $Id: phram.c,v 1.2 2004/08/09 13:19:44 dwmw2 Exp $ - * - * Copyright (c) Jochen Schaeuble - * 07/2003 rewritten by Joern Engel - * - * DISCLAIMER: This driver makes use of Rusty's excellent module code, - * so it will not work for 2.4 without changes and it wont work for 2.4 - * as a module without major changes. Oh well! + * Copyright (c) ???? Jochen Schäuble + * Copyright (c) 2003-2004 Jörn Engel * * Usage: * @@ -15,11 +10,12 @@ * phram=,, * may be up to 63 characters. * and can be octal, decimal or hexadecimal. If followed - * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or + * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or * gigabytes. * + * Example: + * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi */ - #include #include #include @@ -31,17 +27,16 @@ #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) struct phram_mtd_list { + struct mtd_info mtd; struct list_head list; - struct mtd_info *mtdinfo; }; static LIST_HEAD(phram_list); - -int phram_erase(struct mtd_info *mtd, struct erase_info *instr) +static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (instr->addr + instr->len > mtd->size) return -EINVAL; @@ -60,10 +55,10 @@ return 0; } -int phram_point(struct mtd_info *mtd, loff_t from, size_t len, +static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -73,17 +68,21 @@ return 0; } -void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len) { } -int phram_read(struct mtd_info *mtd, loff_t from, size_t len, +static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; - if (from + len > mtd->size) + if (from >= mtd->size) return -EINVAL; + + if (len > mtd->size - from) + len = mtd->size - from; memcpy(buf, start + from, len); @@ -91,13 +90,16 @@ return 0; } -int phram_write(struct mtd_info *mtd, loff_t to, size_t len, +static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; - if (to + len > mtd->size) + if (to >= mtd->size) return -EINVAL; + + if (len > mtd->size - to) + len = mtd->size - to; memcpy(start + to, buf, len); @@ -109,12 +111,11 @@ static void unregister_devices(void) { - struct phram_mtd_list *this; + struct phram_mtd_list *this, *safe; - list_for_each_entry(this, &phram_list, list) { - del_mtd_device(this->mtdinfo); - iounmap(this->mtdinfo->priv); - kfree(this->mtdinfo); + list_for_each_entry_safe(this, safe, &phram_list, list) { + del_mtd_device(&this->mtd); + iounmap(this->mtd.priv); kfree(this); } } @@ -128,45 +129,39 @@ if (!new) goto out0; - new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!new->mtdinfo) - goto out1; - - memset(new->mtdinfo, 0, sizeof(struct mtd_info)); + memset(new, 0, sizeof(*new)); ret = -EIO; - new->mtdinfo->priv = ioremap(start, len); - if (!new->mtdinfo->priv) { + new->mtd.priv = ioremap(start, len); + if (!new->mtd.priv) { ERROR("ioremap failed\n"); - goto out2; + goto out1; } - new->mtdinfo->name = name; - new->mtdinfo->size = len; - new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; - new->mtdinfo->erase = phram_erase; - new->mtdinfo->point = phram_point; - new->mtdinfo->unpoint = phram_unpoint; - new->mtdinfo->read = phram_read; - new->mtdinfo->write = phram_write; - new->mtdinfo->owner = THIS_MODULE; - new->mtdinfo->type = MTD_RAM; - new->mtdinfo->erasesize = 0x0; + new->mtd.name = name; + new->mtd.size = len; + new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; + new->mtd.erase = phram_erase; + new->mtd.point = phram_point; + new->mtd.unpoint = phram_unpoint; + new->mtd.read = phram_read; + new->mtd.write = phram_write; + new->mtd.owner = THIS_MODULE; + new->mtd.type = MTD_RAM; + new->mtd.erasesize = PAGE_SIZE; ret = -EAGAIN; - if (add_mtd_device(new->mtdinfo)) { + if (add_mtd_device(&new->mtd)) { ERROR("Failed to register new device\n"); - goto out3; + goto out2; } list_add_tail(&new->list, &phram_list); return 0; -out3: - iounmap(new->mtdinfo->priv); out2: - kfree(new->mtdinfo); + iounmap(new->mtd.priv); out1: kfree(new); out0: @@ -184,7 +179,9 @@ result *= 1024; case 'k': result *= 1024; - endp++; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; } return result; } @@ -221,6 +218,15 @@ return 0; } + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + #define parse_err(fmt, args...) do { \ ERROR(fmt , ## args); \ return 0; \ @@ -235,10 +241,11 @@ uint32_t len; int i, ret; - if (strnlen(val, sizeof(str)) >= sizeof(str)) + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) parse_err("parameter too long\n"); strcpy(str, val); + kill_final_newline(str); for (i=0; i<3; i++) token[i] = strsep(&str, ","); @@ -271,78 +278,11 @@ } module_param_call(phram, phram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(phram, "Memory region to map. \"map=,\""); - -/* - * Just for compatibility with slram, this is horrible and should go someday. - */ -static int __init slram_setup(const char *val, struct kernel_param *kp) -{ - char buf[256], *str = buf; - - if (!val || !val[0]) - parse_err("no arguments to \"slram=\"\n"); - - if (strnlen(val, sizeof(str)) >= sizeof(str)) - parse_err("parameter too long\n"); - - strcpy(str, val); - - while (str) { - char *token[3]; - char *name; - uint32_t start; - uint32_t len; - int i, ret; - - for (i=0; i<3; i++) { - token[i] = strsep(&str, ","); - if (token[i]) - continue; - parse_err("wrong number of arguments to \"slram=\"\n"); - } - - /* name */ - ret = parse_name(&name, token[0]); - if (ret == -ENOMEM) - parse_err("of memory\n"); - if (ret == -ENOSPC) - parse_err("too long\n"); - if (ret) - return 1; - - /* start */ - ret = parse_num32(&start, token[1]); - if (ret) - parse_err("illegal start address\n"); - - /* len */ - if (token[2][0] == '+') - ret = parse_num32(&len, token[2] + 1); - else - ret = parse_num32(&len, token[2]); - - if (ret) - parse_err("illegal device length\n"); - - if (token[2][0] != '+') { - if (len < start) - parse_err("end < start\n"); - len -= start; - } - - register_device(name, start, len); - } - return 1; -} - -module_param_call(slram, slram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=,\""); +MODULE_PARM_DESC(phram,"Memory region to map. \"map=,,\""); -int __init init_phram(void) +static int __init init_phram(void) { - printk(KERN_ERR "phram loaded\n"); return 0; } diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/pmc551.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/pmc551.c --- linux-2.6.9-intc1/drivers/mtd/devices/pmc551.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/pmc551.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.28 2004/08/09 13:19:44 dwmw2 Exp $ + * $Id: pmc551.c,v 1.31 2005/03/17 19:44:27 gleixner Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -113,7 +113,7 @@ static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -176,7 +176,7 @@ static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi; u32 soff_lo; @@ -217,7 +217,7 @@ static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -279,7 +279,7 @@ static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -630,10 +630,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Ferrell "); MODULE_DESCRIPTION(PMC551_VERSION); -MODULE_PARM(msize, "i"); -MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]"); -MODULE_PARM(asize, "i"); -MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); /* * Stuff these outside the ifdef so as to not bust compiled in driver support @@ -645,10 +641,15 @@ static int asize=0; #endif +module_param(msize, int, 0); +MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]"); +module_param(asize, int, 0); +MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); + /* * PMC551 Card Initialization */ -int __init init_pmc551(void) +static int __init init_pmc551(void) { struct pci_dev *PCI_Device = NULL; struct mypriv *priv; @@ -820,7 +821,7 @@ struct mypriv *priv; while((mtd=pmc551list)) { - priv = (struct mypriv *)mtd->priv; + priv = mtd->priv; pmc551list = priv->nextpmc551; if(priv->start) { diff -Nur linux-2.6.9-intc1/drivers/mtd/devices/slram.c linux-2.6.9-intc1-mih1/drivers/mtd/devices/slram.c --- linux-2.6.9-intc1/drivers/mtd/devices/slram.c 2004-10-18 17:53:05.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/devices/slram.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,6 +1,6 @@ /*====================================================================== - $Id: slram.c,v 1.31 2004/08/09 13:19:44 dwmw2 Exp $ + $Id: slram.c,v 1.35 2005/03/17 19:44:27 gleixner Exp $ This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually @@ -50,6 +50,7 @@ #include #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ +#define SLRAM_BLK_SZ 0x4000 #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) @@ -66,22 +67,22 @@ #ifdef MODULE static char *map[SLRAM_MAX_DEVICES_PARAMS]; + +module_param_array(map, charp, NULL, 0); +MODULE_PARM_DESC(map, "List of memory regions to map. \"map=, , \""); #else static char *map; #endif -MODULE_PARM(map, "3-" __MODULE_STRING(SLRAM_MAX_DEVICES_PARAMS) "s"); -MODULE_PARM_DESC(map, "List of memory regions to map. \"map=, , \""); - static slram_mtd_list_t *slram_mtdlist = NULL; -int slram_erase(struct mtd_info *, struct erase_info *); -int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); -void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); -int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); -int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int slram_erase(struct mtd_info *, struct erase_info *); +static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); +static void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); +static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); -int slram_erase(struct mtd_info *mtd, struct erase_info *instr) +static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) { slram_priv_t *priv = mtd->priv; @@ -103,35 +104,47 @@ return(0); } -int slram_point(struct mtd_info *mtd, loff_t from, size_t len, +static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; + + if (from + len > mtd->size) + return -EINVAL; *mtdbuf = priv->start + from; *retlen = len; return(0); } -void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) { } -int slram_read(struct mtd_info *mtd, loff_t from, size_t len, +static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; - + slram_priv_t *priv = mtd->priv; + + if (from > mtd->size) + return -EINVAL; + + if (from + len > mtd->size) + len = mtd->size - from; + memcpy(buf, priv->start + from, len); *retlen = len; return(0); } -int slram_write(struct mtd_info *mtd, loff_t to, size_t len, +static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; + + if (to + len > mtd->size) + return -EINVAL; memcpy(priv->start + to, buf, len); @@ -141,7 +154,7 @@ /*====================================================================*/ -int register_device(char *name, unsigned long start, unsigned long length) +static int register_device(char *name, unsigned long start, unsigned long length) { slram_mtd_list_t **curmtd; @@ -161,7 +174,7 @@ if ((*curmtd)->mtdinfo) { memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); (*curmtd)->mtdinfo->priv = - (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + kmalloc(sizeof(slram_priv_t), GFP_KERNEL); if (!(*curmtd)->mtdinfo->priv) { kfree((*curmtd)->mtdinfo); @@ -188,7 +201,7 @@ (*curmtd)->mtdinfo->name = name; (*curmtd)->mtdinfo->size = length; (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | - MTD_WRITEB_WRITEABLE | MTD_VOLATILE; + MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM; (*curmtd)->mtdinfo->erase = slram_erase; (*curmtd)->mtdinfo->point = slram_point; (*curmtd)->mtdinfo->unpoint = slram_unpoint; @@ -196,7 +209,7 @@ (*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->owner = THIS_MODULE; (*curmtd)->mtdinfo->type = MTD_RAM; - (*curmtd)->mtdinfo->erasesize = 0x0; + (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; if (add_mtd_device((*curmtd)->mtdinfo)) { E("slram: Failed to register new device\n"); @@ -213,7 +226,7 @@ return(0); } -void unregister_devices(void) +static void unregister_devices(void) { slram_mtd_list_t *nextitem; @@ -228,7 +241,7 @@ } } -unsigned long handle_unit(unsigned long value, char *unit) +static unsigned long handle_unit(unsigned long value, char *unit) { if ((*unit == 'M') || (*unit == 'm')) { return(value * 1024 * 1024); @@ -238,7 +251,7 @@ return(value); } -int parse_cmdline(char *devname, char *szstart, char *szlength) +static int parse_cmdline(char *devname, char *szstart, char *szlength) { char *buffer; unsigned long devstart; @@ -261,7 +274,7 @@ } T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", devname, devstart, devlength); - if ((devstart < 0) || (devlength < 0)) { + if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { E("slram: Illegal start / length parameter.\n"); return(-EINVAL); } @@ -285,7 +298,7 @@ #endif -int init_slram(void) +static int init_slram(void) { char *devname; int i; diff -Nur linux-2.6.9-intc1/drivers/mtd/ftl.c linux-2.6.9-intc1-mih1/drivers/mtd/ftl.c --- linux-2.6.9-intc1/drivers/mtd/ftl.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/ftl.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.53 2004/08/09 13:55:43 dwmw2 Exp $ + * $Id: ftl.c,v 1.56 2005/03/17 19:54:36 gleixner Exp $ * * Fixes: Arnaldo Carvalho de Melo * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -80,7 +80,7 @@ /* Parameters that can be set with 'insmod' */ static int shuffle_freq = 50; -MODULE_PARM(shuffle_freq, "i"); +module_param(shuffle_freq, int, 0); /*====================================================================*/ @@ -357,6 +357,7 @@ if (!erase) return -ENOMEM; + erase->mtd = part->mbd.mtd; erase->callback = ftl_erase_callback; erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; @@ -1067,16 +1068,18 @@ partition->mbd.blksize = SECTOR_SIZE; partition->mbd.tr = tr; partition->mbd.devnum = -1; - if (add_mtd_blktrans_dev((void *)partition)) - kfree(partition); - - } else - kfree(partition); + if (!add_mtd_blktrans_dev((void *)partition)) + return; + } + + ftl_freepart(partition); + kfree(partition); } static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); + ftl_freepart((partition_t *)dev); kfree(dev); } @@ -1094,7 +1097,7 @@ int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.53 2004/08/09 13:55:43 dwmw2 Exp $\n"); + DEBUG(0, "$Id: ftl.c,v 1.56 2005/03/17 19:54:36 gleixner Exp $\n"); return register_mtd_blktrans(&ftl_tr); } diff -Nur linux-2.6.9-intc1/drivers/mtd/inftlcore.c linux-2.6.9-intc1-mih1/drivers/mtd/inftlcore.c --- linux-2.6.9-intc1/drivers/mtd/inftlcore.c 2004-10-18 17:53:13.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/inftlcore.c 2005-03-21 21:28:41.000000000 -0500 @@ -7,7 +7,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * Author: David Woodhouse * - * $Id: inftlcore.c,v 1.17 2004/08/09 13:56:48 dwmw2 Exp $ + * $Id: inftlcore.c,v 1.18 2004/11/16 18:28:59 dwmw2 Exp $ * * 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 @@ -352,7 +352,7 @@ return targetEUN; } -u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock) +static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock) { /* * This is the part that needs some cleverness applied. @@ -877,7 +877,7 @@ return 0; } -struct mtd_blktrans_ops inftl_tr = { +static struct mtd_blktrans_ops inftl_tr = { .name = "inftl", .major = INFTL_MAJOR, .part_bits = INFTL_PARTN_BITS, @@ -891,9 +891,9 @@ extern char inftlmountrev[]; -int __init init_inftl(void) +static int __init init_inftl(void) { - printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.17 $, " + printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.18 $, " "inftlmount.c %s\n", inftlmountrev); return register_mtd_blktrans(&inftl_tr); diff -Nur linux-2.6.9-intc1/drivers/mtd/inftlmount.c linux-2.6.9-intc1-mih1/drivers/mtd/inftlmount.c --- linux-2.6.9-intc1/drivers/mtd/inftlmount.c 2004-10-18 17:53:10.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/inftlmount.c 2005-03-21 21:28:41.000000000 -0500 @@ -8,7 +8,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.14 2004/08/09 13:57:42 dwmw2 Exp $ + * $Id: inftlmount.c,v 1.16 2004/11/22 13:50:53 kalev Exp $ * * 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 @@ -41,7 +41,7 @@ #include #include -char inftlmountrev[]="$Revision: 1.14 $"; +char inftlmountrev[]="$Revision: 1.16 $"; /* * find_boot_record: Find the INFTL Media Header and its Spare copy which @@ -222,6 +222,8 @@ if (ip->Reserved0 != ip->firstUnit) { struct erase_info *instr = &inftl->instr; + instr->mtd = inftl->mbd.mtd; + /* * Most likely this is using the * undocumented qiuck mount feature. @@ -396,6 +398,7 @@ _first_? */ /* Use async erase interface, test return code */ + instr->mtd = inftl->mbd.mtd; instr->addr = block * inftl->EraseSize; instr->len = inftl->mbd.mtd->erasesize; /* Erase one physical eraseblock at a time, even though the NAND api diff -Nur linux-2.6.9-intc1/drivers/mtd/Kconfig linux-2.6.9-intc1-mih1/drivers/mtd/Kconfig --- linux-2.6.9-intc1/drivers/mtd/Kconfig 2004-10-18 17:53:41.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/Kconfig 2005-03-21 21:28:41.000000000 -0500 @@ -1,4 +1,4 @@ -# $Id: Kconfig,v 1.6 2004/08/09 13:19:42 dwmw2 Exp $ +# $Id: Kconfig,v 1.8 2005/03/17 19:53:57 gleixner Exp $ menu "Memory Technology Devices (MTD)" @@ -27,6 +27,15 @@ help Determines the verbosity level of the MTD debugging messages. +config MTD_CONCAT + tristate "MTD concatenating support" + depends on MTD + help + Support for concatenating several MTD devices into a single + (virtual) one. This allows you to have -for example- a JFFS(2) + file system spanning multiple physical flash chips. If unsure, + say 'Y'. + config MTD_PARTITIONS bool "MTD partitioning support" depends on MTD @@ -40,22 +49,13 @@ devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. -config MTD_CONCAT - tristate "MTD concatenating support" - depends on MTD - help - Support for concatenating several MTD devices into a single - (virtual) one. This allows you to have -for example- a JFFS(2) - file system spanning multiple physical flash chips. If unsure, - say 'Y'. - config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" depends on MTD_PARTITIONS ---help--- RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table in the last erase - block of the device, similar to a partition table, which gives + 'images' in flash devices by putting a table one of the erase + blocks on the device, similar to a partition table, which gives the offsets, lengths and names of all the images stored in the flash. @@ -68,6 +68,23 @@ SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. +config MTD_REDBOOT_DIRECTORY_BLOCK + int "Location of RedBoot partition table" + depends on MTD_REDBOOT_PARTS + default "-1" + ---help--- + This option is the Linux counterpart to the + CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time + option. + + The option specifies which Flash sectors holds the RedBoot + partition table. A zero or positive value gives an absolete + erase block number. A negative value specifies a number of + sectors before the end of the device. + + For example "2" means block number 2, "-1" means the last + block and "-2" means the penultimate block. + config MTD_REDBOOT_PARTS_UNALLOCATED bool " Include unallocated flash regions" depends on MTD_REDBOOT_PARTS diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/alchemy-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/alchemy-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/alchemy-flash.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/alchemy-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,192 @@ +/* + * Flash memory access on AMD Alchemy evaluation boards + * + * $Id: alchemy-flash.c,v 1.1 2005/02/27 21:50:21 ppopov Exp $ + * + * (C) 2003, 2004 Pete Popov + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef DEBUG_RW +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef CONFIG_MIPS_PB1000 +#define BOARD_MAP_NAME "Pb1000 Flash" +#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1500 +#define BOARD_MAP_NAME "Pb1500 Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1100 +#define BOARD_MAP_NAME "Pb1100 Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1550 +#define BOARD_MAP_NAME "Pb1550 Flash" +#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1200 +#define BOARD_MAP_NAME "Pb1200 Flash" +#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ +#define BOARD_FLASH_WIDTH 2 /* 16-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1000 +#define BOARD_MAP_NAME "Db1000 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1500 +#define BOARD_MAP_NAME "Db1500 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1100 +#define BOARD_MAP_NAME "Db1100 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1550 +#define BOARD_MAP_NAME "Db1550 Flash" +#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1200 +#define BOARD_MAP_NAME "Db1200 Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 2 /* 16-bits */ +#endif + +#ifdef CONFIG_MIPS_HYDROGEN3 +#define BOARD_MAP_NAME "Hydrogen3 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#define USE_LOCAL_ACCESSORS /* why? */ +#endif + +#ifdef CONFIG_MIPS_BOSPORUS +#define BOARD_MAP_NAME "Bosporus Flash" +#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ +#define BOARD_FLASH_WIDTH 2 /* 16-bits */ +#endif + +#ifdef CONFIG_MIPS_MIRAGE +#define BOARD_MAP_NAME "Mirage Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#define USE_LOCAL_ACCESSORS /* why? */ +#endif + +static struct map_info alchemy_map = { + .name = BOARD_MAP_NAME, +}; + +static struct mtd_partition alchemy_partitions[] = { + { + .name = "User FS", + .size = BOARD_FLASH_SIZE - 0x00400000, + .offset = 0x0000000 + },{ + .name = "YAMON", + .size = 0x0100000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE + },{ + .name = "raw kernel", + .size = (0x300000 - 0x40000), /* last 256KB is yamon env */ + .offset = MTDPART_OFS_APPEND, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +static struct mtd_info *mymtd; + +int __init alchemy_mtd_init(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + unsigned long window_addr; + unsigned long window_size; + + /* Default flash buswidth */ + alchemy_map.bankwidth = BOARD_FLASH_WIDTH; + + window_addr = 0x20000000 - BOARD_FLASH_SIZE; + window_size = BOARD_FLASH_SIZE; +#ifdef CONFIG_MIPS_MIRAGE_WHY + /* Boot ROM flash bank only; no user bank */ + window_addr = 0x1C000000; + window_size = 0x04000000; + /* USERFS from 0x1C00 0000 to 0x1FC00000 */ + alchemy_partitions[0].size = 0x03C00000; +#endif + + /* + * Static partition definition selection + */ + parts = alchemy_partitions; + nb_parts = NB_OF(alchemy_partitions); + alchemy_map.size = window_size; + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n", + alchemy_map.bankwidth*8); + alchemy_map.virt = ioremap(window_addr, window_size); + mymtd = do_map_probe("cfi_probe", &alchemy_map); + if (!mymtd) { + iounmap(alchemy_map.virt); + return -ENXIO; + } + mymtd->owner = THIS_MODULE; + + add_mtd_partitions(mymtd, parts, nb_parts); + return 0; +} + +static void __exit alchemy_mtd_cleanup(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + iounmap(alchemy_map.virt); + } +} + +module_init(alchemy_mtd_init); +module_exit(alchemy_mtd_cleanup); + +MODULE_AUTHOR("Embedded Alley Solutions, Inc"); +MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/amd76xrom.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/amd76xrom.c --- linux-2.6.9-intc1/drivers/mtd/maps/amd76xrom.c 2004-10-18 17:53:44.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/amd76xrom.c 2005-03-21 21:28:42.000000000 -0500 @@ -2,7 +2,7 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.12 2004/07/14 14:44:31 thayne Exp $ + * $Id: amd76xrom.c,v 1.20 2005/03/18 14:04:35 gleixner Exp $ */ #include @@ -12,61 +12,73 @@ #include #include #include +#include +#include #include #include #include +#include #define xstr(s) str(s) #define str(s) #s #define MOD_NAME xstr(KBUILD_BASENAME) -#define MTD_DEV_NAME_LENGTH 16 +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ + +struct amd76xrom_window { + void __iomem *virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; struct amd76xrom_map_info { + struct list_head list; struct map_info map; struct mtd_info *mtd; - void __iomem * window_addr; - u32 window_start, window_size; - struct pci_dev *pdev; - struct resource window_rsrc; - struct resource rom_rsrc; - char mtd_name[MTD_DEV_NAME_LENGTH]; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; - -static struct amd76xrom_map_info amd76xrom_map = { - .map = { - .name = MOD_NAME, - .size = 0, - .bankwidth = 1, - } - /* remaining fields of structure are initialized to 0 */ +static struct amd76xrom_window amd76xrom_window = { + .maps = LIST_HEAD_INIT(amd76xrom_window.maps), }; - -static void amd76xrom_cleanup(struct amd76xrom_map_info *info) +static void amd76xrom_cleanup(struct amd76xrom_window *window) { + struct amd76xrom_map_info *map, *scratch; u8 byte; - /* Disable writes through the rom window */ - pci_read_config_byte(info->pdev, 0x40, &byte); - pci_write_config_byte(info->pdev, 0x40, byte & ~1); - - if (info->mtd) { - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.virt = NULL; - } - if (info->rom_rsrc.parent) - release_resource(&info->rom_rsrc); - if (info->window_rsrc.parent) - release_resource(&info->window_rsrc); - - if (info->window_addr) { - iounmap(info->window_addr); - info->window_addr = NULL; + if (window->pdev) { + /* Disable writes through the rom window */ + pci_read_config_byte(window->pdev, 0x40, &byte); + pci_write_config_byte(window->pdev, 0x40, byte & ~1); + } + + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) { + release_resource(&map->rsrc); + } + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; } } @@ -74,167 +86,196 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { - struct rom_window { - u32 start; - u32 size; - u8 segen_bits; - }; - static struct rom_window rom_window[] = { - /* - * Need the 5MiB window for chips that have block lock/unlock - * registers located below 4MiB window. - */ - { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, - { 0xffc00000, 4*1024*1024, (1<<7), }, - { 0xffff0000, 64*1024, 0 }, - { 0 , 0, 0 }, - }; - static const u32 rom_probe_sizes[] = { - 5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024, - 256*1024, 128*1024, 64*1024, 0}; static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; - struct amd76xrom_map_info *info = &amd76xrom_map; - struct rom_window *window; - int i; - u32 rom_size; - - info->pdev = pdev; - window = &rom_window[0]; - - while (window->size) { - /* - * Try to reserve the window mem region. If this fails then - * it is likely due to a fragment of the window being - * "reseved" by the BIOS. In the case that the - * request_mem_region() fails then once the rom size is - * discovered we will try to reserve the unreserved fragment. - */ - info->window_rsrc.name = MOD_NAME; - info->window_rsrc.start = window->start; - info->window_rsrc.end = window->start + window->size - 1; - info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&iomem_resource, &info->window_rsrc)) { - info->window_rsrc.parent = NULL; - printk(KERN_ERR MOD_NAME - " %s(): Unable to register resource" - " 0x%.08lx-0x%.08lx - kernel bug?\n", - __func__, - info->window_rsrc.start, info->window_rsrc.end); - } - - /* Enable the selected rom window */ - pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); - - /* Enable writes through the rom window */ - pci_read_config_byte(pdev, 0x40, &byte); - pci_write_config_byte(pdev, 0x40, byte | 1); - - /* FIXME handle registers 0x80 - 0x8C the bios region locks */ - - printk(KERN_NOTICE MOD_NAME " window : %x at %x\n", - window->size, window->start); - /* For write accesses caches are useless */ - info->window_addr = ioremap_nocache(window->start, - window->size); - - if (!info->window_addr) { - printk(KERN_ERR "Failed to ioremap\n"); - continue; - } + struct amd76xrom_window *window = &amd76xrom_window; + struct amd76xrom_map_info *map = NULL; + unsigned long map_top; + + /* Remember the pci dev I find the window in */ + window->pdev = pdev; + + /* Assume the rom window is properly setup, and find it's size */ + pci_read_config_byte(pdev, 0x43, &byte); + if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { + window->phys = 0xffb00000; /* 5MiB */ + } + else if ((byte & (1<<7)) == (1<<7)) { + window->phys = 0xffc00000; /* 4MiB */ + } + else { + window->phys = 0xffff0000; /* 64KiB */ + } + window->size = 0xffffffffUL - window->phys + 1UL; + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to a fragment of the window being + * "reseved" by the BIOS. In the case that the + * request_mem_region() fails then once the rom size is + * discovered we will try to reserve the unreserved fragment. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_ERR MOD_NAME + " %s(): Unable to register resource" + " 0x%.08lx-0x%.08lx - kernel bug?\n", + __func__, + window->rsrc.start, window->rsrc.end); + } - info->mtd = NULL; +#if 0 - for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { - char **chip_type; - if (rom_size > window->size) { - continue; - } - info->map.phys = window->start + window->size - rom_size; - info->map.virt = - info->window_addr + window->size - rom_size; - info->map.size = rom_size; - simple_map_init(&info->map); - chip_type = rom_probe_types; - for(; !info->mtd && *chip_type; chip_type++) { - info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); - } - if (info->mtd) goto found_mtd; - } - iounmap(info->window_addr); - info->window_addr = NULL; + /* Enable the selected rom window */ + pci_read_config_byte(pdev, 0x43, &byte); + pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); +#endif - /* Disable writes through the rom window */ - pci_read_config_byte(pdev, 0x40, &byte); - pci_write_config_byte(pdev, 0x40, byte & ~1); + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, 0x40, &byte); + pci_write_config_byte(pdev, 0x40, byte | 1); + + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + + /* For write accesses caches are useless */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } - window++; + /* Get the first address to look for an rom chip at */ + map_top = window->phys; +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * Probe at most the last 4M of the address space. + */ + if (map_top < 0xffc00000) { + map_top = 0xffc00000; } - goto failed; +#endif + /* Loop through and look for rom chips */ + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; - found_mtd: - printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n", - window->size - rom_size); - - info->mtd->owner = THIS_MODULE; - - if (!info->window_rsrc.parent) { - /* failed to reserve entire window - try fragments */ - info->window_rsrc.name = MOD_NAME; - info->window_rsrc.start = window->start; - info->window_rsrc.end = window->start + window->size - rom_size - 1; - info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&iomem_resource, &info->window_rsrc)) { - printk(KERN_ERR MOD_NAME - ": cannot reserve window resource fragment\n"); -#if 0 + if (!map) { + map = kmalloc(sizeof(*map), GFP_KERNEL); + } + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* There is no generic VPP support */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { /* - * The BIOS e820 usually reserves this so it isn't - * usually an error. + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. */ - goto failed; -#endif + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } } - } - add_mtd_device(info->mtd); - info->window_start = window->start; - info->window_size = window->size; - - if (info->window_rsrc.parent) { - /* - * Registering the MTD device in iomem may not be possible - * if there is a BIOS "reserved" and BUSY range. If this - * fails then continue anyway. - */ - snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, - "mtd%d", info->mtd->index); - - info->rom_rsrc.name = info->mtd_name; - info->rom_rsrc.start = window->start + window->size - rom_size; - info->rom_rsrc.end = window->start + window->size - 1; - info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { - printk(KERN_ERR MOD_NAME - ": cannot reserve MTD resource\n"); - info->rom_rsrc.parent = NULL; + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) { + cfi->chips[i].start += offset; + } + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; } + + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; } + out: + /* Free any left over map structures */ + if (map) { + kfree(map); + } + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + amd76xrom_cleanup(window); + return -ENODEV; + } return 0; - - failed: - amd76xrom_cleanup(info); - return -ENODEV; } static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) { - struct amd76xrom_map_info *info = &amd76xrom_map; + struct amd76xrom_window *window = &amd76xrom_window; - amd76xrom_cleanup(info); + amd76xrom_cleanup(window); } static struct pci_device_id amd76xrom_pci_tbl[] = { @@ -257,7 +298,7 @@ }; #endif -int __init init_amd76xrom(void) +static int __init init_amd76xrom(void) { struct pci_dev *pdev; struct pci_device_id *id; @@ -269,18 +310,17 @@ } } if (pdev) { - amd76xrom_map.pdev = pdev; return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]); } return -ENXIO; #if 0 - return pci_module_init(&amd76xrom_driver); + return pci_register_driver(&amd76xrom_driver); #endif } static void __exit cleanup_amd76xrom(void) { - amd76xrom_remove_one(amd76xrom_map.pdev); + amd76xrom_remove_one(amd76xrom_window.pdev); } module_init(init_amd76xrom); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/arctic-mtd.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/arctic-mtd.c --- linux-2.6.9-intc1/drivers/mtd/maps/arctic-mtd.c 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/arctic-mtd.c 2005-03-21 21:28:43.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: arctic-mtd.c,v 1.11 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: arctic-mtd.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for * IBM 405LP Arctic boards. @@ -98,7 +98,7 @@ { printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); - arctic_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); + arctic_mtd_map.virt = ioremap(PADDR, SIZE); if (!arctic_mtd_map.virt) { printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/autcpu12-nvram.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/autcpu12-nvram.c --- linux-2.6.9-intc1/drivers/mtd/maps/autcpu12-nvram.c 2004-10-18 17:55:24.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/autcpu12-nvram.c 2005-03-21 21:28:42.000000000 -0500 @@ -2,7 +2,7 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.6 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: autcpu12-nvram.c,v 1.8 2004/11/04 13:24:14 gleixner Exp $ * * 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 @@ -47,7 +47,7 @@ { int err, save0, save1; - autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K); + autcpu12_sram_map.virt = ioremap(0x12000000, SZ_128K); if (!autcpu12_sram_map.virt) { printk("Failed to ioremap autcpu12 NV-RAM space\n"); err = -EIO; @@ -76,7 +76,7 @@ /* We have a 128K found, restore 0x10000 and set size * to 128K */ - ma[_write32(&autcpu12_sram_map,save1,0x10000); + map_write32(&autcpu12_sram_map,save1,0x10000); autcpu12_sram_map.size = SZ_128K; map: diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/bast-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/bast-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/bast-flash.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/bast-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,230 @@ +/* linux/drivers/mtd/maps/bast_flash.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks + * + * Simtec Bast (EB2410ITX) NOR MTD Mapping driver + * + * Changelog: + * 20-Sep-2004 BJD Initial version + * 17-Jan-2005 BJD Add whole device if no partitions found + * + * $Id: bast-flash.c,v 1.2 2005/01/18 11:13:47 bjd Exp $ + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MTD_BAST_MAXSIZE +#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M) +#else +#define AREA_MAXSIZE (32 * SZ_1M) +#endif + +#define PFX "bast-flash: " + +struct bast_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *area; +}; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static struct bast_flash_info *to_bast_info(struct device *dev) +{ + return (struct bast_flash_info *)dev_get_drvdata(dev); +} + +static void bast_flash_setrw(int to) +{ + unsigned int val; + unsigned long flags; + + local_irq_save(flags); + val = __raw_readb(BAST_VA_CTRL3); + + if (to) + val |= BAST_CPLD_CTRL3_ROMWEN; + else + val &= ~BAST_CPLD_CTRL3_ROMWEN; + + pr_debug("new cpld ctrl3=%02x\n", val); + + __raw_writeb(val, BAST_VA_CTRL3); + local_irq_restore(flags); +} + +static int bast_flash_remove(struct device *dev) +{ + struct bast_flash_info *info = to_bast_info(dev); + + dev_set_drvdata(dev, NULL); + + if (info == NULL) + return 0; + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + + if (info->partitions) + kfree(info->partitions); + + if (info->area) { + release_resource(info->area); + kfree(info->area); + } + + kfree(info); + + return 0; +} + +static int bast_flash_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bast_flash_info *info; + struct resource *res; + int err = 0; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + printk(KERN_ERR PFX "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info, sizeof(*info)); + dev_set_drvdata(dev, info); + + res = pdev->resource; /* assume that the flash has one resource */ + + info->map.phys = res->start; + info->map.size = res->end - res->start + 1; + info->map.name = dev->bus_id; + info->map.bankwidth = 2; + + if (info->map.size > AREA_MAXSIZE) + info->map.size = AREA_MAXSIZE; + + pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__, + info->map.phys, info->map.size); + + info->area = request_mem_region(res->start, info->map.size, + pdev->name); + if (info->area == NULL) { + printk(KERN_ERR PFX "cannot reserve flash memory region\n"); + err = -ENOENT; + goto exit_error; + } + + info->map.virt = ioremap(res->start, info->map.size); + pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt); + + if (info->map.virt == 0) { + printk(KERN_ERR PFX "failed to ioremap() region\n"); + err = -EIO; + goto exit_error; + } + + simple_map_init(&info->map); + + /* enable the write to the flash area */ + + bast_flash_setrw(1); + + /* probe for the device(s) */ + + info->mtd = do_map_probe("jedec_probe", &info->map); + if (info->mtd == NULL) + info->mtd = do_map_probe("cfi_probe", &info->map); + + if (info->mtd == NULL) { + printk(KERN_ERR PFX "map_probe() failed\n"); + err = -ENXIO; + goto exit_error; + } + + /* mark ourselves as the owner */ + info->mtd->owner = THIS_MODULE; + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if (err) + printk(KERN_ERR PFX "cannot add/parse partitions\n"); + } else { + err = add_mtd_device(info->mtd); + } + + if (err == 0) + return 0; + + /* fall through to exit error */ + + exit_error: + bast_flash_remove(dev); + return err; +} + +static struct device_driver bast_flash_driver = { + .name = "bast-nor", + .bus = &platform_bus_type, + .probe = bast_flash_probe, + .remove = bast_flash_remove, +}; + +static int __init bast_flash_init(void) +{ + printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n"); + return driver_register(&bast_flash_driver); +} + +static void __exit bast_flash_exit(void) +{ + driver_unregister(&bast_flash_driver); +} + +module_init(bast_flash_init); +module_exit(bast_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("BAST MTD Map driver"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/beech-mtd.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/beech-mtd.c --- linux-2.6.9-intc1/drivers/mtd/maps/beech-mtd.c 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/beech-mtd.c 2005-03-21 21:28:43.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: beech-mtd.c,v 1.8 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: beech-mtd.c,v 1.10 2004/11/04 13:24:14 gleixner Exp $ * * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for * IBM 405LP Beech boards. @@ -74,7 +74,7 @@ { printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); - beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); + beech_mtd_map.virt = ioremap(PADDR, SIZE); if (!beech_mtd_map.virt) { printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/cdb89712.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/cdb89712.c --- linux-2.6.9-intc1/drivers/mtd/maps/cdb89712.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/cdb89712.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.8 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: cdb89712.c,v 1.10 2004/11/04 13:24:14 gleixner Exp $ */ #include @@ -44,7 +44,7 @@ goto out; } - cdb89712_flash_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); + cdb89712_flash_map.virt = ioremap(FLASH_START, FLASH_SIZE); if (!cdb89712_flash_map.virt) { printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); err = -EIO; @@ -114,7 +114,7 @@ goto out; } - cdb89712_sram_map.virt = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); + cdb89712_sram_map.virt = ioremap(SRAM_START, SRAM_SIZE); if (!cdb89712_sram_map.virt) { printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); err = -EIO; @@ -182,7 +182,7 @@ goto out; } - cdb89712_bootrom_map.virt = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); + cdb89712_bootrom_map.virt = ioremap(BOOTROM_START, BOOTROM_SIZE); if (!cdb89712_bootrom_map.virt) { printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); err = -EIO; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ceiva.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ceiva.c --- linux-2.6.9-intc1/drivers/mtd/maps/ceiva.c 2004-10-18 17:53:50.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ceiva.c 2005-03-21 21:28:42.000000000 -0500 @@ -11,7 +11,7 @@ * * (C) 2000 Nicolas Pitre * - * $Id: ceiva.c,v 1.10 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $ */ #include @@ -103,7 +103,7 @@ unsigned long base; unsigned long size; int width; - void __iomem *vbase; + void *vbase; struct map_info *map; struct mtd_info *mtd; struct resource *res; @@ -150,7 +150,7 @@ break; } - clps[i].map->virt = clps[i].vbase; + clps[i].map->virt = (void __iomem *)clps[i].vbase; clps[i].map->bankwidth = clps[i].width; clps[i].map->size = clps[i].size; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/cfi_flagadm.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/cfi_flagadm.c --- linux-2.6.9-intc1/drivers/mtd/maps/cfi_flagadm.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/cfi_flagadm.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,7 +1,7 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson * - * $Id: cfi_flagadm.c,v 1.12 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: cfi_flagadm.c,v 1.14 2004/11/04 13:24:14 gleixner Exp $ * * 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 @@ -96,7 +96,7 @@ FLASH_SIZE, FLASH_PHYS_ADDR); flagadm_map.phys = FLASH_PHYS_ADDR; - flagadm_map.virt = (unsigned long)ioremap(FLASH_PHYS_ADDR, + flagadm_map.virt = ioremap(FLASH_PHYS_ADDR, FLASH_SIZE); if (!flagadm_map.virt) { diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/cstm_mips_ixx.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/cstm_mips_ixx.c --- linux-2.6.9-intc1/drivers/mtd/maps/cstm_mips_ixx.c 2004-10-18 17:53:22.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/cstm_mips_ixx.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: cstm_mips_ixx.c,v 1.10 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: cstm_mips_ixx.c,v 1.13 2005/01/12 22:34:35 gleixner Exp $ * * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. * Config with both CFI and JEDEC device support. @@ -58,7 +58,7 @@ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) { - static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(vpp_lock); static int vpp_count = 0; unsigned long flags; @@ -170,7 +170,7 @@ cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr; - cstm_mips_ixx_map[i].virt = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); + cstm_mips_ixx_map[i].virt = ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); if (!cstm_mips_ixx_map[i].virt) { printk(KERN_WARNING "Failed to ioremap\n"); return -EIO; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/dbox2-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/dbox2-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/dbox2-flash.c 2004-10-18 17:55:28.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/dbox2-flash.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: dbox2-flash.c,v 1.11 2004/07/12 21:59:43 dwmw2 Exp $ + * $Id: dbox2-flash.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * D-Box 2 flash driver */ @@ -75,7 +75,7 @@ int __init init_dbox2_flash(void) { printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); - dbox2_flash_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!dbox2_flash_map.virt) { printk("Failed to ioremap\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/dc21285.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/dc21285.c --- linux-2.6.9-intc1/drivers/mtd/maps/dc21285.c 2004-10-18 17:53:51.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/dc21285.c 2005-03-21 21:28:42.000000000 -0500 @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: dc21285.c,v 1.20 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: dc21285.c,v 1.22 2004/11/01 13:39:21 rmk Exp $ */ #include #include @@ -32,7 +32,8 @@ * is unpredictible. So we have a 25us penalty per * write access. */ -static void nw_en_write(void) { +static void nw_en_write(void) +{ extern spinlock_t gpio_lock; unsigned long flags; @@ -55,53 +56,60 @@ static map_word dc21285_read8(struct map_info *map, unsigned long ofs) { - return *(uint8_t*)(map->map_priv_1 + ofs); + map_word val; + val.x[0] = *(uint8_t*)(map->virt + ofs); + return val; } static map_word dc21285_read16(struct map_info *map, unsigned long ofs) { - return *(uint16_t*)(map->map_priv_1 + ofs); + map_word val; + val.x[0] = *(uint16_t*)(map->virt + ofs); + return val; } static map_word dc21285_read32(struct map_info *map, unsigned long ofs) { - return *(uint32_t*)(map->map_priv_1 + ofs); + map_word val; + val.x[0] = *(uint32_t*)(map->virt + ofs); + return val; } static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { - memcpy(to, (void*)(map->map_priv_1 + from), len); + memcpy(to, (void*)(map->virt + from), len); } -static void dc21285_write(struct map_info *map, map_word d, unsigned long adr) +static void dc21285_write8(struct map_info *map, const map_word d, unsigned long adr) { if (machine_is_netwinder()) nw_en_write(); *CSR_ROMWRITEREG = adr & 3; adr &= ~3; - *(uint8_t*)(map->map_priv_1 + adr) = d.x[0]; + *(uint8_t*)(map->virt + adr) = d.x[0]; } -static void dc21285_write16(struct map_info *map, map_word d, unsigned long adr) +static void dc21285_write16(struct map_info *map, const map_word d, unsigned long adr) { if (machine_is_netwinder()) nw_en_write(); *CSR_ROMWRITEREG = adr & 3; adr &= ~3; - *(uint16_t*)(map->map_priv_1 + adr) = d.x[0]; + *(uint16_t*)(map->virt + adr) = d.x[0]; } -static void dc21285_write32(struct map_info *map, map_word d, unsigned long adr) +static void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr) { if (machine_is_netwinder()) nw_en_write(); - *(uint32_t*)(map->map_priv_1 + adr) = d.x[0]; + *(uint32_t*)(map->virt + adr) = d.x[0]; } static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len) { while (len > 0) { - uint32_t d = *((uint32_t*)from)++; + map_word d; + d.x[0] = *((uint32_t*)from)++; dc21285_write32(map, d, to); to += 4; len -= 4; @@ -111,7 +119,8 @@ static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len) { while (len > 0) { - uint16_t d = *((uint16_t*)from)++; + map_word d; + d.x[0] = *((uint16_t*)from)++; dc21285_write16(map, d, to); to += 2; len -= 2; @@ -120,7 +129,8 @@ static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len) { - uint8_t d = *((uint8_t*)from)++; + map_word d; + d.x[0] = *((uint8_t*)from)++; dc21285_write8(map, d, to); to++; len--; @@ -135,8 +145,8 @@ /* Partition stuff */ -static struct mtd_partition *dc21285_parts; #ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition *dc21285_parts; static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; #endif @@ -163,10 +173,10 @@ break; case SA110_CNTL_ROMWIDTH_32: dc21285_map.bankwidth = 4; - break; dc21285_map.read = dc21285_read32; dc21285_map.write = dc21285_write32; dc21285_map.copy_to = dc21285_copy_to_32; + break; default: printk (KERN_ERR "DC21285 flash: undefined bankwidth\n"); return -ENXIO; @@ -175,8 +185,8 @@ dc21285_map.bankwidth*8); /* Let's map the flash area */ - dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024); - if (!dc21285_map.map_priv_1) { + dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024); + if (!dc21285_map.virt) { printk("Failed to ioremap\n"); return -EIO; } @@ -188,14 +198,14 @@ } if (!dc21285_mtd) { - iounmap((void *)dc21285_map.map_priv_1); + iounmap(dc21285_map.virt); return -ENXIO; } dc21285_mtd->owner = THIS_MODULE; #ifdef CONFIG_MTD_PARTITIONS - nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, (void *)0); + nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, 0); if (nrparts > 0) add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts); else @@ -231,7 +241,7 @@ del_mtd_device(dc21285_mtd); map_destroy(dc21285_mtd); - iounmap((void *)dc21285_map.map_priv_1); + iounmap(dc21285_map.virt); } module_init(init_dc21285); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/dilnetpc.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/dilnetpc.c --- linux-2.6.9-intc1/drivers/mtd/maps/dilnetpc.c 2004-10-18 17:53:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/dilnetpc.c 2005-03-21 21:28:42.000000000 -0500 @@ -14,7 +14,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: dilnetpc.c,v 1.18 2005/01/12 22:34:35 gleixner Exp $ * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this @@ -197,7 +197,7 @@ ************************************************************ */ -static spinlock_t dnpc_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dnpc_spin); static int vpp_counter = 0; /* ** This is what has to be done for the DNP board .. @@ -403,7 +403,7 @@ printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); - dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size); + dnpc_map.virt = ioremap_nocache(dnpc_map.phys, dnpc_map.size); dnpc_map_flash(dnpc_map.phys, dnpc_map.size); @@ -413,7 +413,7 @@ } simple_map_init(&dnpc_map); - printk("FLASH virtual address: 0x%lx\n", dnpc_map.virt); + printk("FLASH virtual address: 0x%p\n", dnpc_map.virt); mymtd = do_map_probe("jedec_probe", &dnpc_map); @@ -430,7 +430,7 @@ mymtd->erasesize = 0x10000; if (!mymtd) { - iounmap((void *)dnpc_map.virt); + iounmap(dnpc_map.virt); return -ENXIO; } @@ -481,9 +481,9 @@ map_destroy(mymtd); } if (dnpc_map.virt) { - iounmap((void *)dnpc_map.virt); + iounmap(dnpc_map.virt); dnpc_unmap_flash(); - dnpc_map.virt = 0; + dnpc_map.virt = NULL; } } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/dmv182.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/dmv182.c --- linux-2.6.9-intc1/drivers/mtd/maps/dmv182.c 2004-10-18 17:53:21.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/dmv182.c 2005-03-21 21:28:43.000000000 -0500 @@ -4,7 +4,7 @@ * * Flash map driver for the Dy4 SVME182 board * - * $Id: dmv182.c,v 1.3 2004/07/14 17:45:40 dwmw2 Exp $ + * $Id: dmv182.c,v 1.5 2004/11/04 13:24:14 gleixner Exp $ * * Copyright 2003-2004, TimeSys Corporation * @@ -103,8 +103,7 @@ partitions = svme182_partitions; - svme182_map.virt = - (unsigned long)ioremap(FLASH_BASE_ADDR, svme182_map.size); + svme182_map.virt = ioremap(FLASH_BASE_ADDR, svme182_map.size); if (svme182_map.virt == 0) { printk("Failed to ioremap FLASH memory area.\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ebony.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ebony.c --- linux-2.6.9-intc1/drivers/mtd/maps/ebony.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ebony.c 2005-03-21 21:28:43.000000000 -0500 @@ -1,11 +1,11 @@ /* - * $Id: ebony.c,v 1.10 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: ebony.c,v 1.15 2004/12/09 18:39:54 holindho Exp $ * * Mapping for Ebony user flash * - * Matt Porter + * Matt Porter * - * Copyright 2002 MontaVista Software Inc. + * Copyright 2002-2004 MontaVista Software 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 @@ -21,9 +21,10 @@ #include #include #include +#include #include #include -#include +#include static struct mtd_info *flash; @@ -63,7 +64,7 @@ int __init init_ebony(void) { u8 fpga0_reg; - unsigned long fpga0_adr; + u8 __iomem *fpga0_adr; unsigned long long small_flash_base, large_flash_base; fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16); @@ -92,8 +93,7 @@ large_flash_base = EBONY_LARGE_FLASH_HIGH; ebony_small_map.phys = small_flash_base; - ebony_small_map.virt = - (unsigned long)ioremap64(small_flash_base, + ebony_small_map.virt = ioremap64(small_flash_base, ebony_small_map.size); if (!ebony_small_map.virt) { @@ -103,7 +103,7 @@ simple_map_init(&ebony_small_map); - flash = do_map_probe("map_rom", &ebony_small_map); + flash = do_map_probe("jedec_probe", &ebony_small_map); if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ebony_small_partitions, @@ -114,8 +114,7 @@ } ebony_large_map.phys = large_flash_base; - ebony_large_map.virt = - (unsigned long)ioremap64(large_flash_base, + ebony_large_map.virt = ioremap64(large_flash_base, ebony_large_map.size); if (!ebony_large_map.virt) { @@ -125,7 +124,7 @@ simple_map_init(&ebony_large_map); - flash = do_map_probe("cfi_probe", &ebony_large_map); + flash = do_map_probe("jedec_probe", &ebony_large_map); if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ebony_large_partitions, @@ -146,13 +145,13 @@ } if (ebony_small_map.virt) { - iounmap((void *)ebony_small_map.virt); - ebony_small_map.virt = 0; + iounmap(ebony_small_map.virt); + ebony_small_map.virt = NULL; } if (ebony_large_map.virt) { - iounmap((void *)ebony_large_map.virt); - ebony_large_map.virt = 0; + iounmap(ebony_large_map.virt); + ebony_large_map.virt = NULL; } } @@ -160,5 +159,5 @@ module_exit(cleanup_ebony); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matt Porter "); +MODULE_AUTHOR("Matt Porter "); MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/edb7312.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/edb7312.c --- linux-2.6.9-intc1/drivers/mtd/maps/edb7312.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/edb7312.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: edb7312.c,v 1.11 2004/07/14 09:52:55 dwmw2 Exp $ + * $Id: edb7312.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * Handle mapping of the NOR flash on Cogent EDB7312 boards * @@ -71,19 +71,18 @@ #endif -static int mtd_parts_nb; -static struct mtd_partition *mtd_parts; +static int mtd_parts_nb = 0; +static struct mtd_partition *mtd_parts = 0; int __init init_edb7312nor(void) { static const char *rom_probe_types[] = PROBETYPES; const char **type; - const char *part_type = NULL; + const char *part_type = 0; printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", WINDOW_SIZE, WINDOW_ADDR); - edb7312nor_map.virt = (unsigned long) - ioremap(WINDOW_ADDR, WINDOW_SIZE); + edb7312nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!edb7312nor_map.virt) { printk(MSG_PREFIX "failed to ioremap\n"); @@ -92,7 +91,7 @@ simple_map_init(&edb7312nor_map); - mymtd = NULL; + mymtd = 0; type = rom_probe_types; for(; !mymtd && *type; type++) { mymtd = do_map_probe(*type, &edb7312nor_map); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/epxa10db-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/epxa10db-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/epxa10db-flash.c 2004-10-18 17:54:54.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/epxa10db-flash.c 2005-03-21 21:28:42.000000000 -0500 @@ -5,7 +5,7 @@ * Copyright (C) 2001 Altera Corporation * Copyright (C) 2001 Red Hat, Inc. * - * $Id: epxa10db-flash.c,v 1.11 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: epxa10db-flash.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * 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 @@ -62,7 +62,7 @@ printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); - epxa_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); + epxa_map.virt = ioremap(FLASH_START, FLASH_SIZE); if (!epxa_map.virt) { printk("Failed to ioremap %s flash\n",BOARD_NAME); return -EIO; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/fortunet.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/fortunet.c --- linux-2.6.9-intc1/drivers/mtd/maps/fortunet.c 2004-10-18 17:53:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/fortunet.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,6 +1,6 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.7 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: fortunet.c,v 1.9 2004/11/04 13:24:14 gleixner Exp $ */ #include @@ -209,8 +209,8 @@ map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, - map_regions[ix].map_info.virt = - (int)ioremap_nocache( + map_regions[ix].map_info.virt = + ioremap_nocache( map_regions[ix].window_addr_physical, map_regions[ix].map_info.size); if(!map_regions[ix].map_info.virt) diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/h720x-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/h720x-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/h720x-flash.c 2004-10-18 17:53:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/h720x-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -2,7 +2,7 @@ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based * evaluation boards * - * $Id: h720x-flash.c,v 1.9 2004/07/14 17:45:40 dwmw2 Exp $ + * $Id: h720x-flash.c,v 1.11 2004/11/04 13:24:14 gleixner Exp $ * * (C) 2002 Jungjun Kim * 2003 Thomas Gleixner @@ -73,7 +73,7 @@ char *part_type = NULL; - h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE); + h720x_map.virt = ioremap(FLASH_PHYS, FLASH_SIZE); if (!h720x_map.virt) { printk(KERN_ERR "H720x-MTD: ioremap failed\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ichxrom.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ichxrom.c --- linux-2.6.9-intc1/drivers/mtd/maps/ichxrom.c 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ichxrom.c 2005-03-21 21:28:42.000000000 -0500 @@ -2,7 +2,7 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.8 2004/07/16 17:43:11 dwmw2 Exp $ + * $Id: ichxrom.c,v 1.17 2005/03/18 14:04:35 gleixner Exp $ */ #include @@ -12,187 +12,87 @@ #include #include #include +#include +#include #include #include #include -#include +#include #define xstr(s) str(s) #define str(s) #s #define MOD_NAME xstr(KBUILD_BASENAME) -#define MTD_DEV_NAME_LENGTH 16 - -#define RESERVE_MEM_REGION 0 - +#define ADDRESS_NAME_LEN 18 -#define MANUFACTURER_INTEL 0x0089 -#define I82802AB 0x00ad -#define I82802AC 0x00ac +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ -#define ICHX_FWH_REGION_START 0xFF000000UL -#define ICHX_FWH_REGION_SIZE 0x01000000UL #define BIOS_CNTL 0x4e #define FWH_DEC_EN1 0xE3 #define FWH_DEC_EN2 0xF0 #define FWH_SEL1 0xE8 #define FWH_SEL2 0xEE -struct ichxrom_map_info { - struct map_info map; - struct mtd_info *mtd; - unsigned long window_addr; +struct ichxrom_window { + void __iomem* virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; struct pci_dev *pdev; - struct resource window_rsrc; - struct resource rom_rsrc; - char mtd_name[MTD_DEV_NAME_LENGTH]; }; -static inline unsigned long addr(struct map_info *map, unsigned long ofs) -{ - unsigned long offset; - offset = ((8*1024*1024) - map->size) + ofs; - if (offset >= (4*1024*1024)) { - offset += 0x400000; - } - return map->map_priv_1 + 0x400000 + offset; -} - -static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr) -{ - return addr - map->map_priv_1 + ICHX_FWH_REGION_START; -} - -static map_word ichxrom_read(struct map_info *map, unsigned long ofs) -{ - map_word val; - int i; - switch(map->bankwidth) { - case 1: val.x[0] = __raw_readb(addr(map, ofs)); break; - case 2: val.x[0] = __raw_readw(addr(map, ofs)); break; - case 4: val.x[0] = __raw_readl(addr(map, ofs)); break; -#if BITS_PER_LONG >= 64 - case 8: val.x[0] = __raw_readq(addr(map, ofs)); break; -#endif - default: val.x[0] = 0; break; - } - for(i = 1; i < map_words(map); i++) { - val.x[i] = 0; - } - return val; -} - -static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, addr(map, from), len); -} - -static void ichxrom_write(struct map_info *map, map_word d, unsigned long ofs) -{ - switch(map->bankwidth) { - case 1: __raw_writeb(d.x[0], addr(map,ofs)); break; - case 2: __raw_writew(d.x[0], addr(map,ofs)); break; - case 4: __raw_writel(d.x[0], addr(map,ofs)); break; -#if BITS_PER_LONG >= 64 - case 8: __raw_writeq(d.x[0], addr(map,ofs)); break; -#endif - } - mb(); -} - -static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(addr(map, to), from, len); -} - -static struct ichxrom_map_info ichxrom_map = { - .map = { - .name = MOD_NAME, - .phys = NO_XIP, - .size = 0, - .bankwidth = 1, - .read = ichxrom_read, - .copy_from = ichxrom_copy_from, - .write = ichxrom_write, - .copy_to = ichxrom_copy_to, - /* Firmware hubs only use vpp when being programmed - * in a factory setting. So in-place programming - * needs to use a different method. - */ - }, - /* remaining fields of structure are initialized to 0 */ +struct ichxrom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; -enum fwh_lock_state { - FWH_DENY_WRITE = 1, - FWH_IMMUTABLE = 2, - FWH_DENY_READ = 4, +static struct ichxrom_window ichxrom_window = { + .maps = LIST_HEAD_INIT(ichxrom_window.maps), }; -static void ichxrom_cleanup(struct ichxrom_map_info *info) +static void ichxrom_cleanup(struct ichxrom_window *window) { + struct ichxrom_map_info *map, *scratch; u16 word; /* Disable writes through the rom window */ - pci_read_config_word(info->pdev, BIOS_CNTL, &word); - pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1); - - if (info->mtd) { - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.virt = 0; - } - if (info->rom_rsrc.parent) - release_resource(&info->rom_rsrc); - if (info->window_rsrc.parent) - release_resource(&info->window_rsrc); - - if (info->window_addr) { - iounmap((void *)(info->window_addr)); - info->window_addr = 0; - } -} + pci_read_config_word(window->pdev, BIOS_CNTL, &word); + pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1); - -static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len, - enum fwh_lock_state state) -{ - struct map_info *map = mtd->priv; - unsigned long start = ofs; - unsigned long end = start + len -1; - - /* FIXME do I need to guard against concurrency here? */ - /* round down to 64K boundaries */ - start = start & ~0xFFFF; - end = end & ~0xFFFF; - while (start <= end) { - unsigned long ctrl_addr; - ctrl_addr = addr(map, start) - 0x400000 + 2; - writeb(state, ctrl_addr); - start = start + 0x10000; + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; } - return 0; } -static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE); -} - -static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return ichxrom_set_lock_state(mtd, ofs, len, 0); -} static int __devinit ichxrom_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + struct ichxrom_window *window = &ichxrom_window; + struct ichxrom_map_info *map = NULL; + unsigned long map_top; + u8 byte; u16 word; - struct ichxrom_map_info *info = &ichxrom_map; - unsigned long map_size; - static char *probes[] = { "cfi_probe", "jedec_probe" }; - struct cfi_private *cfi; /* For now I just handle the ichx and I assume there * are not a lot of resources up at the top of the address @@ -204,26 +104,56 @@ * Also you can page firmware hubs if an 8MB window isn't enough * but don't currently handle that case either. */ + window->pdev = pdev; - info->pdev = pdev; + /* Find a region continuous to the end of the ROM window */ + window->phys = 0; + pci_read_config_byte(pdev, FWH_DEC_EN1, &byte); + if (byte == 0xff) { + window->phys = 0xffc00000; + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + if ((byte & 0x0f) == 0x0f) { + window->phys = 0xff400000; + } + else if ((byte & 0x0e) == 0x0e) { + window->phys = 0xff500000; + } + else if ((byte & 0x0c) == 0x0c) { + window->phys = 0xff600000; + } + else if ((byte & 0x08) == 0x08) { + window->phys = 0xff700000; + } + } + else if ((byte & 0xfe) == 0xfe) { + window->phys = 0xffc80000; + } + else if ((byte & 0xfc) == 0xfc) { + window->phys = 0xffd00000; + } + else if ((byte & 0xf8) == 0xf8) { + window->phys = 0xffd80000; + } + else if ((byte & 0xf0) == 0xf0) { + window->phys = 0xffe00000; + } + else if ((byte & 0xe0) == 0xe0) { + window->phys = 0xffe80000; + } + else if ((byte & 0xc0) == 0xc0) { + window->phys = 0xfff00000; + } + else if ((byte & 0x80) == 0x80) { + window->phys = 0xfff80000; + } - /* - * Try to reserve the window mem region. If this fails then - * it is likely due to the window being "reseved" by the BIOS. - */ - info->window_rsrc.name = MOD_NAME; - info->window_rsrc.start = ICHX_FWH_REGION_START; - info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1; - info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&iomem_resource, &info->window_rsrc)) { - info->window_rsrc.parent = NULL; - printk(KERN_ERR MOD_NAME - " %s(): Unable to register resource" - " 0x%.08lx-0x%.08lx - kernel bug?\n", - __func__, - info->window_rsrc.start, info->window_rsrc.end); + if (window->phys == 0) { + printk(KERN_ERR MOD_NAME ": Rom window is closed\n"); + goto out; } - + window->phys -= 0x400000UL; + window->size = (0xffffffffUL - window->phys) + 1UL; + /* Enable writes through the rom window */ pci_read_config_word(pdev, BIOS_CNTL, &word); if (!(word & 1) && (word & (1<<1))) { @@ -231,119 +161,167 @@ * this device, so don't even try. */ printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); - goto failed; + goto out; } pci_write_config_word(pdev, BIOS_CNTL, word | 1); - - /* Map the firmware hub into my address space. */ - /* Does this use too much virtual address space? */ - info->window_addr = (unsigned long)ioremap( - ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); - if (!info->window_addr) { - printk(KERN_ERR "Failed to ioremap\n"); - goto failed; + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to the window being "reseved" by the BIOS. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_DEBUG MOD_NAME + ": %s(): Unable to register resource" + " 0x%.08lx-0x%.08lx - kernel bug?\n", + __func__, + window->rsrc.start, window->rsrc.end); } - /* For now assume the firmware has setup all relevant firmware - * windows. We don't have enough information to handle this case - * intelligently. + /* Map the firmware hub into my address space. */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } + + /* Get the first address to look for an rom chip at */ + map_top = window->phys; + if ((window->phys & 0x3fffff) != 0) { + map_top = window->phys + 0x400000; + } +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * Probe at most the last 4M of the address space. */ + if (map_top < 0xffc00000) { + map_top = 0xffc00000; + } +#endif + /* Loop through and look for rom chips */ + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; - /* FIXME select the firmware hub and enable a window to it. */ - - info->mtd = NULL; - info->map.map_priv_1 = info->window_addr; + if (!map) { + map = kmalloc(sizeof(*map), GFP_KERNEL); + } + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); - /* Loop through the possible bankwidths */ - for(ichxrom_map.map.bankwidth = 4; ichxrom_map.map.bankwidth; ichxrom_map.map.bankwidth >>= 1) { - map_size = ICHX_FWH_REGION_SIZE; - while(!info->mtd && (map_size > 0)) { - int i; - info->map.size = map_size; - for(i = 0; i < sizeof(probes)/sizeof(char *); i++) { - info->mtd = do_map_probe(probes[i], &ichxrom_map.map); - if (info->mtd) - break; + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in-place programming + * needs to use a different method. + */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; } - map_size -= 512*1024; } - if (info->mtd) - break; - } - if (!info->mtd) { - goto failed; - } - cfi = ichxrom_map.map.fldrv_priv; - if ((cfi->mfr == MANUFACTURER_INTEL) && ( - (cfi->id == I82802AB) || - (cfi->id == I82802AC))) - { - /* If it is a firmware hub put in the special lock - * and unlock routines. - */ - info->mtd->lock = ichxrom_lock; - info->mtd->unlock = ichxrom_unlock; - } - if (info->mtd->size > info->map.size) { - printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%lu). fixing...\n", - info->mtd->size, info->map.size); - info->mtd->size = info->map.size; - } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } + } + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) { + cfi->chips[i].start += offset; + } - info->mtd->owner = THIS_MODULE; - add_mtd_device(info->mtd); + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } - if (info->window_rsrc.parent) { - /* - * Registering the MTD device in iomem may not be possible - * if there is a BIOS "reserved" and BUSY range. If this - * fails then continue anyway. - */ - snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, - "mtd%d", info->mtd->index); - info->rom_rsrc.name = info->mtd_name; - info->rom_rsrc.start = ICHX_FWH_REGION_START - + ICHX_FWH_REGION_SIZE - map_size; - info->rom_rsrc.end = ICHX_FWH_REGION_START - + ICHX_FWH_REGION_SIZE; - info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { - printk(KERN_ERR MOD_NAME - ": cannot reserve MTD resource\n"); - info->rom_rsrc.parent = NULL; - } - } + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + if (map) { + kfree(map); + } + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + ichxrom_cleanup(window); + return -ENODEV; + } return 0; - - failed: - ichxrom_cleanup(info); - return -ENODEV; } static void __devexit ichxrom_remove_one (struct pci_dev *pdev) { - struct ichxrom_map_info *info = &ichxrom_map; - u16 word; - - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.map_priv_1 = 0; - - iounmap((void *)(info->window_addr)); - info->window_addr = 0; - - /* Disable writes through the rom window */ - pci_read_config_word(pdev, BIOS_CNTL, &word); - pci_write_config_word(pdev, BIOS_CNTL, word & ~1); - -#if RESERVE_MEM_REGION - release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); -#endif + struct ichxrom_window *window = &ichxrom_window; + ichxrom_cleanup(window); } static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { @@ -371,8 +349,7 @@ }; #endif -static struct pci_dev *mydev; -int __init init_ichxrom(void) +static int __init init_ichxrom(void) { struct pci_dev *pdev; struct pci_device_id *id; @@ -385,18 +362,17 @@ } } if (pdev) { - mydev = pdev; return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]); } return -ENXIO; #if 0 - return pci_module_init(&ichxrom_driver); + return pci_register_driver(&ichxrom_driver); #endif } static void __exit cleanup_ichxrom(void) { - ichxrom_remove_one(mydev); + ichxrom_remove_one(ichxrom_window.pdev); } module_init(init_ichxrom); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/impa7.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/impa7.c --- linux-2.6.9-intc1/drivers/mtd/maps/impa7.c 2004-10-18 17:53:41.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/impa7.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: impa7.c,v 1.11 2004/07/14 09:52:55 dwmw2 Exp $ + * $Id: impa7.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * Handle mapping of the NOR flash on implementa A7 boards * @@ -77,7 +77,7 @@ { static const char *rom_probe_types[] = PROBETYPES; const char **type; - const char *part_type = NULL; + const char *part_type = 0; int i; static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { { WINDOW_ADDR0, WINDOW_SIZE0 }, @@ -91,15 +91,14 @@ pt[i].size, pt[i].addr); impa7_map[i].phys = pt[i].addr; - impa7_map[i].virt = (unsigned long) - ioremap(pt[i].addr, pt[i].size); + impa7_map[i].virt = ioremap(pt[i].addr, pt[i].size); if (!impa7_map[i].virt) { printk(MSG_PREFIX "failed to ioremap\n"); return -EIO; } simple_map_init(&impa7_map[i]); - impa7_mtd[i] = NULL; + impa7_mtd[i] = 0; type = rom_probe_types; for(; !impa7_mtd[i] && *type; type++) { impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/integrator-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/integrator-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/integrator-flash.c 2004-10-18 17:55:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/integrator-flash.c 2005-03-21 21:28:42.000000000 -0500 @@ -22,7 +22,7 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: integrator-flash.c,v 1.16 2004/07/12 21:59:44 dwmw2 Exp $ + $Id: integrator-flash.c,v 1.18 2004/11/01 13:26:15 rmk Exp $ ======================================================================*/ @@ -75,7 +75,7 @@ unsigned int size = res->end - res->start + 1; struct armflash_info *info; int err; - void *base; + void __iomem *base; info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL); if (!info) { @@ -110,7 +110,7 @@ info->map.size = size; info->map.bankwidth = plat->width; info->map.phys = res->start; - info->map.virt = (unsigned long) base; + info->map.virt = base; info->map.name = dev->dev.bus_id; info->map.set_vpp = armflash_set_vpp; @@ -179,7 +179,7 @@ if (info->parts) kfree(info->parts); - iounmap((void *)info->map.virt); + iounmap(info->map.virt); release_resource(info->res); kfree(info->res); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ipaq-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ipaq-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/ipaq-flash.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ipaq-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,464 @@ +/* + * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based) + * + * (C) 2000 Nicolas Pitre + * (C) 2002 Hewlett-Packard Company + * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes + * + * $Id: ipaq-flash.c,v 1.4 2005/01/12 22:34:35 gleixner Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef CONFIG_MTD_CONCAT +#include +#endif + +#include +#include +#include + + +#ifndef CONFIG_IPAQ_HANDHELD +#error This is for iPAQ Handhelds only +#endif +#ifdef CONFIG_SA1100_JORNADA56X + +static void jornada56x_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; +} + +#endif + +#ifdef CONFIG_SA1100_JORNADA720 + +static void jornada720_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} + +#endif + +#define MAX_IPAQ_CS 2 /* Number of CS we are going to test */ + +#define IPAQ_MAP_INIT(X) \ + { \ + name: "IPAQ flash " X, \ + } + + +static struct map_info ipaq_map[MAX_IPAQ_CS] = { + IPAQ_MAP_INIT("bank 1"), + IPAQ_MAP_INIT("bank 2") +}; + +static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = { + NULL, + NULL +}; + +/* + * Here are partition information for all known IPAQ-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * + * The *_max_flash_size is the maximum possible mapped flash size which + * is not necessarily the actual flash size. It must be no more than + * the value specified in the "struct map_desc *_io_desc" mapping + * definition for the corresponding machine. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. + */ + +#ifdef CONFIG_IPAQ_HANDHELD +static unsigned long h3xxx_max_flash_size = 0x04000000; +static struct mtd_partition h3xxx_partitions[] = { + { + name: "H3XXX boot firmware", +#ifndef CONFIG_LAB + size: 0x00040000, +#else + size: 0x00080000, +#endif + offset: 0, +#ifndef CONFIG_LAB + mask_flags: MTD_WRITEABLE, /* force read-only */ +#endif + }, + { + name: "H3XXX root jffs2", +#ifndef CONFIG_LAB + size: 0x2000000 - 2*0x40000, /* Warning, this is fixed later */ + offset: 0x00040000, +#else + size: 0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */ + offset: 0x00080000, +#endif + }, + { + name: "asset", + size: 0x40000, + offset: 0x2000000 - 0x40000, /* Warning, this is fixed later */ + mask_flags: MTD_WRITEABLE, /* force read-only */ + } +}; + +#ifndef CONFIG_MTD_CONCAT +static struct mtd_partition h3xxx_partitions_bank2[] = { + /* this is used only on 2 CS machines when concat is not present */ + { + name: "second H3XXX root jffs2", + size: 0x1000000 - 0x40000, /* Warning, this is fixed later */ + offset: 0x00000000, + }, + { + name: "second asset", + size: 0x40000, + offset: 0x1000000 - 0x40000, /* Warning, this is fixed later */ + mask_flags: MTD_WRITEABLE, /* force read-only */ + } +}; +#endif + +static DEFINE_SPINLOCK(ipaq_vpp_lock); + +static void h3xxx_set_vpp(struct map_info *map, int vpp) +{ + static int nest = 0; + + spin_lock(&ipaq_vpp_lock); + if (vpp) + nest++; + else + nest--; + if (nest) + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1); + else + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0); + spin_unlock(&ipaq_vpp_lock); +} + +#endif + +#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720) +static unsigned long jornada_max_flash_size = 0x02000000; +static struct mtd_partition jornada_partitions[] = { + { + name: "Jornada boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "Jornada root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00040000, + } +}; +#endif + + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +static unsigned long cs_phys[] = { +#ifdef CONFIG_ARCH_SA1100 + SA1100_CS0_PHYS, + SA1100_CS1_PHYS, + SA1100_CS2_PHYS, + SA1100_CS3_PHYS, + SA1100_CS4_PHYS, + SA1100_CS5_PHYS, +#else + PXA_CS0_PHYS, + PXA_CS1_PHYS, + PXA_CS2_PHYS, + PXA_CS3_PHYS, + PXA_CS4_PHYS, + PXA_CS5_PHYS, +#endif +}; + +static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; + +static int __init h1900_special_case(void); + +int __init ipaq_mtd_init(void) +{ + struct mtd_partition *parts = NULL; + int nb_parts = 0; + int parsed_nr_parts = 0; + const char *part_type; + int i; /* used when we have >1 flash chips */ + unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */ + + /* Default flash bankwidth */ + // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; + + if (machine_is_h1900()) + { + /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */ + return h1900_special_case(); + } + + if (machine_is_h3100() || machine_is_h1900()) + for(i=0; isize); + + /* do we really need this debugging? --joshua 20030703 */ + // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]); + my_sub_mtd[i]->owner = THIS_MODULE; + tot_flashsize += my_sub_mtd[i]->size; + } +#ifdef CONFIG_MTD_CONCAT + /* fix the asset location */ +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */; +# else + h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000; +# endif + h3xxx_partitions[2].offset = tot_flashsize - 0x40000; + /* and concat the devices */ + mymtd = mtd_concat_create(&my_sub_mtd[0], i, + "ipaq"); + if (!mymtd) { + printk("Cannot create iPAQ concat device\n"); + return -ENXIO; + } +#else + mymtd = my_sub_mtd[0]; + + /* + *In the very near future, command line partition parsing + * will use the device name as 'mtd-id' instead of a value + * passed to the parse_cmdline_partitions() routine. Since + * the bootldr says 'ipaq', make sure it continues to work. + */ + mymtd->name = "ipaq"; + + if ((machine_is_h3600())) { +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000; +# else + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000; +# endif + nb_parts = 2; + } else { +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */ +# else + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000; +# endif + h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000; + } + + if (my_sub_mtd[1]) { +# ifdef CONFIG_LAB + h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000; +# else + h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000; +# endif + h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000; + } +#endif + } + else { + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt); + mymtd = do_map_probe("cfi_probe", &ipaq_map[0]); + if (!mymtd) + return -ENXIO; + mymtd->owner = THIS_MODULE; + } + + + /* + * Dynamic partition selection stuff (might override the static ones) + */ + + i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0); + + if (i > 0) { + nb_parts = parsed_nr_parts = i; + parts = parsed_parts; + part_type = "dynamic"; + } + + if (!parts) { + printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n"); + add_mtd_device(mymtd); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + add_mtd_device(my_sub_mtd[1]); +#endif + } else { + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2)); +#endif + } + + return 0; +} + +static void __exit ipaq_mtd_cleanup(void) +{ + int i; + + if (mymtd) { + del_mtd_partitions(mymtd); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + del_mtd_partitions(my_sub_mtd[1]); +#endif + map_destroy(mymtd); +#ifdef CONFIG_MTD_CONCAT + for(i=0; i @@ -44,8 +44,8 @@ }; static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs) -{ - unsigned long (*set_bank)(unsigned long) = +{ + unsigned long (*set_bank)(unsigned long) = (unsigned long(*)(unsigned long))map->map_priv_2; return (set_bank ? set_bank(ofs) : ofs); @@ -53,15 +53,15 @@ #ifdef __ARMEB__ /* - * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which - * causes the lower address bits to be XORed with 0x11 on 8 bit accesses - * and XORed with 0x10 on 16 bit accesses. See the spec update, erratta 44. + * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which + * causes the lower address bits to be XORed with 0x11 on 8 bit accesses + * and XORed with 0x10 on 16 bit accesses. See the spec update, erratum 44. */ -static int errata44_workaround = 0; +static int erratum44_workaround = 0; static inline unsigned long address_fix8_write(unsigned long addr) { - if (errata44_workaround) { + if (erratum44_workaround) { return (addr ^ 3); } return addr; @@ -88,7 +88,7 @@ unsigned long from, ssize_t len) { from = flash_bank_setup(map, from); - while(len--) + while(len--) *(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++); } @@ -127,8 +127,8 @@ if (info->map.map_priv_1) iounmap((void *) info->map.map_priv_1); - if (info->partitions) - kfree(info->partitions); + if (info->partitions) { + kfree(info->partitions); } if (info->res) { release_resource(info->res); @@ -147,11 +147,11 @@ static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; struct platform_device *dev = to_platform_device(_dev); struct ixp2000_flash_data *ixp_data = dev->dev.platform_data; - struct flash_platform_data *plat; + struct flash_platform_data *plat; struct ixp2000_flash_info *info; unsigned long window_size; int err = -1; - + if (!ixp_data) return -ENODEV; @@ -160,7 +160,7 @@ return -ENODEV; window_size = dev->resource->end - dev->resource->start + 1; - dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dM)\n", + dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n", ixp_data->nr_banks, ((u32)window_size >> 20)); if (plat->width != 1) { @@ -173,7 +173,7 @@ if(!info) { err = -ENOMEM; goto Error; - } + } memzero(info, sizeof(struct ixp2000_flash_info)); dev_set_drvdata(&dev->dev, info); @@ -183,7 +183,7 @@ * not attempt to do a direct access on us. */ info->map.phys = NO_XIP; - + info->nr_banks = ixp_data->nr_banks; info->map.size = ixp_data->nr_banks * window_size; info->map.bankwidth = 1; @@ -191,7 +191,7 @@ /* * map_priv_2 is used to store a ptr to to the bank_setup routine */ - info->map.map_priv_2 = (u32) ixp_data->bank_setup; + info->map.map_priv_2 = (void __iomem *) ixp_data->bank_setup; info->map.name = dev->dev.bus_id; info->map.read = ixp2000_flash_read8; @@ -199,8 +199,8 @@ info->map.copy_from = ixp2000_flash_copy_from; info->map.copy_to = ixp2000_flash_copy_to; - info->res = request_mem_region(dev->resource->start, - dev->resource->end - dev->resource->start + 1, + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, dev->dev.bus_id); if (!info->res) { dev_err(_dev, "Could not reserve memory region\n"); @@ -208,28 +208,22 @@ goto Error; } - info->map.map_priv_1 = - (unsigned long) ioremap(dev->resource->start, - dev->resource->end - dev->resource->start + 1); + info->map.map_priv_1 = ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); if (!info->map.map_priv_1) { dev_err(_dev, "Failed to ioremap flash region\n"); err = -EIO; goto Error; } - /* - * Setup read mode for FLASH - */ - *IXP2000_SLOWPORT_FRM = 1; - #if defined(__ARMEB__) /* - * Enable errata 44 workaround for NPUs with broken slowport + * Enable erratum 44 workaround for NPUs with broken slowport */ - errata44_workaround = ixp2000_has_broken_slowport(); - dev_info(_dev, "Errata 44 workaround %s\n", - errata44_workaround ? "enabled" : "disabled"); + erratum44_workaround = ixp2000_has_broken_slowport(); + dev_info(_dev, "Erratum 44 workaround %s\n", + erratum44_workaround ? "enabled" : "disabled"); #endif info->mtd = do_map_probe(plat->map_name, &info->map); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ixp4xx.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ixp4xx.c --- linux-2.6.9-intc1/drivers/mtd/maps/ixp4xx.c 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ixp4xx.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: ixp4xx.c,v 1.6 2004/09/17 00:25:06 gleixner Exp $ + * $Id: ixp4xx.c,v 1.7 2004/11/04 13:24:15 gleixner Exp $ * * drivers/mtd/maps/ixp4xx.c * @@ -69,7 +69,7 @@ dest[len - 1] = BYTE0(src[i]); } -/* +/* * Unaligned writes are ignored, causing the 8-bit * probe to fail and proceed to the 16-bit probe (which succeeds). */ @@ -79,7 +79,7 @@ *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; } -/* +/* * Fast write16 function without the probing check above */ static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) @@ -196,9 +196,8 @@ goto Error; } - info->map.map_priv_1 = - (void __iomem *) ioremap(dev->resource->start, - dev->resource->end - dev->resource->start + 1); + info->map.map_priv_1 = ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); if (!info->map.map_priv_1) { printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n"); err = -EIO; @@ -212,7 +211,7 @@ goto Error; } info->mtd->owner = THIS_MODULE; - + /* Use the fast version */ info->map.write = ixp4xx_write16, diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/Kconfig linux-2.6.9-intc1-mih1/drivers/mtd/maps/Kconfig --- linux-2.6.9-intc1/drivers/mtd/maps/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/Kconfig 2005-03-21 23:19:26.000000000 -0500 @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.30 2004/07/21 00:16:14 jwboyer Exp $ +# $Id: Kconfig,v 1.51 2005/03/18 02:07:22 gleixner Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -60,6 +60,92 @@ Ignore this option if you use run-time physmap configuration (i.e., run-time calling physmap_configure()). +config MTD_MULTI_PHYSMAP + tristate "multiple CFI Flash devices in physical memory map" + depends on MTD_CFI + help + Similar to MTD_PHYSMAP, just allows several seperate mappings. + +config MTD_MULTI_PHYSMAP_1_NAME + string "1st mapping name" + depends on MTD_MULTI_PHYSMAP + default "" + +config MTD_MULTI_PHYSMAP_1_START + hex "1st mapping start" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_1_LEN + hex "1st mapping length" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_1_WIDTH + int "1st mapping bus width - 0 disables mapping" + depends on MTD_MULTI_PHYSMAP + default "2" + +config MTD_MULTI_PHYSMAP_2_NAME + string "2nd mapping name" + depends on MTD_MULTI_PHYSMAP + default "" + +config MTD_MULTI_PHYSMAP_2_START + hex "2nd mapping start" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_2_LEN + hex "2nd mapping length" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_2_WIDTH + int "2nd mapping bus width - 0 disables mapping" + depends on MTD_MULTI_PHYSMAP + default "2" + +config MTD_MULTI_PHYSMAP_3_NAME + string "3rd mapping name" + depends on MTD_MULTI_PHYSMAP + default "" + +config MTD_MULTI_PHYSMAP_3_START + hex "3rd mapping start" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_3_LEN + hex "3rd mapping length" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_3_WIDTH + int "3rd mapping bus width - 0 disables mapping" + depends on MTD_MULTI_PHYSMAP + default "2" + +config MTD_MULTI_PHYSMAP_4_NAME + string "4th mapping name" + depends on MTD_MULTI_PHYSMAP + default "" + +config MTD_MULTI_PHYSMAP_4_START + hex "4th mapping start" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_4_LEN + hex "4th mapping length" + depends on MTD_MULTI_PHYSMAP + default "0x00000000" + +config MTD_MULTI_PHYSMAP_4_WIDTH + int "4th mapping bus width - 0 disables mapping" + depends on MTD_MULTI_PHYSMAP + default "2" + config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" depends on (SPARC32 || SPARC64) && MTD_CFI @@ -92,6 +178,25 @@ demonstration board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_TS5500 + tristate "JEDEC Flash device mapped on Technologic Systems TS-5500" + depends on X86 && MTD_JEDECPROBE && MTD_PARTITIONS + help + This provides a driver for the on-board flash of the Technologic + System's TS-5500 board. The flash is split into 3 partitions + which are accessed as separate MTD devices. + + mtd0 and mtd2 are the two BIOS drives. Unfortunately the BIOS + uses a proprietary flash translation layer from General Software, + which is not supported (the drives cannot be mounted). You can + create your own file system (jffs for example), but the BIOS + won't be able to boot from it. + + mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL. + + Note that jumper 3 ("Write Enable Drive A") must be set + otherwise detection won't succeeed. + config MTD_SBC_GXX tristate "CFI Flash device mapped on Arcom SBC-GXx boards" depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS @@ -103,16 +208,6 @@ More info at . -config MTD_ELAN_104NC - tristate "CFI Flash device mapped on Arcom ELAN-104NC" - depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS - help - This provides a driver for the on-board flash of the Arcom Control - System's ELAN-104NC development board. By default the flash - is split into 3 partitions which are accessed as separate MTD - devices. This board utilizes Intel StrataFlash. More info at - . - config MTD_LUBBOCK tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS @@ -140,7 +235,7 @@ config MTD_SCx200_DOCFLASH tristate "Flash device mapped with DOCCS on NatSemi SCx200" - depends on X86 && MTD_CFI + depends on SCx200 && MTD_CFI && MTD_PARTITIONS help Enable support for a flash chip mapped using the DOCCS signal on a National Semiconductor SCx200 processor. @@ -160,7 +255,7 @@ config MTD_ICHXROM tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5" - depends on X86 && MTD_JEDECPROBE && MTD_COMPLEX_MAPPINGS + depends on X86 && MTD_JEDECPROBE help Support for treating the BIOS flash chip on ICHX motherboards as an MTD device - with this you can reprogram your BIOS. @@ -194,74 +289,11 @@ help Support for flash chips on NETtel/SecureEdge/SnapGear boards. -config MTD_PB1XXX - tristate "Flash devices on Alchemy PB1xxx boards" - depends on MIPS && ( MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 ) - help - Flash memory access on Alchemy Pb1000/Pb1100/Pb1500 boards - -config MTD_PB1XXX_BOOT - bool "PB1x00 boot flash device" - depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 ) - help - Use the first of the two 32MiB flash banks on Pb1100/Pb1500 board. - You can say 'Y' to both this and 'MTD_PB1XXX_USER' below, to use - both banks. - -config MTD_PB1XXX_USER - bool "PB1x00 user flash device" - depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 ) - default y if MTD_PB1XX_BOOT = n - help - Use the second of the two 32MiB flash banks on Pb1100/Pb1500 board. - You can say 'Y' to both this and 'MTD_PB1XXX_BOOT' above, to use - both banks. - -config MTD_PB1550 - tristate "Flash devices on Alchemy PB1550 board" - depends on MIPS && MIPS_PB1550 - help - Flash memory access on Alchemy Pb1550 board - -config MTD_PB1550_BOOT - bool "PB1550 boot flash device" - depends on MTD_PB1550 - help - Use the first of the two 64MiB flash banks on Pb1550 board. - You can say 'Y' to both this and 'MTD_PB1550_USER' below, to use - both banks. - -config MTD_PB1550_USER - bool "PB1550 user flash device" - depends on MTD_PB1550 - default y if MTD_PB1550_BOOT = n - help - Use the second of the two 64MiB flash banks on Pb1550 board. - You can say 'Y' to both this and 'MTD_PB1550_BOOT' above, to use - both banks. - -config MTD_DB1550 - tristate "Flash devices on Alchemy DB1550 board" - depends on MIPS && MIPS_DB1550 - help - Flash memory access on Alchemy Db1550 board - -config MTD_DB1550_BOOT - bool "DB1550 boot flash device" - depends on MTD_DB1550 - help - Use the first of the two 64MiB flash banks on Db1550 board. - You can say 'Y' to both this and 'MTD_DB1550_USER' below, to use - both banks. - -config MTD_DB1550_USER - bool "DB1550 user flash device" - depends on MTD_DB1550 - default y if MTD_DB1550_BOOT = n - help - Use the second of the two 64MiB flash banks on Db1550 board. - You can say 'Y' to both this and 'MTD_DB1550_BOOT' above, to use - both banks. +config MTD_ALCHEMY + tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support' + depends on MIPS && SOC_AU1X00 + help + Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" @@ -335,7 +367,7 @@ tristate "CFI Flash device mapping on FlagaDM" depends on PPC32 && 8xx && MTD_CFI help - Mapping for the Flaga digital module. If you don´t have one, ignore + Mapping for the Flaga digital module. If you don't have one, ignore this setting. config MTD_BEECH @@ -354,14 +386,30 @@ Arctic board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_WALNUT + tristate "Flash device mapped on IBM 405GP Walnut" + depends on MTD_JEDECPROBE && PPC32 && 40x && WALNUT + help + This enables access routines for the flash chips on the IBM 405GP + Walnut board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + config MTD_EBONY - tristate "CFI Flash device mapped on IBM 440GP Ebony" - depends on MTD_CFI && PPC32 && 440 && EBONY + tristate "Flash devices mapped on IBM 440GP Ebony" + depends on MTD_JEDECPROBE && PPC32 && 44x && EBONY help This enables access routines for the flash chips on the IBM 440GP Ebony board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_OCOTEA + tristate "Flash devices mapped on IBM 440GX Ocotea" + depends on MTD_CFI && PPC32 && 44x && OCOTEA + help + This enables access routines for the flash chips on the IBM 440GX + Ocotea board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + config MTD_REDWOOD tristate "CFI Flash devices mapped on IBM Redwood" depends on MTD_CFI && PPC32 && 4xx && 40x && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 ) @@ -374,7 +422,7 @@ tristate "Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board" depends on MIPS && MTD_CFI && MTD_JEDECPROBE && MTD_PARTITIONS help - This provides a mapping driver for the Integrated Tecnology + This provides a mapping driver for the Integrated Technology Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference Board. It provides the necessary addressing, length, buswidth, vpp code and addition setup of the flash device for @@ -448,6 +496,12 @@ the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. If you have such a board, say 'Y'. +config MTD_IPAQ + tristate "CFI Flash device mapped on Compaq/HP iPAQ" + depends on ARM && IPAQ_HANDHELD && MTD_CFI + help + This provides a driver for the on-board flash of the iPAQ. + config MTD_DC21285 tristate "CFI Flash device mapped on DC21285 Footbridge" depends on ARM && MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS @@ -468,7 +522,7 @@ tristate "CFI Flash device mapped on Intel IXP4xx based systems" depends on ARM && MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP4XX help - This eables MTD access to flash devices on platforms based + This enables MTD access to flash devices on platforms based on Intel's IXP4xx family of network processors such as the IXDP425 and Coyote. If you have an IXP4xx based board and would like to use the flash chips on it, say 'Y'. @@ -477,9 +531,9 @@ tristate "CFI Flash device mapped on Intel IXP2000 based systems" depends on ARM && MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000 help - This enables MTD access to flash devices on platforms based + This enables MTD access to flash devices on platforms based on Intel's IXP2000 family of network processors such as the - IXDP2400 and IXDP2401. If you have an IXP2000 based board and + IXDP425 and Coyote. If you have an IXP2000 based board and would like to use the flash chips on it, say 'Y'. config MTD_EPXA10DB @@ -595,6 +649,40 @@ help Map driver for Dy-4 SVME/DMV-182 board. +config MTD_BAST + tristate "Map driver for Simtec BAST (EB2410ITX) or Thorcom VR1000" + depends on ARCH_BAST || MACH_VR1000 + select MTD_PARTITIONS + select MTD_MAP_BANK_WIDTH_16 + select MTD_JEDECPROBE + help + Map driver for NOR flash on the Simtec BAST (EB2410ITX), or the + Thorcom VR1000 + + Note, this driver *cannot* over-ride the WP link on the + board, or currently detect the state of the link. + +config MTD_BAST_MAXSIZE + int "Maximum size for BAST flash area (MiB)" + depends on MTD_BAST + default "4" + +config MTD_SHARP_SL + bool "ROM maped on Sharp SL Series" + depends on MTD && ARCH_PXA + help + This enables access to the flash chip on the Sharp SL Series of PDAs. + +config MTD_PLATRAM + tristate "Map driver for platform device RAM (mtd-ram)" + depends on MTD + select MTD_RAM + help + Map driver for RAM areas described via the platform device + system. + + This selection automatically selects the map_ram driver. + config MTD_PXA27x tristate "CFI Flash device mapped on Intel PXA27x-based boards" depends on PXA27x && MTD_CFI_INTELEXT && MTD_PARTITIONS @@ -602,5 +690,13 @@ This provides a driver for the on-chip flash of the Intel PXA27x-based boards. +config MTD_PXA27x_WINDOWSIZE + int "Window size of Flash memory" + depends on MTD_PXA27x + default "64" + help + The amount of Flash memory available on the Intel PXA27x board in MB. + + endmenu diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/l440gx.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/l440gx.c --- linux-2.6.9-intc1/drivers/mtd/maps/l440gx.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/l440gx.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: l440gx.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: l440gx.c,v 1.17 2004/11/28 09:40:39 dwmw2 Exp $ * * BIOS Flash chip on Intel 440GX board. * @@ -30,7 +30,7 @@ /* Is this really the vpp port? */ -void l440gx_set_vpp(struct map_info *map, int vpp) +static void l440gx_set_vpp(struct map_info *map, int vpp) { unsigned long l; @@ -43,7 +43,7 @@ outl(l, VPP_PORT); } -struct map_info l440gx_map = { +static struct map_info l440gx_map = { .name = "L440GX BIOS", .size = WINDOW_SIZE, .bankwidth = BUSWIDTH, @@ -73,7 +73,7 @@ return -ENODEV; } - l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); + l440gx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); if (!l440gx_map.virt) { printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); @@ -103,7 +103,7 @@ /* Allocate the resource region */ if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { printk(KERN_WARNING "Could not allocate pm iobase resource\n"); - iounmap((void *)l440gx_map.virt); + iounmap(l440gx_map.virt); return -ENXIO; } } @@ -137,7 +137,7 @@ return 0; } - iounmap((void *)l440gx_map.virt); + iounmap(l440gx_map.virt); return -ENXIO; } @@ -146,7 +146,7 @@ del_mtd_device(mymtd); map_destroy(mymtd); - iounmap((void *)l440gx_map.virt); + iounmap(l440gx_map.virt); } module_init(init_l440gx); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/lasat.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/lasat.c --- linux-2.6.9-intc1/drivers/mtd/maps/lasat.c 2004-10-18 17:53:22.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/lasat.c 2005-03-21 21:28:42.000000000 -0500 @@ -7,7 +7,7 @@ * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * - * $Id: lasat.c,v 1.7 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: lasat.c,v 1.9 2004/11/04 13:24:15 gleixner Exp $ * */ @@ -50,7 +50,7 @@ ENABLE_VPP((&lasat_map)); lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); - lasat_map.virt = (unsigned long)ioremap_nocache( + lasat_map.virt = ioremap_nocache( lasat_map.phys, lasat_board_info.li_flash_size); lasat_map.size = lasat_board_info.li_flash_size; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/lubbock-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/lubbock-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/lubbock-flash.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/lubbock-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: lubbock-flash.c,v 1.18 2004/09/28 18:54:40 nico Exp $ + * $Id: lubbock-flash.c,v 1.19 2004/11/04 13:24:15 gleixner Exp $ * * Map driver for the Lubbock developer platform. * @@ -84,7 +84,7 @@ lubbock_maps[flashboot].name = "Lubbock Boot ROM"; for (i = 0; i < 2; i++) { - lubbock_maps[i].virt = (void __iomem *)ioremap(lubbock_maps[i].phys, WINDOW_SIZE); + lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE); if (!lubbock_maps[i].virt) { printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); if (!ret) diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/Makefile linux-2.6.9-intc1-mih1/drivers/mtd/maps/Makefile --- linux-2.6.9-intc1/drivers/mtd/maps/Makefile 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/Makefile 2005-03-21 22:30:01.000000000 -0500 @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.14 2004/07/12 16:07:31 dwmw2 Exp $ +# $Id: Makefile.common,v 1.28 2005/03/17 14:16:13 joern Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -10,11 +10,11 @@ # Chip mappings obj-$(CONFIG_MTD_CDB89712) += cdb89712.o obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o +obj-$(CONFIG_MTD_BAST) += bast-flash.o obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o -obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o @@ -26,14 +26,17 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +obj-$(CONFIG_MTD_MULTI_PHYSMAP) += mphysmap.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +obj-$(CONFIG_MTD_IPAQ) += ipaq-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o +obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o @@ -41,10 +44,7 @@ obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o -obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o -obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o -obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o -obj-$(CONFIG_MTD_DB1550) += db1550-flash.o +obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o obj-$(CONFIG_MTD_LASAT) += lasat.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o @@ -55,8 +55,10 @@ obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_EBONY) += ebony.o +obj-$(CONFIG_MTD_OCOTEA) += ocotea.o obj-$(CONFIG_MTD_BEECH) += beech-mtd.o obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o +obj-$(CONFIG_MTD_WALNUT) += walnut.o obj-$(CONFIG_MTD_H720X) += h720x-flash.o obj-$(CONFIG_MTD_SBC8240) += sbc8240.o obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o @@ -65,4 +67,6 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_DMV182) += dmv182.o -obj-$(CONFIG_MTD_PXA27x) += pxa27x-flash.o +obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o +obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o +obj-$(CONFIG_MTD_PXA27x) += pxa27x-flash.o \ Kein Zeilenumbruch am Dateiende. diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/mbx860.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/mbx860.c --- linux-2.6.9-intc1/drivers/mtd/maps/mbx860.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/mbx860.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mbx860.c,v 1.6 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: mbx860.c,v 1.8 2004/11/04 13:24:15 gleixner Exp $ * * Handle mapping of the flash on MBX860 boards * @@ -60,7 +60,7 @@ int __init init_mbx(void) { printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); - mbx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); if (!mbx_map.virt) { printk("Failed to ioremap\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/mpc1211.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/mpc1211.c --- linux-2.6.9-intc1/drivers/mtd/maps/mpc1211.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/mpc1211.c 2005-03-21 21:28:43.000000000 -0500 @@ -1,7 +1,7 @@ /* * Flash on MPC-1211 * - * $Id: mpc1211.c,v 1.3 2004/07/14 17:45:40 dwmw2 Exp $ + * $Id: mpc1211.c,v 1.4 2004/09/16 23:27:13 gleixner Exp $ * * (C) 2002 Interface, Saito.K & Jeanne * @@ -44,7 +44,7 @@ int nr_parts; mpc1211_flash_map.phys = 0; - mpc1211_flash_map.virt = P2SEGADDR(0); + mpc1211_flash_map.virt = (void __iomem *)P2SEGADDR(0); simple_map_init(&mpc1211_flash_map); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/mphysmap.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/mphysmap.c --- linux-2.6.9-intc1/drivers/mtd/maps/mphysmap.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/mphysmap.c 2005-03-21 21:28:42.000000000 -0500 @@ -0,0 +1,282 @@ +/* + * $Id: mphysmap.c,v 1.2 2005/03/07 23:15:48 joern Exp $ + * + * Several mappings of NOR chips + * + * Copyright (c) 2001-2005 Jörn Engel + */ +#include +#include +#include +#include + + +#define NO_DEVICES 8 +struct map_info maps[NO_DEVICES] = { +#if CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_1_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_1_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_1_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH, + }, +#endif +#if CONFIG_MTD_MULTI_PHYSMAP_2_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_2_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_2_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_2_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_2_WIDTH, + }, +#endif +#if CONFIG_MTD_MULTI_PHYSMAP_3_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_3_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_3_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_3_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_3_WIDTH, + }, +#endif +#if CONFIG_MTD_MULTI_PHYSMAP_4_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_4_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_4_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_4_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_4_WIDTH, + }, +#endif + { + .name = NULL, + }, +}; +DECLARE_MUTEX(map_mutex); + + +static int map_one(struct map_info *map) +{ + struct mtd_info *mtd; + + map->virt = ioremap(map->phys, map->size); + if (!map->virt) + return -EIO; + + simple_map_init(map); + + mtd = do_map_probe("cfi_probe", map); + if (!mtd) { + iounmap(map->virt); + return -ENXIO; + } + + map->map_priv_1 = (unsigned long)mtd; + mtd->owner = THIS_MODULE; + /* TODO: partitioning */ + return add_mtd_device(mtd); +} + + +static void unmap_one(struct map_info *map) +{ + struct mtd_info *mtd = (struct mtd_info*)map->map_priv_1; + + if (map->map_priv_2) + kfree(map->name); + + if (!map->virt) + return; + + BUG_ON(!mtd); + BUG_ON(map->map_priv_2 > 1); + + del_mtd_device(mtd); + map_destroy(mtd); + iounmap(map->virt); + + map->map_priv_1 = 0; + map->map_priv_2 = 0; + map->virt = NULL; +} + + +static struct map_info *next_free_map(void) +{ + int i; + for (i=0; ivirt) + return map; + } + return NULL; +} + + +static int add_one_map(const char *name, unsigned long start, + unsigned long len, int width) +{ + struct map_info *map = next_free_map(); + if (!map) + return -ENOSPC; + + map->name = kmalloc(strlen(name)+1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(map->name, name); + map->phys = start; + map->size = len; + map->bankwidth = width; + map->map_priv_2 = 1; /* marker to free map->name */ + + return map_one(map); +} + + +static int parse_ulong(unsigned long *num, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + + *num = n; + return 0; +} + + +static int parse_uint(unsigned int *num, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + if ((int)n != n) + return -EINVAL; + + *num = n; + return 0; +} + + +static int parse_name(char **pname, const char *token, int limit) +{ + size_t len; + char *name; + + len = strlen(token) + 1; + if (len > limit) + return -ENOSPC; + + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + + memcpy(name, token, len); + + *pname = name; + return 0; +} + + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + +#define parse_err(fmt, args...) do { \ + printk(KERN_ERR fmt "\n", ## args); \ + return 0; \ +} while(0) + +/* mphysmap=name,start,len,width */ +static int mphysmap_setup(const char *val, struct kernel_param *kp) +{ + char buf[64+14+14+14], *str = buf; + char *token[4]; + char *name; + unsigned long start; + unsigned long len; + unsigned int width; + int i, ret; + + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) + parse_err("parameter too long"); + + strcpy(str, val); + kill_final_newline(str); + + for (i=0; i<4; i++) + token[i] = strsep(&str, ","); + + if (str) + parse_err("too many arguments"); + if (!token[3]) + parse_err("not enough arguments"); + + ret = parse_name(&name, token[0], 64); + if (ret == -ENOMEM) + parse_err("out of memory"); + if (ret == -ENOSPC) + parse_err("name too long"); + if (ret) + parse_err("illegal name: %d", ret); + + ret = parse_ulong(&start, token[1]); + if (ret) + parse_err("illegal start address"); + + ret = parse_ulong(&len, token[2]); + if (ret) + parse_err("illegal length"); + + ret = parse_uint(&width, token[3]); + if (ret) + parse_err("illegal bus width"); + + down(&map_mutex); + ret = add_one_map(name, start, len, width); + up(&map_mutex); + if (ret == -ENOSPC) + parse_err("no free space for new map"); + if (ret) + parse_err("error while mapping: %d", ret); + + return 0; +} + + +static int __init mphysmap_init(void) +{ + int i; + down(&map_mutex); + for (i=0; i"); +MODULE_DESCRIPTION("Generic configurable extensible MTD map driver"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/netsc520.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/netsc520.c --- linux-2.6.9-intc1/drivers/mtd/maps/netsc520.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/netsc520.c 2005-03-21 21:28:42.000000000 -0500 @@ -3,7 +3,7 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.10 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: netsc520.c,v 1.13 2004/11/28 09:40:40 dwmw2 Exp $ * * 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 @@ -95,7 +95,7 @@ static int __init init_netsc520(void) { printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); - netsc520_map.virt = (unsigned long)ioremap_nocache(netsc520_map.phys, netsc520_map.size); + netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); if (!netsc520_map.virt) { printk("Failed to ioremap_nocache\n"); @@ -111,7 +111,7 @@ mymtd = do_map_probe("map_rom", &netsc520_map); if (!mymtd) { - iounmap((void *)netsc520_map.virt); + iounmap(netsc520_map.virt); return -ENXIO; } @@ -127,8 +127,8 @@ map_destroy(mymtd); } if (netsc520_map.virt) { - iounmap((void *)netsc520_map.virt); - netsc520_map.virt = 0; + iounmap(netsc520_map.virt); + netsc520_map.virt = NULL; } } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/nettel.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/nettel.c --- linux-2.6.9-intc1/drivers/mtd/maps/nettel.c 2004-10-18 17:53:44.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/nettel.c 2005-03-21 21:28:42.000000000 -0500 @@ -6,7 +6,7 @@ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) * - * $Id: nettel.c,v 1.5 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: nettel.c,v 1.10 2005/01/05 17:11:29 dwmw2 Exp $ */ /****************************************************************************/ @@ -273,8 +273,7 @@ __asm__ ("wbinvd"); nettel_amd_map.phys = amdaddr; - nettel_amd_map.virt = (unsigned long) - ioremap_nocache(amdaddr, maxsize); + nettel_amd_map.virt = ioremap_nocache(amdaddr, maxsize); if (!nettel_amd_map.virt) { printk("SNAPGEAR: failed to ioremap() BOOTCS\n"); return(-EIO); @@ -333,8 +332,8 @@ /* Destroy useless AMD MTD mapping */ amd_mtd = NULL; - iounmap((void *) nettel_amd_map.virt); - nettel_amd_map.virt = (unsigned long) NULL; + iounmap(nettel_amd_map.virt); + nettel_amd_map.virt = NULL; #else /* Only AMD flash supported */ return(-ENXIO); @@ -358,8 +357,7 @@ /* Probe for the the size of the first Intel flash */ nettel_intel_map.size = maxsize; nettel_intel_map.phys = intel0addr; - nettel_intel_map.virt = (unsigned long) - ioremap_nocache(intel0addr, maxsize); + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); return(-EIO); @@ -367,8 +365,8 @@ simple_map_init(&nettel_intel_map); intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); - if (! intel_mtd) { - iounmap((void *) nettel_intel_map.virt); + if (!intel_mtd) { + iounmap(nettel_intel_map.virt); return(-ENXIO); } @@ -389,11 +387,10 @@ /* Delete the old map and probe again to do both chips */ map_destroy(intel_mtd); intel_mtd = NULL; - iounmap((void *) nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); nettel_intel_map.size = maxsize; - nettel_intel_map.virt = (unsigned long) - ioremap_nocache(intel0addr, maxsize); + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); return(-EIO); @@ -472,8 +469,8 @@ map_destroy(amd_mtd); } if (nettel_amd_map.virt) { - iounmap((void *)nettel_amd_map.virt); - nettel_amd_map.virt = 0; + iounmap(nettel_amd_map.virt); + nettel_amd_map.virt = NULL; } #ifdef CONFIG_MTD_CFI_INTELEXT if (intel_mtd) { @@ -481,7 +478,7 @@ map_destroy(intel_mtd); } if (nettel_intel_map.virt) { - iounmap((void *)nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); nettel_intel_map.virt = 0; } #endif diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ocelot.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ocelot.c --- linux-2.6.9-intc1/drivers/mtd/maps/ocelot.c 2004-10-18 17:55:28.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ocelot.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: ocelot.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: ocelot.c,v 1.16 2005/01/05 18:05:13 dwmw2 Exp $ * * Flash on Momenco Ocelot */ @@ -28,7 +28,7 @@ static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; size_t done = 0; /* If we use memcpy, it does word-wide writes. Even though we told the @@ -81,7 +81,7 @@ iounmap(pld); /* Now ioremap the NVRAM space */ - ocelot_nvram_map.virt = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); + ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); if (!ocelot_nvram_map.virt) { printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); return -EIO; @@ -101,7 +101,7 @@ nvram_mtd->write = ocelot_ram_write; /* Now map the flash space */ - ocelot_flash_map.virt = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); + ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); if (!ocelot_flash_map.virt) { printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); goto fail_2; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ocotea.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ocotea.c --- linux-2.6.9-intc1/drivers/mtd/maps/ocotea.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ocotea.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,154 @@ +/* + * Mapping for Ocotea user flash + * + * Matt Porter + * + * Copyright 2002-2004 MontaVista Software 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_info *flash; + +static struct map_info ocotea_small_map = { + .name = "Ocotea small flash", + .size = OCOTEA_SMALL_FLASH_SIZE, + .buswidth = 1, +}; + +static struct map_info ocotea_large_map = { + .name = "Ocotea large flash", + .size = OCOTEA_LARGE_FLASH_SIZE, + .buswidth = 1, +}; + +static struct mtd_partition ocotea_small_partitions[] = { + { + .name = "pibs", + .offset = 0x0, + .size = 0x100000, + } +}; + +static struct mtd_partition ocotea_large_partitions[] = { + { + .name = "fs", + .offset = 0, + .size = 0x300000, + }, + { + .name = "firmware", + .offset = 0x300000, + .size = 0x100000, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_ocotea(void) +{ + u8 fpga0_reg; + u8 *fpga0_adr; + unsigned long long small_flash_base, large_flash_base; + + fpga0_adr = ioremap64(OCOTEA_FPGA_ADDR, 16); + if (!fpga0_adr) + return -ENOMEM; + + fpga0_reg = readb((unsigned long)fpga0_adr); + iounmap(fpga0_adr); + + if (OCOTEA_BOOT_LARGE_FLASH(fpga0_reg)) { + small_flash_base = OCOTEA_SMALL_FLASH_HIGH; + large_flash_base = OCOTEA_LARGE_FLASH_LOW; + } + else { + small_flash_base = OCOTEA_SMALL_FLASH_LOW; + large_flash_base = OCOTEA_LARGE_FLASH_HIGH; + } + + ocotea_small_map.phys = small_flash_base; + ocotea_small_map.virt = ioremap64(small_flash_base, + ocotea_small_map.size); + + if (!ocotea_small_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ocotea_small_map); + + flash = do_map_probe("map_rom", &ocotea_small_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ocotea_small_partitions, + NB_OF(ocotea_small_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + ocotea_large_map.phys = large_flash_base; + ocotea_large_map.virt = ioremap64(large_flash_base, + ocotea_large_map.size); + + if (!ocotea_large_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ocotea_large_map); + + flash = do_map_probe("cfi_probe", &ocotea_large_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ocotea_large_partitions, + NB_OF(ocotea_large_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_ocotea(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (ocotea_small_map.virt) { + iounmap((void *)ocotea_small_map.virt); + ocotea_small_map.virt = 0; + } + + if (ocotea_large_map.virt) { + iounmap((void *)ocotea_large_map.virt); + ocotea_large_map.virt = 0; + } +} + +module_init(init_ocotea); +module_exit(cleanup_ocotea); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 440GX Ocotea boards"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/octagon-5066.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/octagon-5066.c --- linux-2.6.9-intc1/drivers/mtd/maps/octagon-5066.c 2004-10-18 17:53:22.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/octagon-5066.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.26 2004/07/12 22:38:29 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.27 2005/01/12 22:34:35 gleixner Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -41,7 +41,7 @@ static volatile char page_n_dev = 0; static unsigned long iomapadr; -static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(oct5066_spin); /* * We use map_priv_1 to identify which device we are. diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/omap-toto-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/omap-toto-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/omap-toto-flash.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/omap-toto-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -5,7 +5,7 @@ * * (C) 2002 MontVista Software, Inc. * - * $Id: omap-toto-flash.c,v 1.2 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: omap-toto-flash.c,v 1.3 2004/09/16 23:27:13 gleixner Exp $ */ #include @@ -35,7 +35,7 @@ static struct map_info omap_toto_map_flash = { .name = "OMAP Toto flash", .bankwidth = 2, - .virt = OMAP_TOTO_FLASH_BASE, + .virt = (void __iomem *)OMAP_TOTO_FLASH_BASE, }; diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/pci.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/pci.c --- linux-2.6.9-intc1/drivers/mtd/maps/pci.c 2004-10-18 17:53:51.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/pci.c 2005-03-21 21:28:42.000000000 -0500 @@ -7,7 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.8 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: pci.c,v 1.10 2005/03/18 14:04:35 gleixner Exp $ * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. @@ -33,7 +33,7 @@ struct map_pci_info { struct map_info map; - void *base; + void __iomem *base; void (*exit)(struct pci_dev *dev, struct map_pci_info *map); unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs); struct pci_dev *dev; @@ -143,7 +143,7 @@ intel_iq80310_exit(struct pci_dev *dev, struct map_pci_info *map) { if (map->base) - iounmap((void *)map->base); + iounmap(map->base); pci_write_config_dword(dev, 0x44, map->map.map_priv_2); } @@ -236,7 +236,7 @@ u32 val; if (map->base) - iounmap((void *)map->base); + iounmap(map->base); /* * We need to undo the PCI BAR2/PCI ROM BAR address alteration. @@ -370,7 +370,7 @@ static int __init mtd_pci_maps_init(void) { - return pci_module_init(&mtd_pci_driver); + return pci_register_driver(&mtd_pci_driver); } static void __exit mtd_pci_maps_exit(void) diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/pcmciamtd.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/pcmciamtd.c --- linux-2.6.9-intc1/drivers/mtd/maps/pcmciamtd.c 2004-10-18 17:53:21.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/pcmciamtd.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: pcmciamtd.c,v 1.51 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: pcmciamtd.c,v 1.52 2005/03/17 20:05:37 gleixner Exp $ * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * @@ -49,7 +49,7 @@ #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.51 $" +#define DRIVER_VERSION "$Revision: 1.52 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -800,7 +800,6 @@ /* Register with Card Services */ client_reg.dev_info = &dev_info; - client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | @@ -854,35 +853,7 @@ { DEBUG(1, DRIVER_DESC " unloading"); pcmcia_unregister_driver(&pcmciamtd_driver); - - while(dev_list) { - dev_link_t *link = dev_list; - - dev_list = link->next; - if (link) { - struct pcmciamtd_dev *dev = link->priv; - - if(dev) { - if(link->state & DEV_PRESENT) { - if (!(link->state & DEV_STALE_LINK)) { - pcmciamtd_detach(link); - } - link->state &= ~DEV_PRESENT; - if(dev->mtd_info) { - del_mtd_device(dev->mtd_info); - info("mtd%d: Removed", - dev->mtd_info->index); - } - } - if(dev->mtd_info) { - DEBUG(2, "Destroying map for mtd%d", - dev->mtd_info->index); - map_destroy(dev->mtd_info); - } - kfree(dev); - } - } - } + BUG_ON(dev_list != NULL); } module_init(init_pcmciamtd); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/physmap.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/physmap.c --- linux-2.6.9-intc1/drivers/mtd/maps/physmap.c 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/physmap.c 2005-04-19 15:02:37.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Id: physmap.c,v 1.34 2004/07/21 00:16:14 jwboyer Exp $ + * $Id: physmap.c,v 1.37 2004/11/28 09:40:40 dwmw2 Exp $ * * Normal mappings of chips in physical memory * @@ -51,7 +51,7 @@ const char **type; printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); - physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size); + physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size); if (!physmap_map.virt) { printk("Failed to ioremap\n"); @@ -68,6 +68,8 @@ if (mymtd) { mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + #ifdef CONFIG_MTD_PARTITIONS mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, &mtd_parts, 0); @@ -87,12 +89,11 @@ } #endif - add_mtd_device(mymtd); return 0; } - iounmap((void *)physmap_map.virt); + iounmap(physmap_map.virt); return -ENXIO; } @@ -112,8 +113,8 @@ #endif map_destroy(mymtd); - iounmap((void *)physmap_map.virt); - physmap_map.virt = 0; + iounmap(physmap_map.virt); + physmap_map.virt = NULL; } module_init(init_physmap); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/plat-ram.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/plat-ram.c --- linux-2.6.9-intc1/drivers/mtd/maps/plat-ram.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/plat-ram.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,278 @@ +/* drivers/mtd/maps/plat-ram.c + * + * (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks + * + * Generic platfrom device based RAM map + * + * $Id: plat-ram.c,v 1.3 2005/03/19 22:41:27 gleixner Exp $ + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* private structure for each mtd platform ram device created */ + +struct platram_info { + struct device *dev; + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *area; + struct platdata_mtd_ram *pdata; +}; + +/* to_platram_info() + * + * device private data to struct platram_info conversion +*/ + +static inline struct platram_info *to_platram_info(struct device *dev) +{ + return (struct platram_info *)dev_get_drvdata(dev); +} + +/* platram_setrw + * + * call the platform device's set rw/ro control + * + * to = 0 => read-only + * = 1 => read-write +*/ + +static inline void platram_setrw(struct platram_info *info, int to) +{ + if (info->pdata == NULL) + return; + + if (info->pdata->set_rw != NULL) + (info->pdata->set_rw)(info->dev, to); +} + +/* platram_remove + * + * called to remove the device from the driver's control +*/ + +static int platram_remove(struct device *dev) +{ + struct platram_info *info = to_platram_info(dev); + + dev_set_drvdata(dev, NULL); + + dev_dbg(dev, "removing device\n"); + + if (info == NULL) + return 0; + + if (info->mtd) { +#ifdef CONFIG_MTD_PARTITIONS + if (info->partitions) { + del_mtd_partitions(info->mtd); + kfree(info->partitions); + } +#endif + del_mtd_device(info->mtd); + map_destroy(info->mtd); + } + + /* ensure ram is left read-only */ + + platram_setrw(info, PLATRAM_RO); + + /* release resources */ + + if (info->area) { + release_resource(info->area); + kfree(info->area); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + kfree(info); + + return 0; +} + +/* platram_probe + * + * called from device drive system when a device matching our + * driver is found. +*/ + +static int platram_probe(struct device *dev) +{ + struct platform_device *pd = to_platform_device(dev); + struct platdata_mtd_ram *pdata; + struct platram_info *info; + struct resource *res; + int err = 0; + + dev_dbg(dev, "probe entered\n"); + + if (dev->platform_data == NULL) { + dev_err(dev, "no platform data supplied\n"); + err = -ENOENT; + goto exit_error; + } + + pdata = dev->platform_data; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(dev, "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memset(info, 0, sizeof(*info)); + dev_set_drvdata(dev, info); + + info->dev = dev; + info->pdata = pdata; + + /* get the resource for the memory mapping */ + + res = platform_get_resource(pd, IORESOURCE_MEM, 0); + + if (res == NULL) { + dev_err(dev, "no memory resource specified\n"); + err = -ENOENT; + goto exit_free; + } + + dev_dbg(dev, "got platform resource %p (0x%lx)\n", res, res->start); + + /* setup map parameters */ + + info->map.phys = res->start; + info->map.size = (res->end - res->start) + 1; + info->map.name = pdata->mapname != NULL ? pdata->mapname : pd->name; + info->map.bankwidth = pdata->bankwidth; + + /* register our usage of the memory area */ + + info->area = request_mem_region(res->start, info->map.size, pd->name); + if (info->area == NULL) { + dev_err(dev, "failed to request memory region\n"); + err = -EIO; + goto exit_free; + } + + /* remap the memory area */ + + info->map.virt = ioremap(res->start, info->map.size); + dev_dbg(dev, "virt %p, %lu bytes\n", info->map.virt, info->map.size); + + if (info->map.virt == NULL) { + dev_err(dev, "failed to ioremap() region\n"); + err = -EIO; + goto exit_free; + } + + simple_map_init(&info->map); + + dev_dbg(dev, "initialised map, probing for mtd\n"); + + /* probe for the right mtd map driver */ + + info->mtd = do_map_probe("map_ram" , &info->map); + if (info->mtd == NULL) { + dev_err(dev, "failed to probe for map_ram\n"); + err = -ENOMEM; + goto exit_free; + } + + info->mtd->owner = THIS_MODULE; + + platram_setrw(info, PLATRAM_RW); + + /* check to see if there are any available partitions, or wether + * to add this device whole */ + +#ifdef CONFIG_MTD_PARTITIONS + if (pdata->nr_partitions > 0) { + const char **probes = { NULL }; + + if (pdata->probes) + probes = (const char **)pdata->probes; + + err = parse_mtd_partitions(info->mtd, probes, + &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, + err); + } + } +#endif /* CONFIG_MTD_PARTITIONS */ + + if (add_mtd_device(info->mtd)) { + dev_err(dev, "add_mtd_device() failed\n"); + err = -ENOMEM; + } + + dev_info(dev, "registered mtd device\n"); + return err; + + exit_free: + platram_remove(dev); + exit_error: + return err; +} + +/* device driver info */ + +static struct device_driver platram_driver = { + .name = "mtd-ram", + .bus = &platform_bus_type, + .probe = platram_probe, + .remove = platram_remove, +}; + +/* module init/exit */ + +static int __init platram_init(void) +{ + printk("Generic platform RAM MTD, (c) 2004 Simtec Electronics\n"); + return driver_register(&platram_driver); +} + +static void __exit platram_exit(void) +{ + driver_unregister(&platram_driver); +} + +module_init(platram_init); +module_exit(platram_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("MTD platform RAM map driver"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/pnc2000.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/pnc2000.c --- linux-2.6.9-intc1/drivers/mtd/maps/pnc2000.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/pnc2000.c 2005-03-21 21:28:42.000000000 -0500 @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: pnc2000.c,v 1.15 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: pnc2000.c,v 1.17 2004/11/16 18:29:02 dwmw2 Exp $ */ #include @@ -26,12 +26,12 @@ */ -struct map_info pnc_map = { +static struct map_info pnc_map = { .name = "PNC-2000", .size = WINDOW_SIZE, .bankwidth = 4, .phys = 0xFFFFFFFF, - .virt = WINDOW_ADDR, + .virt = (void __iomem *)WINDOW_ADDR, }; @@ -62,7 +62,7 @@ */ static struct mtd_info *mymtd; -int __init init_pnc2000(void) +static int __init init_pnc2000(void) { printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/pxa27x-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/pxa27x-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/pxa27x-flash.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/pxa27x-flash.c 2005-05-01 00:34:51.000000000 -0400 @@ -4,6 +4,7 @@ * Modified from linux/drivers/mtd/maps/lubbock-flash.c * * Copyright (C) 2004, Intel Corporation. + * Modified and extended by Matthias Ihmig * * 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 @@ -11,6 +12,7 @@ */ #include +#include #include #include #include @@ -24,41 +26,73 @@ #include #include +// Size of complete available flash memory in bytes +#define WINDOW_SIZE (CONFIG_MTD_PXA27x_WINDOWSIZE * 1024 * 1024) -#define WINDOW_SIZE 64*1024*1024 +/* Size settings (in bytes) and calculations for default flash partition table */ +#define BLOB_FLASH_LEN (256 * 1024) +#define KERNEL_FLASH_LEN (2 * 1024 * 1024) +// RAMDISK_FLASH_LEN takes up the remainder and is calculated a few lines later +#define PARAM_FLASH_LEN (256 * 1024) + +#define BLOB_FLASH_BASE (0x00000000) +#define KERNEL_FLASH_BASE (BLOB_FLASH_BASE + BLOB_FLASH_LEN) +#define RAMDISK_FLASH_BASE (KERNEL_FLASH_BASE + KERNEL_FLASH_LEN) + +#define RAMDISK_FLASH_LEN (WINDOW_SIZE - KERNEL_FLASH_LEN - PARAM_FLASH_LEN - BLOB_FLASH_LEN) + +#define PARAM_FLASH_BASE (RAMDISK_FLASH_BASE + RAMDISK_FLASH_LEN) + + +/* Uncomment define to use cached memory access to flash + * + * This should not be used, as mtdblock already does caching thus + * making caching on this level unnecessary! + * + * Also, data may be lost in case of a sudden power-off (which may occur + * esp. in mobile devices + */ +//#define USE_CACHED_FLASH struct pxa27x_flash_info { struct mtd_info *mtd; - struct mtd_partition * part; + struct mtd_partition *part; struct map_info *map; int nparts; }; +#ifdef USE_CACHED_FLASH static void pxa27x_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) { consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); } +#endif +// don't use spaces within the name or else the cmdline partition table parser won't work static struct map_info pxa27x_map = { .size = WINDOW_SIZE, .phys = 0x00000000, - .inval_cache = pxa27x_map_inval_cache, + .name = "pxa27x-flash" }; static struct mtd_partition pxa27x_partitions[] = { { - .name = "Bootloader", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE /* force read-only */ + name: "Bootloader", + size: BLOB_FLASH_LEN, + offset: BLOB_FLASH_BASE, +// mask_flags: MTD_WRITEABLE /* uncomment to enforce read-only partition */ },{ - .name = "Kernel", - .size = 0x00200000, - .offset = 0x00040000, + name: "Kernel", + size: KERNEL_FLASH_LEN, + offset: KERNEL_FLASH_BASE },{ - .name = "Filesystem", - .size = 0x01000000, - .offset = 0x00240000 + name: "Filesystem", + size: RAMDISK_FLASH_LEN, + offset: RAMDISK_FLASH_BASE + } ,{ + name: "Param", + size: PARAM_FLASH_LEN, + offset: PARAM_FLASH_BASE } }; @@ -84,18 +118,22 @@ dev_set_drvdata(dev, info); pxa27x_map.bankwidth = (BOOT_DEF & 1) ? 2 : 4; + pxa27x_map.cached = NULL; - pxa27x_map.name = "Intel PXA27x On-Chip ROM"; - - pxa27x_map.virt = (unsigned long)ioremap(pxa27x_map.phys, WINDOW_SIZE); + pxa27x_map.virt = ioremap(pxa27x_map.phys, pxa27x_map.size); if (!pxa27x_map.virt) { printk(KERN_WARNING "Failed to ioremap %s\n", pxa27x_map.name); err = -ENOMEM; goto Error; } +#ifdef USE_CACHED_FLASH + pxa27x_map.inval_cache = pxa27x_map_inval_cache; pxa27x_map.cached = __ioremap(pxa27x_map.phys, WINDOW_SIZE, L_PTE_CACHEABLE, 1); if (!pxa27x_map.cached) printk(KERN_WARNING "Failed to ioremap cached %s\n", pxa27x_map.name); + else + printk(KERN_INFO "Using cached access to flash memory! Do you really want to do this?\n"); +#endif simple_map_init(&pxa27x_map); @@ -111,6 +149,7 @@ err = -EIO; goto Error; } + mymtd->owner = THIS_MODULE; /* Unlock the flash device. */ for (i = 0; i < mymtd->numeraseregions; i++) { @@ -122,7 +161,6 @@ } } - mymtd->owner = THIS_MODULE; ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); @@ -154,10 +192,15 @@ if (!mymtd) return 0; - del_mtd_partitions(mymtd); + if (nr_parsed_part) + del_mtd_partitions(mymtd); + else + del_mtd_device(mymtd); + map_destroy(mymtd); - iounmap((void *)pxa27x_map.virt); + if (pxa27x_map.virt) + iounmap((void *)pxa27x_map.virt); if (pxa27x_map.cached) iounmap(pxa27x_map.cached); @@ -200,7 +243,6 @@ } } } - return 0; } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/redwood.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/redwood.c --- linux-2.6.9-intc1/drivers/mtd/maps/redwood.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/redwood.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: redwood.c,v 1.8 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: redwood.c,v 1.10 2004/11/04 13:24:15 gleixner Exp $ * * drivers/mtd/maps/redwood.c * @@ -131,8 +131,7 @@ printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - redwood_flash_map.virt = - (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + redwood_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!redwood_flash_map.virt) { printk("init_redwood_flash: failed to ioremap\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/rpxlite.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/rpxlite.c --- linux-2.6.9-intc1/drivers/mtd/maps/rpxlite.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/rpxlite.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: rpxlite.c,v 1.20 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $ * * Handle mapping of the flash on the RPX Lite and CLLF boards */ @@ -28,7 +28,7 @@ int __init init_rpxlite(void) { printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); - rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); if (!rpxlite_map.virt) { printk("Failed to ioremap\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/sa1100-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/sa1100-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/sa1100-flash.c 2004-10-18 17:53:44.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/sa1100-flash.c 2005-03-21 21:28:42.000000000 -0500 @@ -3,9 +3,8 @@ * * (C) 2000 Nicolas Pitre * - * $Id: sa1100-flash.c,v 1.39 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: sa1100-flash.c,v 1.48 2005/03/18 02:01:48 gleixner Exp $ */ - #include #include #include @@ -14,1339 +13,440 @@ #include #include #include +#include +#include #include #include #include #include -#include #include #include #include +#include -#include - -#ifndef CONFIG_ARCH_SA1100 -#error This is for SA1100 architecture only -#endif - -/* - * This isnt complete yet, so... - */ -#define CONFIG_MTD_SA1100_STATICMAP 1 - -#ifdef CONFIG_MTD_SA1100_STATICMAP -/* - * Here are partition information for all known SA1100-based devices. - * See include/linux/mtd/partitions.h for definition of the mtd_partition - * structure. - * - * Please note: - * 1. We no longer support static flash mappings via the machine io_desc - * structure. - * 2. The flash size given should be the largest flash size that can - * be accommodated. - * - * The MTD layer will detect flash chip aliasing and reduce the size of - * the map accordingly. - * - * Please keep these in alphabetical order, and formatted as per existing - * entries. Thanks. - */ - -#ifdef CONFIG_SA1100_ADSBITSY -static struct mtd_partition adsbitsy_partitions[] = { - { - .name = "bootROM", - .size = 0x80000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "zImage", - .size = 0x100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "ramdisk.gz", - .size = 0x300000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "User FS", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_ASSABET -/* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ -static struct mtd_partition assabet4_partitions[] = { - { - .name = "bootloader", - .size = 0x00020000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "bootloader params", - .size = 0x00020000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "jffs", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; - -/* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ -static struct mtd_partition assabet5_partitions[] = { - { - .name = "bootloader", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "bootloader params", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "jffs", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; - -#define assabet_partitions assabet5_partitions -#endif - -#ifdef CONFIG_SA1100_BADGE4 +#if 0 /* - * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) - * Eight 4 KiW Parameter Bottom Blocks (64 KiB) - * Sixty-three 32 KiW Main Blocks (4032 Ki b) - * - * - * - * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) - * Eight 4 KiW Parameter Bottom Blocks (64 KiB) - * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) + * This is here for documentation purposes only - until these people + * submit their machine types. It will be gone January 2005. */ -static struct mtd_partition badge4_partitions[] = { - { - .name = "BLOB boot loader", - .offset = 0, - .size = 0x0000A000 - }, { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = 0x00006000 - }, { - .name = "root", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL - } -}; -#endif - - -#ifdef CONFIG_SA1100_CERF -#ifdef CONFIG_SA1100_CERF_FLASH_32MB -# define CERF_FLASH_SIZE 0x02000000 -#elif defined CONFIG_SA1100_CERF_FLASH_16MB -# define CERF_FLASH_SIZE 0x01000000 -#elif defined CONFIG_SA1100_CERF_FLASH_8MB -# define CERF_FLASH_SIZE 0x00800000 -#else -# error "Undefined flash size for CERF in sa1100-flash.c" -#endif - -static struct mtd_partition cerf_partitions[] = { - { - .name = "Bootloader", - .size = 0x00020000, - .offset = 0x00000000, - }, { - .name = "Params", - .size = 0x00040000, - .offset = 0x00020000, - }, { - .name = "Kernel", - .size = 0x00100000, - .offset = 0x00060000, - }, { - .name = "Filesystem", - .size = CERF_FLASH_SIZE-0x00160000, - .offset = 0x00160000, - } -}; -#endif - -#ifdef CONFIG_SA1100_CONSUS static struct mtd_partition consus_partitions[] = { { - .name = "Consus boot firmware", - .offset = 0, - .size = 0x00040000, - .mask_flags = MTD_WRITABLE, /* force read-only */ - }, { - .name = "Consus kernel", - .offset = 0x00040000, - .size = 0x00100000, - .mask_flags = 0, - }, { - .name = "Consus disk", - .offset = 0x00140000, - /* The rest (up to 16M) for jffs. We could put 0 and - make it find the size automatically, but right now - i have 32 megs. jffs will use all 32 megs if given - the chance, and this leads to horrible problems - when you try to re-flash the image because blob - won't erase the whole partition. */ - .size = 0x01000000 - 0x00140000, - .mask_flags = 0, - }, { - /* this disk is a secondary disk, which can be used as - needed, for simplicity, make it the size of the other - consus partition, although realistically it could be - the remainder of the disk (depending on the file - system used) */ - .name = "Consus disk2", - .offset = 0x01000000, - .size = 0x01000000 - 0x00140000, - .mask_flags = 0, - } -}; -#endif - -#ifdef CONFIG_SA1100_FLEXANET -/* Flexanet has two 28F128J3A flash parts in bank 0: */ -#define FLEXANET_FLASH_SIZE 0x02000000 -static struct mtd_partition flexanet_partitions[] = { - { - .name = "bootloader", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "bootloader params", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "kernel", - .size = 0x000C0000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "altkernel", - .size = 0x000C0000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "root", - .size = 0x00400000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "free1", - .size = 0x00300000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "free2", - .size = 0x00300000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "free3", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, - } -}; -#endif - -#ifdef CONFIG_SA1100_FREEBIRD -static struct mtd_partition freebird_partitions[] = { -#ifdef CONFIG_SA1100_FREEBIRD_NEW - { - .name = "firmware", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "kernel", - .size = 0x00080000, - .offset = 0x00040000, - }, { - .name = "params", - .size = 0x00040000, - .offset = 0x000C0000, - }, { - .name = "initrd", - .size = 0x00100000, - .offset = 0x00100000, - }, { - .name = "root cramfs", - .size = 0x00300000, - .offset = 0x00200000, - }, { - .name = "usr cramfs", - .size = 0x00C00000, - .offset = 0x00500000, - }, { - .name = "local", - .size = MTDPART_SIZ_FULL, - .offset = 0x01100000, - } -#else - { - .size = 0x00040000, - .offset = 0, - }, { - .size = 0x000c0000, - .offset = MTDPART_OFS_APPEND, - }, { - .size = 0x00400000, - .offset = MTDPART_OFS_APPEND, - }, { - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -#endif -}; -#endif - -#ifdef CONFIG_SA1100_FRODO -/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ -static struct mtd_partition frodo_partitions[] = -{ - { - .name = "bootloader", - .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE - }, { - .name = "bootloader params", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "ramdisk", - .size = 0x00400000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - }, { - .name = "file system", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND - } -}; -#endif - -#ifdef CONFIG_SA1100_GRAPHICSCLIENT -static struct mtd_partition graphicsclient_partitions[] = { - { - .name = "zImage", - .size = 0x100000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "ramdisk.gz", - .size = 0x300000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "User FS", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_GRAPHICSMASTER -static struct mtd_partition graphicsmaster_partitions[] = { - { - .name = "zImage", - .size = 0x100000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, - { - .name = "ramdisk.gz", - .size = 0x300000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, - { - .name = "User FS", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_H3XXX -static struct mtd_partition h3xxx_partitions[] = { - { - .name = "H3XXX boot firmware", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { -#ifdef CONFIG_MTD_2PARTS_IPAQ - .name = "H3XXX root jffs2", - .size = MTDPART_SIZ_FULL, - .offset = 0x00040000, -#else - .name = "H3XXX kernel", - .size = 0x00080000, - .offset = 0x00040000, - }, { - .name = "H3XXX params", - .size = 0x00040000, - .offset = 0x000C0000, - }, { -#ifdef CONFIG_JFFS2_FS - .name = "H3XXX root jffs2", - .size = MTDPART_SIZ_FULL, - .offset = 0x00100000, -#else - .name = "H3XXX initrd", - .size = 0x00100000, - .offset = 0x00100000, - }, { - .name = "H3XXX root cramfs", - .size = 0x00300000, - .offset = 0x00200000, - }, { - .name = "H3XXX usr cramfs", - .size = 0x00800000, - .offset = 0x00500000, - }, { - .name = "H3XXX usr local", - .size = MTDPART_SIZ_FULL, - .offset = 0x00d00000, -#endif -#endif - } -}; - -static void h3xxx_set_vpp(struct map_info *map, int vpp) -{ - assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); -} -#else -#define h3xxx_set_vpp NULL -#endif - -#ifdef CONFIG_SA1100_HACKKIT -static struct mtd_partition hackkit_partitions[] = { - { - .name = "BLOB", - .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "config", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - }, { - .name = "kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - }, { - .name = "initrd", - .size = 0x00180000, - .offset = MTDPART_OFS_APPEND, - }, { - .name = "rootfs", - .size = 0x700000, - .offset = MTDPART_OFS_APPEND, - }, { - .name = "data", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_HUW_WEBPANEL -static struct mtd_partition huw_webpanel_partitions[] = { - { - .name = "Loader", - .size = 0x00040000, - .offset = 0, - }, { - .name = "Sector 1", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - }, { - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_JORNADA720 -static struct mtd_partition jornada720_partitions[] = { - { - .name = "JORNADA720 boot firmware", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "JORNADA720 kernel", - .size = 0x000c0000, - .offset = 0x00040000, - }, { - .name = "JORNADA720 params", - .size = 0x00040000, - .offset = 0x00100000, - }, { - .name = "JORNADA720 initrd", - .size = 0x00100000, - .offset = 0x00140000, - }, { - .name = "JORNADA720 root cramfs", - .size = 0x00300000, - .offset = 0x00240000, - }, { - .name = "JORNADA720 usr cramfs", - .size = 0x00800000, - .offset = 0x00540000, - }, { - .name = "JORNADA720 usr local", - .size = 0, /* will expand to the end of the flash */ - .offset = 0x00d00000, - } -}; - -static void jornada720_set_vpp(struct map_info *map, int vpp) -{ - if (vpp) - PPSR |= 0x80; - else - PPSR &= ~0x80; - PPDR |= 0x80; -} -#else -#define jornada720_set_vpp NULL -#endif - -#ifdef CONFIG_SA1100_PANGOLIN -static struct mtd_partition pangolin_partitions[] = { - { - .name = "boot firmware", - .size = 0x00080000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "kernel", - .size = 0x00100000, - .offset = 0x00080000, - }, { - .name = "initrd", - .size = 0x00280000, - .offset = 0x00180000, - }, { - .name = "initrd-test", - .size = 0x03C00000, - .offset = 0x00400000, - } -}; -#endif - -#ifdef CONFIG_SA1100_PT_SYSTEM3 -/* erase size is 0x40000 == 256k partitions have to have this boundary */ -static struct mtd_partition system3_partitions[] = { - { - .name = "BLOB", - .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "config", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, - }, { - .name = "kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - }, { - .name = "root", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_SHANNON -static struct mtd_partition shannon_partitions[] = { - { - .name = "BLOB boot loader", - .offset = 0, - .size = 0x20000 - }, - { - .name = "kernel", - .offset = MTDPART_OFS_APPEND, - .size = 0xe0000 - }, - { - .name = "initrd", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL - } -}; - -#endif - -#ifdef CONFIG_SA1100_SHERMAN -static struct mtd_partition sherman_partitions[] = { - { - .size = 0x50000, - .offset = 0, - }, { - .size = 0x70000, - .offset = MTDPART_OFS_APPEND, - }, { - .size = 0x600000, - .offset = MTDPART_OFS_APPEND, - }, { - .size = 0xA0000, - .offset = MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_SIMPAD -static struct mtd_partition simpad_partitions[] = { - { - .name = "SIMpad boot firmware", - .size = 0x00080000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "SIMpad kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, - }, { -#ifdef CONFIG_ROOT_CRAMFS - .name = "SIMpad root cramfs", - .size =0x00D80000, - .offset = MTDPART_OFS_APPEND - - }, { - .name = "SIMpad local jffs2", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND -#else - .name = "SIMpad root jffs2", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND -#endif - } -}; -#endif /* CONFIG_SA1100_SIMPAD */ - -#ifdef CONFIG_SA1100_STORK -static struct mtd_partition stork_partitions[] = { - { - .name = "STORK boot firmware", - .size = 0x00040000, + .name = "Consus boot firmware", .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "STORK params", .size = 0x00040000, - .offset = 0x00040000, - }, { - .name = "STORK kernel", - .size = 0x00100000, - .offset = 0x00080000, + .mask_flags = MTD_WRITABLE, /* force read-only */ }, { -#ifdef CONFIG_JFFS2_FS - .name = "STORK root jffs2", - .offset = 0x00180000, - .size = MTDPART_SIZ_FULL, -#else - .name = "STORK initrd", + .name = "Consus kernel", + .offset = 0x00040000, .size = 0x00100000, - .offset = 0x00180000, - }, { - .name = "STORK root cramfs", - .size = 0x00300000, - .offset = 0x00280000, + .mask_flags = 0, }, { - .name = "STORK usr cramfs", - .size = 0x00800000, - .offset = 0x00580000, + .name = "Consus disk", + .offset = 0x00140000, + /* The rest (up to 16M) for jffs. We could put 0 and + make it find the size automatically, but right now + i have 32 megs. jffs will use all 32 megs if given + the chance, and this leads to horrible problems + when you try to re-flash the image because blob + won't erase the whole partition. */ + .size = 0x01000000 - 0x00140000, + .mask_flags = 0, }, { - .name = "STORK usr local", - .offset = 0x00d80000, - .size = MTDPART_SIZ_FULL, -#endif + /* this disk is a secondary disk, which can be used as + needed, for simplicity, make it the size of the other + consus partition, although realistically it could be + the remainder of the disk (depending on the file + system used) */ + .name = "Consus disk2", + .offset = 0x01000000, + .size = 0x01000000 - 0x00140000, + .mask_flags = 0, } }; -#endif -#ifdef CONFIG_SA1100_TRIZEPS -static struct mtd_partition trizeps_partitions[] = { +/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +static struct mtd_partition frodo_partitions[] = +{ { - .name = "Bootloader", - .size = 0x00100000, - .offset = 0, + .name = "bootloader", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE + }, { + .name = "bootloader params", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE }, { - .name = "Kernel", + .name = "kernel", .size = 0x00100000, .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE }, { - .name = "root", - .size = MTDPART_SIZ_FULL, + .name = "ramdisk", + .size = 0x00400000, .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE + }, { + .name = "file system", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND } }; -#endif -#ifdef CONFIG_SA1100_YOPY -static struct mtd_partition yopy_partitions[] = { +static struct mtd_partition jornada56x_partitions[] = { { - .name = "boot firmware", + .name = "bootldr", .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "kernel", - .size = 0x00080000, - .offset = 0x00080000, - }, { - .name = "initrd", - .size = 0x00300000, - .offset = 0x00100000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, }, { - .name = "root", - .size = 0x01000000, - .offset = 0x00400000, + .name = "rootfs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; -#endif -static int __init sa1100_static_partitions(struct mtd_partition **parts) +static void jornada56x_set_vpp(int vpp) { - int nb_parts = 0; - -#ifdef CONFIG_SA1100_ADSBITSY - if (machine_is_adsbitsy()) { - *parts = adsbitsy_partitions; - nb_parts = ARRAY_SIZE(adsbitsy_partitions); - } -#endif -#ifdef CONFIG_SA1100_ASSABET - if (machine_is_assabet()) { - *parts = assabet_partitions; - nb_parts = ARRAY_SIZE(assabet_partitions); - } -#endif -#ifdef CONFIG_SA1100_BADGE4 - if (machine_is_badge4()) { - *parts = badge4_partitions; - nb_parts = ARRAY_SIZE(badge4_partitions); - } -#endif -#ifdef CONFIG_SA1100_CERF - if (machine_is_cerf()) { - *parts = cerf_partitions; - nb_parts = ARRAY_SIZE(cerf_partitions); - } -#endif -#ifdef CONFIG_SA1100_CONSUS - if (machine_is_consus()) { - *parts = consus_partitions; - nb_parts = ARRAY_SIZE(consus_partitions); - } -#endif -#ifdef CONFIG_SA1100_FLEXANET - if (machine_is_flexanet()) { - *parts = flexanet_partitions; - nb_parts = ARRAY_SIZE(flexanet_partitions); - } -#endif -#ifdef CONFIG_SA1100_FREEBIRD - if (machine_is_freebird()) { - *parts = freebird_partitions; - nb_parts = ARRAY_SIZE(freebird_partitions); - } -#endif -#ifdef CONFIG_SA1100_FRODO - if (machine_is_frodo()) { - *parts = frodo_partitions; - nb_parts = ARRAY_SIZE(frodo_partitions); - } -#endif -#ifdef CONFIG_SA1100_GRAPHICSCLIENT - if (machine_is_graphicsclient()) { - *parts = graphicsclient_partitions; - nb_parts = ARRAY_SIZE(graphicsclient_partitions); - } -#endif -#ifdef CONFIG_SA1100_GRAPHICSMASTER - if (machine_is_graphicsmaster()) { - *parts = graphicsmaster_partitions; - nb_parts = ARRAY_SIZE(graphicsmaster_partitions); - } -#endif -#ifdef CONFIG_SA1100_H3XXX - if (machine_is_h3xxx()) { - *parts = h3xxx_partitions; - nb_parts = ARRAY_SIZE(h3xxx_partitions); - } -#endif -#ifdef CONFIG_SA1100_HACKKIT - if (machine_is_hackkit()) { - *parts = hackkit_partitions; - nb_parts = ARRAY_SIZE(hackkit_partitions); - } -#endif -#ifdef CONFIG_SA1100_HUW_WEBPANEL - if (machine_is_huw_webpanel()) { - *parts = huw_webpanel_partitions; - nb_parts = ARRAY_SIZE(huw_webpanel_partitions); - } -#endif -#ifdef CONFIG_SA1100_JORNADA720 - if (machine_is_jornada720()) { - *parts = jornada720_partitions; - nb_parts = ARRAY_SIZE(jornada720_partitions); - } -#endif -#ifdef CONFIG_SA1100_PANGOLIN - if (machine_is_pangolin()) { - *parts = pangolin_partitions; - nb_parts = ARRAY_SIZE(pangolin_partitions); - } -#endif -#ifdef CONFIG_SA1100_PT_SYSTEM3 - if (machine_is_pt_system3()) { - *parts = system3_partitions; - nb_parts = ARRAY_SIZE(system3_partitions); - } -#endif -#ifdef CONFIG_SA1100_SHANNON - if (machine_is_shannon()) { - *parts = shannon_partitions; - nb_parts = ARRAY_SIZE(shannon_partitions); - } -#endif -#ifdef CONFIG_SA1100_SHERMAN - if (machine_is_sherman()) { - *parts = sherman_partitions; - nb_parts = ARRAY_SIZE(sherman_partitions); - } -#endif -#ifdef CONFIG_SA1100_SIMPAD - if (machine_is_simpad()) { - *parts = simpad_partitions; - nb_parts = ARRAY_SIZE(simpad_partitions); - } -#endif -#ifdef CONFIG_SA1100_STORK - if (machine_is_stork()) { - *parts = stork_partitions; - nb_parts = ARRAY_SIZE(stork_partitions); - } -#endif -#ifdef CONFIG_SA1100_TRIZEPS - if (machine_is_trizeps()) { - *parts = trizeps_partitions; - nb_parts = ARRAY_SIZE(trizeps_partitions); - } -#endif -#ifdef CONFIG_SA1100_YOPY - if (machine_is_yopy()) { - *parts = yopy_partitions; - nb_parts = ARRAY_SIZE(yopy_partitions); - } -#endif - - return nb_parts; + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; } + +/* + * Machine Phys Size set_vpp + * Consus : SA1100_CS0_PHYS SZ_32M + * Frodo : SA1100_CS0_PHYS SZ_32M + * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp + */ #endif -struct sa_info { - unsigned long base; - unsigned long size; - int width; - void __iomem *vbase; - void (*set_vpp)(struct map_info *, int); - struct map_info *map; +struct sa_subdev_info { + char name[16]; + struct map_info map; struct mtd_info *mtd; - struct resource *res; + struct flash_platform_data *data; }; -#define NR_SUBMTD 4 +struct sa_info { + struct mtd_partition *parts; + struct mtd_info *mtd; + int num_subdev; + struct sa_subdev_info subdev[0]; +}; -static struct sa_info info[NR_SUBMTD]; +static void sa1100_set_vpp(struct map_info *map, int on) +{ + struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map); + subdev->data->set_vpp(on); +} -static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) +static void sa1100_destroy_subdev(struct sa_subdev_info *subdev) { - struct mtd_info *subdev[nr]; - struct map_info *maps; - int i, found = 0, ret = 0; + if (subdev->mtd) + map_destroy(subdev->mtd); + if (subdev->map.virt) + iounmap(subdev->map.virt); + release_mem_region(subdev->map.phys, subdev->map.size); +} - /* - * Allocate the map_info structs in one go. - */ - maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); - if (!maps) - return -ENOMEM; +static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res) +{ + unsigned long phys; + unsigned int size; + int ret; - memset(maps, 0, sizeof(struct map_info) * nr); + phys = res->start; + size = res->end - phys + 1; /* - * Claim and then map the memory regions. + * Retrieve the bankwidth from the MSC registers. + * We currently only implement CS0 and CS1 here. */ - for (i = 0; i < nr; i++) { - if (sa[i].base == (unsigned long)-1) - break; + switch (phys) { + default: + printk(KERN_WARNING "SA1100 flash: unknown base address " + "0x%08lx, assuming CS0\n", phys); - sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash"); - if (!sa[i].res) { - ret = -EBUSY; - break; - } - - sa[i].map = maps + i; - - sa[i].vbase = ioremap(sa[i].base, sa[i].size); - if (!sa[i].vbase) { - ret = -ENOMEM; - break; - } + case SA1100_CS0_PHYS: + subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; + break; - sa[i].map->virt = sa[i].vbase; - sa[i].map->phys = sa[i].base; - sa[i].map->set_vpp = sa[i].set_vpp; - sa[i].map->bankwidth = sa[i].width; - sa[i].map->size = sa[i].size; - - simple_map_init(sa[i].map); - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); - if (sa[i].mtd == NULL) { - ret = -ENXIO; - break; - } - sa[i].mtd->owner = THIS_MODULE; - subdev[i] = sa[i].mtd; - - printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " - "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, - sa[i].width * 8); - found += 1; + case SA1100_CS1_PHYS: + subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; + break; } - /* - * ENXIO is special. It means we didn't find a chip when - * we probed. We need to tear down the mapping, free the - * resource and mark it as such. - */ - if (ret == -ENXIO) { - iounmap(sa[i].vbase); - sa[i].vbase = NULL; - release_resource(sa[i].res); - sa[i].res = NULL; + if (!request_mem_region(phys, size, subdev->name)) { + ret = -EBUSY; + goto out; } - /* - * If we found one device, don't bother with concat support. - * If we found multiple devices, use concat if we have it - * available, otherwise fail. - */ - if (ret == 0 || ret == -ENXIO) { - if (found == 1) { - *rmtd = subdev[0]; - ret = 0; - } else if (found > 1) { - /* - * We detected multiple devices. Concatenate - * them together. - */ -#ifdef CONFIG_MTD_CONCAT - *rmtd = mtd_concat_create(subdev, found, - "sa1100 flash"); - if (*rmtd == NULL) - ret = -ENXIO; -#else - printk(KERN_ERR "SA1100 flash: multiple devices " - "found but MTD concat support disabled.\n"); - ret = -ENXIO; -#endif - } + if (subdev->data->set_vpp) + subdev->map.set_vpp = sa1100_set_vpp; + + subdev->map.phys = phys; + subdev->map.size = size; + subdev->map.virt = ioremap(phys, size); + if (!subdev->map.virt) { + ret = -ENOMEM; + goto err; } + simple_map_init(&subdev->map); + /* - * If we failed, clean up. + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. */ - if (ret) { - do { - if (sa[i].mtd) - map_destroy(sa[i].mtd); - if (sa[i].vbase) - iounmap(sa[i].vbase); - if (sa[i].res) - release_resource(sa[i].res); - } while (i--); - - kfree(maps); - } - + subdev->mtd = do_map_probe(subdev->data->map_name, &subdev->map); + if (subdev->mtd == NULL) { + ret = -ENXIO; + goto err; + } + subdev->mtd->owner = THIS_MODULE; + + printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " + "%d-bit\n", phys, subdev->mtd->size >> 20, + subdev->map.bankwidth * 8); + + return 0; + + err: + sa1100_destroy_subdev(subdev); + out: return ret; } -static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) +static void sa1100_destroy(struct sa_info *info) { int i; - del_mtd_partitions(mtd); + if (info->mtd) { + del_mtd_partitions(info->mtd); #ifdef CONFIG_MTD_CONCAT - if (mtd != sa[0].mtd) - mtd_concat_destroy(mtd); + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); #endif - - for (i = NR_SUBMTD; i >= 0; i--) { - if (sa[i].mtd) - map_destroy(sa[i].mtd); - if (sa[i].vbase) - iounmap(sa[i].vbase); - if (sa[i].res) - release_resource(sa[i].res); } - kfree(sa[0].map); -} -/* - * A Thought: can we automatically detect the flash? - * - Check to see if the region is busy (yes -> failure) - * - Is the MSC setup for flash (no -> failure) - * - Probe for flash - */ + if (info->parts) + kfree(info->parts); -static struct map_info sa1100_probe_map __initdata = { - .name = "SA1100-flash", -}; + for (i = info->num_subdev - 1; i >= 0; i--) + sa1100_destroy_subdev(&info->subdev[i]); + kfree(info); +} -static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) +static struct sa_info *__init +sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *flash) { - struct mtd_info *mtd; + struct sa_info *info; + int nr, size, i, ret = 0; - printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", - phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); - - if (check_mem_region(phys, 0x08000000)) { - printk("busy\n"); - return; - } + /* + * Count number of devices. + */ + for (nr = 0; ; nr++) + if (!platform_get_resource(pdev, IORESOURCE_MEM, nr)) + break; - if ((msc & 3) == 1) { - printk("wrong type\n"); - return; + if (nr == 0) { + ret = -ENODEV; + goto out; } - sa1100_probe_map.bankwidth = msc & MSC_RBW ? 2 : 4; - sa1100_probe_map.size = SZ_1M; - sa1100_probe_map.phys = phys; - sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M); - if (sa1100_probe_map.virt == 0) - goto fail; - simple_map_init(&sa1100_probe_map); - - /* Shame cfi_probe blurts out kernel messages... */ - mtd = do_map_probe("cfi_probe", &sa1100_probe_map); - if (mtd) - map_destroy(mtd); - iounmap((void *)sa1100_probe_map.virt); - - if (!mtd) - goto fail; + size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr; - printk("pass\n"); - return; + /* + * Allocate the map_info structs in one go. + */ + info = kmalloc(size, GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto out; + } - fail: - printk("failed\n"); -} + memset(info, 0, size); -static void __init sa1100_probe_flash(void) -{ - printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); - sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); - sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); - sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); - sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); - sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); - sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); - printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); -} + /* + * Claim and then map the memory regions. + */ + for (i = 0; i < nr; i++) { + struct sa_subdev_info *subdev = &info->subdev[i]; + struct resource *res; -static int __init sa1100_locate_flash(void) -{ - int i, nr = -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + break; - sa1100_probe_flash(); + subdev->map.name = subdev->name; + sprintf(subdev->name, "sa1100-%d", i); + subdev->data = flash; - if (machine_is_adsbitsy()) { - info[0].base = SA1100_CS1_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_assabet()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - info[1].base = SA1100_CS1_PHYS; /* neponset */ - info[1].size = SZ_32M; - nr = 2; - } - if (machine_is_badge4()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_64M; - nr = 1; - } - if (machine_is_cerf()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_consus()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_flexanet()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_freebird()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_frodo()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_graphicsclient()) { - info[0].base = SA1100_CS1_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_graphicsmaster()) { - info[0].base = SA1100_CS1_PHYS; - info[0].size = SZ_16M; - nr = 1; - } - if (machine_is_h3xxx()) { - info[0].set_vpp = h3xxx_set_vpp; - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_huw_webpanel()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_16M; - nr = 1; - } - if (machine_is_itsy()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_jornada720()) { - info[0].set_vpp = jornada720_set_vpp; - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_nanoengine()) { - info[0].base = SA1100_CS0_PHYS; - info[1].size = SZ_32M; - nr = 1; - } - if (machine_is_pangolin()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_64M; - nr = 1; - } - if (machine_is_pfs168()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_pleb()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_4M; - info[1].base = SA1100_CS1_PHYS; - info[1].size = SZ_4M; - nr = 2; - } - if (machine_is_pt_system3()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_16M; - nr = 1; - } - if (machine_is_shannon()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_4M; - nr = 1; - } - if (machine_is_sherman()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_simpad()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_16M; - info[1].base = SA1100_CS1_PHYS; - info[1].size = SZ_16M; - nr = 2; - } - if (machine_is_stork()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_32M; - nr = 1; - } - if (machine_is_trizeps()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_16M; - nr = 1; - } - if (machine_is_victor()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_2M; - nr = 1; - } - if (machine_is_yopy()) { - info[0].base = SA1100_CS0_PHYS; - info[0].size = SZ_64M; - info[1].base = SA1100_CS1_PHYS; - info[1].size = SZ_64M; - nr = 2; + ret = sa1100_probe_subdev(subdev, res); + if (ret) + break; } - if (nr < 0) - return nr; + info->num_subdev = i; /* - * Retrieve the bankwidth from the MSC registers. - * We currently only implement CS0 and CS1 here. + * ENXIO is special. It means we didn't find a chip when we probed. */ - for (i = 0; i < nr; i++) { - switch (info[i].base) { - default: - printk(KERN_WARNING "SA1100 flash: unknown base address " - "0x%08lx, assuming CS0\n", info[i].base); - case SA1100_CS0_PHYS: - info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; - break; + if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0)) + goto err; - case SA1100_CS1_PHYS: - info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; - break; - } + /* + * If we found one device, don't bother with concat support. If + * we found multiple devices, use concat if we have it available, + * otherwise fail. Either way, it'll be called "sa1100". + */ + if (info->num_subdev == 1) { + strcpy(info->subdev[0].name, "sa1100"); + info->mtd = info->subdev[0].mtd; + ret = 0; + } else if (info->num_subdev > 1) { +#ifdef CONFIG_MTD_CONCAT + struct mtd_info *cdev[nr]; + /* + * We detected multiple devices. Concatenate them together. + */ + for (i = 0; i < info->num_subdev; i++) + cdev[i] = info->subdev[i].mtd; + + info->mtd = mtd_concat_create(cdev, info->num_subdev, + "sa1100"); + if (info->mtd == NULL) + ret = -ENXIO; +#else + printk(KERN_ERR "SA1100 flash: multiple devices " + "found but MTD concat support disabled.\n"); + ret = -ENXIO; +#endif } - return nr; + if (ret == 0) + return info; + + err: + sa1100_destroy(info); + out: + return ERR_PTR(ret); } -static struct mtd_partition *parsed_parts; -const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; +static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; -static void __init sa1100_locate_partitions(struct mtd_info *mtd) +static int __init sa1100_mtd_probe(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct flash_platform_data *flash = pdev->dev.platform_data; + struct mtd_partition *parts; const char *part_type = NULL; - int nr_parts = 0; + struct sa_info *info; + int err, nr_parts = 0; - do { - /* - * Partition selection stuff. - */ + if (!flash) + return -ENODEV; + + info = sa1100_setup_mtd(pdev, flash); + if (IS_ERR(info)) { + err = PTR_ERR(info); + goto out; + } + + /* + * Partition selection stuff. + */ #ifdef CONFIG_MTD_PARTITIONS - nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0); - if (nr_parts > 0) { - part_type = "dynamic"; - break; - } -#endif -#ifdef CONFIG_MTD_SA1100_STATICMAP - nr_parts = sa1100_static_partitions(&parsed_parts); - if (nr_parts > 0) { - part_type = "static"; - break; - } + nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0); + if (nr_parts > 0) { + info->parts = parts; + part_type = "dynamic"; + } else #endif - } while (0); + { + parts = flash->parts; + nr_parts = flash->nr_parts; + part_type = "static"; + } if (nr_parts == 0) { printk(KERN_NOTICE "SA1100 flash: no partition info " "available, registering whole flash\n"); - add_mtd_device(mtd); + add_mtd_device(info->mtd); } else { printk(KERN_NOTICE "SA1100 flash: using %s partition " "definition\n", part_type); - add_mtd_partitions(mtd, parsed_parts, nr_parts); + add_mtd_partitions(info->mtd, parts, nr_parts); } - /* Always succeeds. */ + dev_set_drvdata(dev, info); + err = 0; + + out: + return err; } -static void __exit sa1100_destroy_partitions(void) +static int __exit sa1100_mtd_remove(struct device *dev) { - if (parsed_parts) - kfree(parsed_parts); + struct sa_info *info = dev_get_drvdata(dev); + dev_set_drvdata(dev, NULL); + sa1100_destroy(info); + return 0; } -static struct mtd_info *mymtd; - -static int __init sa1100_mtd_init(void) +#ifdef CONFIG_PM +static int sa1100_mtd_suspend(struct device *dev, u32 state, u32 level) { - int ret; - int nr; - - nr = sa1100_locate_flash(); - if (nr < 0) - return nr; + struct sa_info *info = dev_get_drvdata(dev); + int ret = 0; - ret = sa1100_setup_mtd(info, nr, &mymtd); - if (ret == 0) - sa1100_locate_partitions(mymtd); + if (info && level == SUSPEND_SAVE_STATE) + ret = info->mtd->suspend(info->mtd); return ret; } -static void __exit sa1100_mtd_cleanup(void) +static int sa1100_mtd_resume(struct device *dev, u32 level) +{ + struct sa_info *info = dev_get_drvdata(dev); + if (info && level == RESUME_RESTORE_STATE) + info->mtd->resume(info->mtd); + return 0; +} +#else +#define sa1100_mtd_suspend NULL +#define sa1100_mtd_resume NULL +#endif + +static struct device_driver sa1100_mtd_driver = { + .name = "flash", + .bus = &platform_bus_type, + .probe = sa1100_mtd_probe, + .remove = __exit_p(sa1100_mtd_remove), + .suspend = sa1100_mtd_suspend, + .resume = sa1100_mtd_resume, +}; + +static int __init sa1100_mtd_init(void) +{ + return driver_register(&sa1100_mtd_driver); +} + +static void __exit sa1100_mtd_exit(void) { - sa1100_destroy_mtd(info, mymtd); - sa1100_destroy_partitions(); + driver_unregister(&sa1100_mtd_driver); } module_init(sa1100_mtd_init); -module_exit(sa1100_mtd_cleanup); +module_exit(sa1100_mtd_exit); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("SA1100 CFI map driver"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/sbc_gxx.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/sbc_gxx.c --- linux-2.6.9-intc1/drivers/mtd/maps/sbc_gxx.c 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/sbc_gxx.c 2005-03-21 21:28:42.000000000 -0500 @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: sbc_gxx.c,v 1.29 2004/07/12 22:38:29 dwmw2 Exp $ + $Id: sbc_gxx.c,v 1.34 2005/01/12 22:34:35 gleixner Exp $ The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. @@ -84,8 +84,8 @@ // Globals static volatile int page_in_window = -1; // Current page in window. -static unsigned long iomapadr; -static spinlock_t sbc_gxx_spin = SPIN_LOCK_UNLOCKED; +static void __iomem *iomapadr; +static DEFINE_SPINLOCK(sbc_gxx_spin); /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the @@ -189,13 +189,13 @@ map_destroy( all_mtd ); } - iounmap((void *)iomapadr); + iounmap(iomapadr); release_region(PAGE_IO,PAGE_IO_SIZE); } -int __init init_sbc_gxx(void) +static int __init init_sbc_gxx(void) { - iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); if (!iomapadr) { printk( KERN_ERR"%s: failed to ioremap memory region\n", sbc_gxx_map.name ); @@ -206,7 +206,7 @@ printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", sbc_gxx_map.name, PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); - iounmap((void *)iomapadr); + iounmap(iomapadr); return -EAGAIN; } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/sc520cdp.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/sc520cdp.c --- linux-2.6.9-intc1/drivers/mtd/maps/sc520cdp.c 2004-10-18 17:55:24.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/sc520cdp.c 2005-03-21 21:28:42.000000000 -0500 @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: sc520cdp.c,v 1.16 2004/07/12 21:59:45 dwmw2 Exp $ + * $Id: sc520cdp.c,v 1.21 2004/12/13 10:27:08 dedekind Exp $ * * * The SC520CDP is an evaluation board for the Elan SC520 processor available @@ -186,12 +186,12 @@ static void sc520cdp_setup_par(void) { - volatile unsigned long *mmcr; + volatile unsigned long __iomem *mmcr; unsigned long mmcr_val; int i, j; /* map in SC520's MMCR area */ - mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); + mmcr = ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */ /* force physical address fields to BIOS defaults: */ for(i = 0; i < NUM_FLASH_BANKS; i++) @@ -223,7 +223,7 @@ sc520cdp_map[i].phys = par_table[i].default_address; } } - iounmap((void *)mmcr); + iounmap(mmcr); } #endif @@ -241,7 +241,7 @@ printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", sc520cdp_map[i].size, sc520cdp_map[i].phys); - sc520cdp_map[i].virt = (unsigned long)ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); + sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); if (!sc520cdp_map[i].virt) { printk("Failed to ioremap_nocache\n"); @@ -261,7 +261,7 @@ ++devices_found; } else { - iounmap((void *)sc520cdp_map[i].virt); + iounmap(sc520cdp_map[i].virt); } } if(devices_found >= 2) { @@ -290,8 +290,8 @@ if (mymtd[i]) map_destroy(mymtd[i]); if (sc520cdp_map[i].virt) { - iounmap((void *)sc520cdp_map[i].virt); - sc520cdp_map[i].virt = 0; + iounmap(sc520cdp_map[i].virt); + sc520cdp_map[i].virt = NULL; } } } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/scb2_flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/scb2_flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/scb2_flash.c 2004-10-18 17:54:31.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/scb2_flash.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,6 +1,6 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.8 2004/07/12 21:59:45 dwmw2 Exp $ + * $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin * @@ -62,9 +62,9 @@ #define SCB2_WINDOW 0x00100000 -static void *scb2_ioaddr; +static void __iomem *scb2_ioaddr; static struct mtd_info *scb2_mtd; -struct map_info scb2_map = { +static struct map_info scb2_map = { .name = "SCB2 BIOS Flash", .size = 0, .bankwidth = 1, @@ -163,7 +163,7 @@ } scb2_map.phys = SCB2_ADDR; - scb2_map.virt = (unsigned long)scb2_ioaddr; + scb2_map.virt = scb2_ioaddr; scb2_map.size = SCB2_WINDOW; simple_map_init(&scb2_map); @@ -238,7 +238,7 @@ static int __init scb2_flash_init(void) { - return pci_module_init(&scb2_flash_driver); + return pci_register_driver(&scb2_flash_driver); } static void __exit diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/scx200_docflash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/scx200_docflash.c --- linux-2.6.9-intc1/drivers/mtd/maps/scx200_docflash.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/scx200_docflash.c 2005-03-21 21:28:43.000000000 -0500 @@ -2,7 +2,7 @@ Copyright (c) 2001,2002 Christer Weinigel - $Id: scx200_docflash.c,v 1.6 2004/07/12 21:59:45 dwmw2 Exp $ + $Id: scx200_docflash.c,v 1.11 2005/03/17 20:01:16 gleixner Exp $ National Semiconductor SCx200 flash mapped with DOCCS */ @@ -26,23 +26,20 @@ MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver"); MODULE_LICENSE("GPL"); -/* Set this to one if you want to partition the flash */ -#define PARTITION 1 +static int probe = 0; /* Don't autoprobe */ +static unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */ +static unsigned width = 8; /* Default to 8 bits wide */ +static char *flashtype = "cfi_probe"; -MODULE_PARM(probe, "i"); +module_param(probe, int, 0); MODULE_PARM_DESC(probe, "Probe for a BIOS mapping"); -MODULE_PARM(size, "i"); +module_param(size, int, 0); MODULE_PARM_DESC(size, "Size of the flash mapping"); -MODULE_PARM(width, "i"); +module_param(width, int, 0); MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)"); -MODULE_PARM(flashtype, "s"); +module_param(flashtype, charp, 0); MODULE_PARM_DESC(flashtype, "Type of MTD probe to do"); -static int probe = 0; /* Don't autoprobe */ -static unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */ -static unsigned width = 8; /* Default to 8 bits wide */ -static char *flashtype = "cfi_probe"; - static struct resource docmem = { .flags = IORESOURCE_MEM, .name = "NatSemi SCx200 DOCCS Flash", @@ -50,7 +47,7 @@ static struct mtd_info *mymtd; -#if PARTITION +#ifdef CONFIG_MTD_PARTITIONS static struct mtd_partition partition_info[] = { { .name = "DOCCS Boot kernel", @@ -81,7 +78,7 @@ .name = "NatSemi SCx200 DOCCS Flash", }; -int __init init_scx200_docflash(void) +static int __init init_scx200_docflash(void) { unsigned u; unsigned base; @@ -95,17 +92,16 @@ PCI_DEVICE_ID_NS_SCx200_BRIDGE, NULL)) == NULL) return -ENODEV; - - if (!scx200_cb_probe(SCx200_CB_BASE)) { - printk(KERN_WARNING NAME ": no configuration block found\n"); + + /* check that we have found the configuration block */ + if (!scx200_cb_present()) return -ENODEV; - } if (probe) { /* Try to use the present flash mapping if any */ pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base); pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl); - pmr = inl(SCx200_CB_BASE + SCx200_PMR); + pmr = inl(scx200_cb_base + SCx200_PMR); if (base == 0 || (ctrl & 0x07000000) != 0x07000000 @@ -158,14 +154,14 @@ pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start); pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl); - pmr = inl(SCx200_CB_BASE + SCx200_PMR); + pmr = inl(scx200_cb_base + SCx200_PMR); if (width == 8) { pmr &= ~(1<<6); } else { pmr |= (1<<6); } - outl(pmr, SCx200_CB_BASE + SCx200_PMR); + outl(pmr, scx200_cb_base + SCx200_PMR); } printk(KERN_INFO NAME ": DOCCS mapped at 0x%lx-0x%lx, width %d\n", @@ -180,7 +176,7 @@ simple_map_init(&scx200_docflash_map); scx200_docflash_map.phys = docmem.start; - scx200_docflash_map.virt = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); + scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size); if (!scx200_docflash_map.virt) { printk(KERN_ERR NAME ": failed to ioremap the flash\n"); release_resource(&docmem); @@ -190,7 +186,7 @@ mymtd = do_map_probe(flashtype, &scx200_docflash_map); if (!mymtd) { printk(KERN_ERR NAME ": unable to detect flash\n"); - iounmap((void *)scx200_docflash_map.virt); + iounmap(scx200_docflash_map.virt); release_resource(&docmem); return -ENXIO; } @@ -200,7 +196,7 @@ mymtd->owner = THIS_MODULE; -#if PARTITION +#ifdef CONFIG_MTD_PARTITIONS partition_info[3].offset = mymtd->size-partition_info[3].size; partition_info[2].size = partition_info[3].offset-partition_info[2].offset; add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); @@ -213,7 +209,7 @@ static void __exit cleanup_scx200_docflash(void) { if (mymtd) { -#if PARTITION +#ifdef CONFIG_MTD_PARTITIONS del_mtd_partitions(mymtd); #else del_mtd_device(mymtd); @@ -221,7 +217,7 @@ map_destroy(mymtd); } if (scx200_docflash_map.virt) { - iounmap((void *)scx200_docflash_map.virt); + iounmap(scx200_docflash_map.virt); release_resource(&docmem); } } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/sharpsl-flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/sharpsl-flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/sharpsl-flash.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/sharpsl-flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,101 @@ +/* + * sharpsl-flash.c + * + * Copyright (C) 2001 Lineo Japan, Inc. + * Copyright (C) 2002 SHARP + * + * $Id: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $ + * + * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp + * Handle mapping of the flash on the RPX Lite and CLLF boards + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define WINDOW_ADDR 0x00000000 +#define WINDOW_SIZE 0x01000000 +#define BANK_WIDTH 2 + +static struct mtd_info *mymtd; + +struct map_info sharpsl_map = { + .name = "sharpsl-flash", + .size = WINDOW_SIZE, + .bankwidth = BANK_WIDTH, + .phys = WINDOW_ADDR +}; + +static struct mtd_partition sharpsl_partitions[1] = { + { + name: "Filesystem", + size: 0x006d0000, + offset: 0x00120000 + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_sharpsl(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + char *part_type = "static"; + + printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + if (!sharpsl_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_map_probe("map_rom", &sharpsl_map); + if (!mymtd) { + iounmap(sharpsl_map.virt); + return -ENXIO; + } + + mymtd->owner = THIS_MODULE; + + parts = sharpsl_partitions; + nb_parts = NB_OF(sharpsl_partitions); + + printk(KERN_NOTICE "Using %s partision definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + + return 0; +} + +static void __exit cleanup_sharpsl(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (sharpsl_map.virt) { + iounmap(sharpsl_map.virt); + sharpsl_map.virt = 0; + } +} + +module_init(init_sharpsl); +module_exit(cleanup_sharpsl); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("SHARP (Original: Arnold Christensen )"); +MODULE_DESCRIPTION("MTD map driver for SHARP SL series"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/solutionengine.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/solutionengine.c --- linux-2.6.9-intc1/drivers/mtd/maps/solutionengine.c 2004-10-18 17:55:28.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/solutionengine.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: solutionengine.c,v 1.13 2004/07/12 21:59:45 dwmw2 Exp $ + * $Id: solutionengine.c,v 1.14 2004/09/16 23:27:14 gleixner Exp $ * * Flash and EPROM on Hitachi Solution Engine and similar boards. * @@ -62,9 +62,9 @@ /* First probe at offset 0 */ soleng_flash_map.phys = 0; - soleng_flash_map.virt = P2SEGADDR(0); + soleng_flash_map.virt = (void __iomem *)P2SEGADDR(0); soleng_eprom_map.phys = 0x01000000; - soleng_eprom_map.virt = P1SEGADDR(0x01000000); + soleng_eprom_map.virt = (void __iomem *)P1SEGADDR(0x01000000); simple_map_init(&soleng_eprom_map); simple_map_init(&soleng_flash_map); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/sun_uflash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/sun_uflash.c --- linux-2.6.9-intc1/drivers/mtd/maps/sun_uflash.c 2004-10-18 17:54:38.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/sun_uflash.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,4 +1,4 @@ -/* $Id: sun_uflash.c,v 1.9 2004/07/12 21:59:45 dwmw2 Exp $ +/* $Id: sun_uflash.c,v 1.12 2005/03/17 19:58:15 gleixner Exp $ * * sun_uflash - Driver implementation for user-programmable flash * present on many Sun Microsystems SME boardsets. @@ -96,8 +96,7 @@ pdev->map.name = pdev->name; } pdev->map.phys = edev->resource[0].start; - pdev->map.virt = - (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size); + pdev->map.virt = ioremap_nocache(edev->resource[0].start, pdev->map.size); if(0 == pdev->map.virt) { printk("%s: failed to map device\n", __FUNCTION__); kfree(pdev->name); @@ -110,7 +109,7 @@ /* MTD registration */ pdev->mtd = do_map_probe("cfi_probe", &pdev->map); if(0 == pdev->mtd) { - iounmap((void *)pdev->map.virt); + iounmap(pdev->map.virt); kfree(pdev->name); kfree(pdev); return(-ENXIO); @@ -164,8 +163,8 @@ map_destroy(udev->mtd); } if(0 != udev->map.virt) { - iounmap((void*)udev->map.virt); - udev->map.virt = 0; + iounmap(udev->map.virt); + udev->map.virt = NULL; } if(0 != udev->name) { kfree(udev->name); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/tqm8xxl.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/tqm8xxl.c --- linux-2.6.9-intc1/drivers/mtd/maps/tqm8xxl.c 2004-10-18 17:53:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/tqm8xxl.c 2005-03-21 21:28:42.000000000 -0500 @@ -2,7 +2,7 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.11 2004/07/12 21:59:45 dwmw2 Exp $ + * $Id: tqm8xxl.c,v 1.13 2004/10/20 22:21:53 dwmw2 Exp $ * * based on rpxlite.c * diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/ts5500_flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/ts5500_flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/ts5500_flash.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/ts5500_flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,141 @@ +/* + * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board + * + * Copyright (C) 2004 Sean Young + * + * 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 + * + * Note: + * - In order for detection to work, jumper 3 must be set. + * - Drive A and B use a proprietary FTL from General Software which isn't + * supported as of yet so standard drives can't be mounted; you can create + * your own (e.g. jffs) file system. + * - If you have created your own jffs file system and the bios overwrites + * it during boot, try disabling Drive A: and B: in the boot order. + * + * $Id: ts5500_flash.c,v 1.2 2004/11/28 09:40:40 dwmw2 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#define WINDOW_ADDR 0x09400000 +#define WINDOW_SIZE 0x00200000 + +static struct map_info ts5500_map = { + .name = "TS-5500 Flash", + .size = WINDOW_SIZE, + .bankwidth = 1, + .phys = WINDOW_ADDR +}; + +#ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition ts5500_partitions[] = { + { + .name = "Drive A", + .offset = 0, + .size = 0x0e0000 + }, + { + .name = "BIOS", + .offset = 0x0e0000, + .size = 0x020000, + }, + { + .name = "Drive B", + .offset = 0x100000, + .size = 0x100000 + } +}; + +#define NUM_PARTITIONS (sizeof(ts5500_partitions)/sizeof(struct mtd_partition)) + +#endif + +static struct mtd_info *mymtd; + +static int __init init_ts5500_map(void) +{ + int rc = 0; + + ts5500_map.virt = ioremap_nocache(ts5500_map.phys, ts5500_map.size); + + if(!ts5500_map.virt) { + printk(KERN_ERR "Failed to ioremap_nocache\n"); + rc = -EIO; + goto err_out_ioremap; + } + + simple_map_init(&ts5500_map); + + mymtd = do_map_probe("jedec_probe", &ts5500_map); + if(!mymtd) + mymtd = do_map_probe("map_rom", &ts5500_map); + + if(!mymtd) { + rc = -ENXIO; + goto err_out_map; + } + + mymtd->owner = THIS_MODULE; +#ifdef CONFIG_MTD_PARTITIONS + add_mtd_partitions(mymtd, ts5500_partitions, NUM_PARTITIONS); +#else + add_mtd_device(mymtd); +#endif + + return 0; + +err_out_map: + map_destroy(mymtd); +err_out_ioremap: + iounmap(ts5500_map.virt); + + return rc; +} + +static void __exit cleanup_ts5500_map(void) +{ + if (mymtd) { +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(mymtd); +#else + del_mtd_device(mymtd); +#endif + map_destroy(mymtd); + } + + if (ts5500_map.virt) { + iounmap(ts5500_map.virt); + ts5500_map.virt = NULL; + } +} + +module_init(init_ts5500_map); +module_exit(cleanup_ts5500_map); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sean Young "); +MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board"); + diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/uclinux.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/uclinux.c --- linux-2.6.9-intc1/drivers/mtd/maps/uclinux.c 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/uclinux.c 2005-03-21 21:28:42.000000000 -0500 @@ -5,7 +5,7 @@ * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * - * $Id: uclinux.c,v 1.7 2004/07/12 21:59:45 dwmw2 Exp $ + * $Id: uclinux.c,v 1.10 2005/01/05 18:05:13 dwmw2 Exp $ */ /****************************************************************************/ @@ -47,7 +47,7 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct map_info *map = (struct map_info *) mtd->priv; + struct map_info *map = mtd->priv; *mtdbuf = (u_char *) (map->virt + ((int) from)); *retlen = len; return(0); @@ -71,7 +71,7 @@ mapp->virt = ioremap_nocache(mapp->phys, mapp->size); - if (!mapp->virt) { + if (mapp->virt == 0) { printk("uclinux[mtd]: ioremap_nocache() failed\n"); return(-EIO); } diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/vmax301.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/vmax301.c --- linux-2.6.9-intc1/drivers/mtd/maps/vmax301.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/vmax301.c 2005-03-21 21:28:42.000000000 -0500 @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.30 2004/07/12 22:38:29 dwmw2 Exp $ +// $Id: vmax301.c,v 1.31 2005/01/12 22:34:35 gleixner Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -38,7 +38,7 @@ the extra indirection from having one of the map->map_priv fields pointing to yet another private struct. */ -static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(vmax301_spin); static void __vmax301_page(struct map_info *map, unsigned long page) { diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/walnut.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/walnut.c --- linux-2.6.9-intc1/drivers/mtd/maps/walnut.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/walnut.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,122 @@ +/* + * $Id: walnut.c,v 1.2 2004/12/10 12:07:42 holindho Exp $ + * + * Mapping for Walnut flash + * (used ebony.c as a "framework") + * + * Heikki Lindholm + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* these should be in platforms/4xx/walnut.h ? */ +#define WALNUT_FLASH_ONBD_N(x) (x & 0x02) +#define WALNUT_FLASH_SRAM_SEL(x) (x & 0x01) +#define WALNUT_FLASH_LOW 0xFFF00000 +#define WALNUT_FLASH_HIGH 0xFFF80000 +#define WALNUT_FLASH_SIZE 0x80000 + +static struct mtd_info *flash; + +static struct map_info walnut_map = { + .name = "Walnut flash", + .size = WALNUT_FLASH_SIZE, + .bankwidth = 1, +}; + +/* Actually, OpenBIOS is the last 128 KiB of the flash - better + * partitioning could be made */ +static struct mtd_partition walnut_partitions[] = { + { + .name = "OpenBIOS", + .offset = 0x0, + .size = WALNUT_FLASH_SIZE, + /*.mask_flags = MTD_WRITEABLE, */ /* force read-only */ + } +}; + +int __init init_walnut(void) +{ + u8 fpga_brds1; + void *fpga_brds1_adr; + void *fpga_status_adr; + unsigned long flash_base; + + /* this should already be mapped (platform/4xx/walnut.c) */ + fpga_status_adr = ioremap(WALNUT_FPGA_BASE, 8); + if (!fpga_status_adr) + return -ENOMEM; + + fpga_brds1_adr = fpga_status_adr+5; + fpga_brds1 = readb(fpga_brds1_adr); + /* iounmap(fpga_status_adr); */ + + if (WALNUT_FLASH_ONBD_N(fpga_brds1)) { + printk("The on-board flash is disabled (U79 sw 5)!"); + return -EIO; + } + if (WALNUT_FLASH_SRAM_SEL(fpga_brds1)) + flash_base = WALNUT_FLASH_LOW; + else + flash_base = WALNUT_FLASH_HIGH; + + walnut_map.phys = flash_base; + walnut_map.virt = + (void __iomem *)ioremap(flash_base, walnut_map.size); + + if (!walnut_map.virt) { + printk("Failed to ioremap flash.\n"); + return -EIO; + } + + simple_map_init(&walnut_map); + + flash = do_map_probe("jedec_probe", &walnut_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, walnut_partitions, + ARRAY_SIZE(walnut_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_walnut(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (walnut_map.virt) { + iounmap((void *)walnut_map.virt); + walnut_map.virt = 0; + } +} + +module_init(init_walnut); +module_exit(cleanup_walnut); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Heikki Lindholm "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405GP Walnut boards"); diff -Nur linux-2.6.9-intc1/drivers/mtd/maps/wr_sbc82xx_flash.c linux-2.6.9-intc1-mih1/drivers/mtd/maps/wr_sbc82xx_flash.c --- linux-2.6.9-intc1/drivers/mtd/maps/wr_sbc82xx_flash.c 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/maps/wr_sbc82xx_flash.c 2005-03-21 21:28:43.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: wr_sbc82xx_flash.c,v 1.5 2004/07/15 14:52:02 dwmw2 Exp $ + * $Id: wr_sbc82xx_flash.c,v 1.7 2004/11/04 13:24:15 gleixner Exp $ * * Map for flash chips on Wind River PowerQUICC II SBC82xx board. * @@ -116,7 +116,7 @@ } printk(" at %08lx)\n", sbc82xx_flash_map[i].phys); - sbc82xx_flash_map[i].virt = (unsigned long)ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size); + sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size); if (!sbc82xx_flash_map[i].virt) { printk("Failed to ioremap\n"); diff -Nur linux-2.6.9-intc1/drivers/mtd/mtd_blkdevs.c linux-2.6.9-intc1-mih1/drivers/mtd/mtd_blkdevs.c --- linux-2.6.9-intc1/drivers/mtd/mtd_blkdevs.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/mtd_blkdevs.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mtd_blkdevs.c,v 1.22 2004/07/12 12:35:28 dwmw2 Exp $ + * $Id: mtd_blkdevs.c,v 1.24 2004/11/16 18:28:59 dwmw2 Exp $ * * (C) 2003 David Woodhouse * @@ -143,7 +143,7 @@ } -int blktrans_open(struct inode *i, struct file *f) +static int blktrans_open(struct inode *i, struct file *f) { struct mtd_blktrans_dev *dev; struct mtd_blktrans_ops *tr; @@ -174,7 +174,7 @@ return ret; } -int blktrans_release(struct inode *i, struct file *f) +static int blktrans_release(struct inode *i, struct file *f) { struct mtd_blktrans_dev *dev; struct mtd_blktrans_ops *tr; @@ -326,7 +326,7 @@ return 0; } -void blktrans_notify_remove(struct mtd_info *mtd) +static void blktrans_notify_remove(struct mtd_info *mtd) { struct list_head *this, *this2, *next; @@ -342,7 +342,7 @@ } } -void blktrans_notify_add(struct mtd_info *mtd) +static void blktrans_notify_add(struct mtd_info *mtd) { struct list_head *this; diff -Nur linux-2.6.9-intc1/drivers/mtd/mtdblock.c linux-2.6.9-intc1-mih1/drivers/mtd/mtdblock.c --- linux-2.6.9-intc1/drivers/mtd/mtdblock.c 2004-10-18 17:53:51.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/mtdblock.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,7 +1,7 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.66 2004/11/25 13:52:52 joern Exp $ * * (C) 2000-2003 Nicolas Pitre * (C) 1999-2003 David Woodhouse @@ -248,7 +248,7 @@ unsigned long block, char *buf) { struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; - if (unlikely(!mtdblk->cache_data)) { + if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); if (!mtdblk->cache_data) return -EINTR; @@ -361,7 +361,7 @@ kfree(dev); } -struct mtd_blktrans_ops mtdblock_tr = { +static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, @@ -375,7 +375,7 @@ .owner = THIS_MODULE, }; -int __init init_mtdblock(void) +static int __init init_mtdblock(void) { return register_mtd_blktrans(&mtdblock_tr); } diff -Nur linux-2.6.9-intc1/drivers/mtd/mtdblock_ro.c linux-2.6.9-intc1-mih1/drivers/mtd/mtdblock_ro.c --- linux-2.6.9-intc1/drivers/mtd/mtdblock_ro.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/mtdblock_ro.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mtdblock_ro.c,v 1.18 2003/06/23 12:00:08 dwmw2 Exp $ + * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $ * * (C) 2003 David Woodhouse * @@ -58,7 +58,7 @@ kfree(dev); } -struct mtd_blktrans_ops mtdblock_tr = { +static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, diff -Nur linux-2.6.9-intc1/drivers/mtd/mtdchar.c linux-2.6.9-intc1-mih1/drivers/mtd/mtdchar.c --- linux-2.6.9-intc1/drivers/mtd/mtdchar.c 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/mtdchar.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.64 2004/08/09 13:59:46 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.69 2005/03/17 19:56:08 gleixner Exp $ * * Character-device access to raw MTD devices. * @@ -59,9 +59,25 @@ #define mtdchar_devfs_exit() do { } while(0) #endif +/* + * We use file->private_data to store a pointer to the MTDdevice. + * Since alighment is at least 32 bits, we have 2 bits free for OTP + * modes as well. + */ + +#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) + +#define MTD_MODE_OTP_FACT 1 +#define MTD_MODE_OTP_USER 2 +#define MTD_MODE(file) ((long)((file)->private_data) & 3) + +#define SET_MTD_MODE(file, mode) \ + do { long __p = (long)((file)->private_data); \ + (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) + static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd=(struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); switch (orig) { case 0: @@ -134,7 +150,7 @@ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = (struct mtd_info *)file->private_data; + mtd = TO_MTD(file); if (mtd->sync) mtd->sync(mtd); @@ -151,7 +167,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); size_t retlen=0; size_t total_retlen=0; int ret=0; @@ -178,8 +194,23 @@ if (!kbuf) return -ENOMEM; - ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); - if (!ret) { + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + case MTD_MODE_OTP_USER: + ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: + ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + } + /* Nand returns -EBADMSG on ecc errors, but it returns + * the data. For our userspace tools it is important + * to dump areas with ecc errors ! + * Userspace software which accesses NAND this way + * must be aware of the fact that it deals with NAND + */ + if (!ret || (ret == -EBADMSG)) { *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); @@ -190,6 +221,8 @@ count -= retlen; buf += retlen; + if (retlen == 0) + count = 0; } else { kfree(kbuf); @@ -198,13 +231,13 @@ kfree(kbuf); } - + return total_retlen; } /* mtd_read */ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); char *kbuf; size_t retlen; size_t total_retlen=0; @@ -239,7 +272,20 @@ return -EFAULT; } - ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = -EROFS; + break; + case MTD_MODE_OTP_USER: + if (!mtd->write_user_prot_reg) { + ret = -EOPNOTSUPP; + break; + } + ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: + ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + } if (!ret) { *ppos += retlen; total_retlen += retlen; @@ -270,7 +316,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); void __user *argp = (void __user *)arg; int ret = 0; u_long size; @@ -279,12 +325,12 @@ size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { - ret = verify_area(VERIFY_READ, argp, size); - if (ret) return ret; + if (!access_ok(VERIFY_READ, argp, size)) + return -EFAULT; } if (cmd & IOC_OUT) { - ret = verify_area(VERIFY_WRITE, argp, size); - if (ret) return ret; + if (!access_ok(VERIFY_WRITE, argp, size)) + return -EFAULT; } switch (cmd) { @@ -383,7 +429,8 @@ if (!mtd->write_oob) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_READ, buf.ptr, buf.length); + ret = access_ok(VERIFY_READ, buf.ptr, + buf.length) ? 0 : EFAULT; if (ret) return ret; @@ -422,7 +469,8 @@ if (!mtd->read_oob) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_WRITE, buf.ptr, buf.length); + ret = access_ok(VERIFY_WRITE, buf.ptr, + buf.length) ? 0 : -EFAULT; if (ret) return ret; @@ -510,6 +558,79 @@ break; } +#ifdef CONFIG_MTD_OTP + case OTPSELECT: + { + int mode; + if (copy_from_user(&mode, argp, sizeof(int))) + return -EFAULT; + SET_MTD_MODE(file, 0); + switch (mode) { + case MTD_OTP_FACTORY: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_FACT); + break; + case MTD_OTP_USER: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_USER); + break; + default: + ret = -EINVAL; + case MTD_OTP_OFF: + break; + } + break; + } + + case OTPGETREGIONCOUNT: + case OTPGETREGIONINFO: + { + struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = -EOPNOTSUPP; + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + if (mtd->get_fact_prot_info) + ret = mtd->get_fact_prot_info(mtd, buf, 4096); + break; + case MTD_MODE_OTP_USER: + if (mtd->get_user_prot_info) + ret = mtd->get_user_prot_info(mtd, buf, 4096); + break; + } + if (ret >= 0) { + if (cmd == OTPGETREGIONCOUNT) { + int nbr = ret / sizeof(struct otp_info); + ret = copy_to_user(argp, &nbr, sizeof(int)); + } else + ret = copy_to_user(argp, buf, ret); + if (ret) + ret = -EFAULT; + } + kfree(buf); + break; + } + + case OTPLOCK: + { + struct otp_info info; + + if (MTD_MODE(file) != MTD_MODE_OTP_USER) + return -EINVAL; + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + if (!mtd->lock_user_prot_reg) + return -EOPNOTSUPP; + ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); + break; + } +#endif + default: ret = -ENOTTY; } diff -Nur linux-2.6.9-intc1/drivers/mtd/mtdcore.c linux-2.6.9-intc1-mih1/drivers/mtd/mtdcore.c --- linux-2.6.9-intc1/drivers/mtd/mtdcore.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/mtdcore.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.43 2004/07/23 15:20:46 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.45 2005/02/18 14:34:50 dedekind Exp $ * * Core registration and callback routines for MTD * drivers and users. @@ -149,8 +149,8 @@ } /** - * register_mtd_user - unregister a 'user' of MTD devices. - * @new: pointer to notifier info structure + * unregister_mtd_user - unregister a 'user' of MTD devices. + * @old: pointer to notifier info structure * * Removes a callback function pair from the list of 'users' to be * notified upon addition or removal of MTD devices. Causes the @@ -382,7 +382,7 @@ /*====================================================================*/ /* Init code */ -int __init init_mtd(void) +static int __init init_mtd(void) { #ifdef CONFIG_PROC_FS if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) diff -Nur linux-2.6.9-intc1/drivers/mtd/mtdpart.c linux-2.6.9-intc1-mih1/drivers/mtd/mtdpart.c --- linux-2.6.9-intc1/drivers/mtd/mtdpart.c 2004-10-18 17:54:40.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/mtdpart.c 2005-03-21 21:28:41.000000000 -0500 @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.50 2004/08/10 16:18:34 dwmw2 Exp $ + * $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $ * * 02-21-2002 Thomas Gleixner * added support for read_oob, write_oob @@ -116,6 +116,13 @@ len, retlen, buf); } +static int part_get_user_prot_info (struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->get_user_prot_info (part->master, buf, len); +} + static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -124,6 +131,13 @@ len, retlen, buf); } +static int part_get_fact_prot_info (struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->get_fact_prot_info (part->master, buf, len); +} + static int part_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { @@ -182,6 +196,12 @@ len, retlen, buf); } +static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->lock_user_prot_reg (part->master, from, len); +} + static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { @@ -409,6 +429,12 @@ slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; if(master->write_user_prot_reg) slave->mtd.write_user_prot_reg = part_write_user_prot_reg; + if(master->lock_user_prot_reg) + slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; + if(master->get_user_prot_info) + slave->mtd.get_user_prot_info = part_get_user_prot_info; + if(master->get_fact_prot_info) + slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { @@ -523,10 +549,10 @@ EXPORT_SYMBOL(add_mtd_partitions); EXPORT_SYMBOL(del_mtd_partitions); -static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); -struct mtd_part_parser *get_partition_parser(const char *name) +static struct mtd_part_parser *get_partition_parser(const char *name) { struct list_head *this; void *ret = NULL; diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/au1550nd.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/au1550nd.c --- linux-2.6.9-intc1/drivers/mtd/nand/au1550nd.c 2004-10-18 17:53:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/au1550nd.c 2005-03-21 21:28:43.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (C) 2004 Embedded Edge, LLC * - * $Id: au1550nd.c,v 1.5 2004/05/17 07:19:35 ppopov Exp $ + * $Id: au1550nd.c,v 1.11 2004/11/04 12:53:10 gleixner Exp $ * * 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 @@ -18,6 +18,17 @@ #include #include #include + +/* fixme: this is ugly */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) +#include +#ifdef CONFIG_MIPS_PB1550 +#include +#endif +#ifdef CONFIG_MIPS_DB1550 +#include +#endif +#else #include #ifdef CONFIG_MIPS_PB1550 #include @@ -25,18 +36,16 @@ #ifdef CONFIG_MIPS_DB1550 #include #endif - +#endif /* * MTD structure for NAND controller */ static struct mtd_info *au1550_mtd = NULL; -static volatile u32 p_nand; -static int nand_width = 1; /* default, only x8 supported for now */ +static void __iomem *p_nand; +static int nand_width = 1; /* default x8*/ -/* Internal buffers. Page buffer and oob buffer for one block*/ -static u_char data_buf[512 + 16]; -static u_char oob_buf[16 * 32]; +#define NAND_CS 1 /* * Define partitions for flash device @@ -70,183 +79,262 @@ #endif }; -static inline void write_cmd_reg(u8 cmd) + +/** + * au_read_byte - read one byte from the chip + * @mtd: MTD device structure + * + * read function for 8bit buswith + */ +static u_char au_read_byte(struct mtd_info *mtd) { - if (nand_width) - *((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd; - else - *((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd; + struct nand_chip *this = mtd->priv; + u_char ret = readb(this->IO_ADDR_R); au_sync(); + return ret; } -static inline void write_addr_reg(u8 addr) +/** + * au_write_byte - write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 8it buswith + */ +static void au_write_byte(struct mtd_info *mtd, u_char byte) { - if (nand_width) - *((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr; - else - *((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr; + struct nand_chip *this = mtd->priv; + writeb(byte, this->IO_ADDR_W); au_sync(); } -static inline void write_data_reg(u8 data) +/** + * au_read_byte16 - read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswith with + * endianess conversion + */ +static u_char au_read_byte16(struct mtd_info *mtd) { - if (nand_width) - *((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data; - else - *((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data; + struct nand_chip *this = mtd->priv; + u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); au_sync(); + return ret; } -static inline u32 read_data_reg(void) +/** + * au_write_byte16 - write one byte endianess aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 16bit buswith with + * endianess conversion + */ +static void au_write_byte16(struct mtd_info *mtd, u_char byte) { - u32 data; - if (nand_width) { - data = *((volatile u8 *)(p_nand + MEM_STNAND_DATA)); - au_sync(); - } - else { - data = *((volatile u16 *)(p_nand + MEM_STNAND_DATA)); - au_sync(); - } - return data; + struct nand_chip *this = mtd->priv; + writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); + au_sync(); } -void au1550_hwcontrol(struct mtd_info *mtd, int cmd) +/** + * au_read_word - read one word from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswith without + * endianess conversion + */ +static u16 au_read_word(struct mtd_info *mtd) { + struct nand_chip *this = mtd->priv; + u16 ret = readw(this->IO_ADDR_R); + au_sync(); + return ret; } -int au1550_device_ready(struct mtd_info *mtd) +/** + * au_write_word - write one word to the chip + * @mtd: MTD device structure + * @word: data word to write + * + * write function for 16bit buswith without + * endianess conversion + */ +static void au_write_word(struct mtd_info *mtd, u16 word) { - int ready; - ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0; - return ready; + struct nand_chip *this = mtd->priv; + writew(word, this->IO_ADDR_W); + au_sync(); } -static u_char au1550_nand_read_byte(struct mtd_info *mtd) +/** + * au_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 8bit buswith + */ +static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { - u_char ret; - ret = read_data_reg(); - return ret; -} + int i; + struct nand_chip *this = mtd->priv; -static void au1550_nand_write_byte(struct mtd_info *mtd, u_char byte) -{ - write_data_reg((u8)byte); + for (i=0; iIO_ADDR_W); + au_sync(); + } } -static void -au1550_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +/** + * au_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 8bit buswith + */ +static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) { int i; + struct nand_chip *this = mtd->priv; - for (i=0; iIO_ADDR_R); + au_sync(); + } } -static void -au1550_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +/** + * au_verify_buf - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * verify function for 8bit buswith + */ +static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + au_sync(); + } - for (i=0; ipriv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_W); + au_sync(); + } + +} - for (i=0; ipriv; + u16 *p = (u16 *) buf; + len >>= 1; - return 0; + for (i=0; iIO_ADDR_R); + au_sync(); + } } -static void au1550_nand_select_chip(struct mtd_info *mtd, int chip) +/** + * au_verify_buf16 - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * verify function for 16bit buswith + */ +static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) { - switch(chip) { - case -1: - /* deassert chip enable */ - au_writel(au_readl(MEM_STNDCTL) & ~0x20 , MEM_STNDCTL); - break; - case 0: - /* assert (force assert) chip enable */ - au_writel(au_readl(MEM_STNDCTL) | 0x20 , MEM_STNDCTL); - break; + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; - default: - BUG(); + for (i=0; iIO_ADDR_R)) + return -EFAULT; + au_sync(); } + return 0; } -static void au1550_nand_command (struct mtd_info *mtd, unsigned command, - int column, int page_addr) + +static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) { register struct nand_chip *this = mtd->priv; - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->oobblock) { - /* OOB area */ - column -= mtd->oobblock; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - write_cmd_reg(readcmd); - } - write_cmd_reg(command); - - if (column != -1 || page_addr != -1) { - - /* Serially input address */ - if (column != -1) - write_addr_reg(column); - if (page_addr != -1) { - write_addr_reg((unsigned char) (page_addr & 0xff)); - write_addr_reg(((page_addr >> 8) & 0xff)); - /* One more address cycle for higher density devices */ - if (mtd->size & 0x0c000000) - write_addr_reg((unsigned char) ((page_addr >> 16) & 0x0f)); - } - } - - switch (command) { - - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: + switch(cmd){ + + case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break; + case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break; + + case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break; + case NAND_CTL_CLRALE: + this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; + /* FIXME: Nobody knows why this is neccecary, + * but it works only that way */ + udelay(1); break; - case NAND_CMD_RESET: - if (this->dev_ready) - break; - udelay(this->chip_delay); - write_cmd_reg(NAND_CMD_STATUS); - while ( !(read_data_reg() & 0x40)); - return; - - /* This applies to read commands */ - default: - udelay (this->chip_delay); + case NAND_CTL_SETNCE: + /* assert (force assert) chip enable */ + au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break; + break; + + case NAND_CTL_CLRNCE: + /* deassert chip enable */ + au_writel(0, MEM_STNDCTL); break; + break; } + + this->IO_ADDR_R = this->IO_ADDR_W; - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + /* Drain the writebuffer */ + au_sync(); } +int au1550_device_ready(struct mtd_info *mtd) +{ + int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0; + au_sync(); + return ret; +} /* * Main initialization routine @@ -255,7 +343,7 @@ { struct nand_chip *this; u16 boot_swapboot = 0; /* default value */ - u32 mem_time; + int retval; /* Allocate memory for MTD device structure and private data */ au1550_mtd = kmalloc (sizeof(struct mtd_info) + @@ -275,11 +363,9 @@ /* Link the private data with the MTD structure */ au1550_mtd->priv = this; - /* disable interrupts */ - au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL); - /* disable NAND boot */ - au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL); + /* MEM_STNDCTL: disable ints, disable nand boot */ + au_writel(0, MEM_STNDCTL); #ifdef CONFIG_MIPS_PB1550 /* set gpio206 high */ @@ -295,7 +381,6 @@ case 0xD: /* x16 NAND Flash */ nand_width = 0; - printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n"); break; case 1: case 9: @@ -307,62 +392,62 @@ break; default: printk("Pb1550 NAND: bad boot:swap\n"); - kfree(au1550_mtd); - return 1; + retval = -EINVAL; + goto outmem; } +#endif /* Configure RCE1 - should be done by YAMON */ - au_writel(0x5 | (nand_width << 22), MEM_STCFG1); - au_writel(NAND_TIMING, MEM_STTIME1); - mem_time = au_readl(MEM_STTIME1); + au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */ + au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */ au_sync(); - /* setup and enable chip select */ + /* setup and enable chip select, MEM_STADDR1 */ /* we really need to decode offsets only up till 0x20 */ au_writel((1<<28) | (NAND_PHYS_ADDR>>4) | (((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18), MEM_STADDR1); au_sync(); -#endif - -#ifdef CONFIG_MIPS_DB1550 - /* Configure RCE1 - should be done by YAMON */ - au_writel(0x00400005, MEM_STCFG1); - au_writel(0x00007774, MEM_STTIME1); - au_writel(0x12000FFF, MEM_STADDR1); -#endif - p_nand = (volatile struct nand_regs *)ioremap(NAND_PHYS_ADDR, 0x1000); + p_nand = ioremap(NAND_PHYS_ADDR, 0x1000); /* Set address of hardware control function */ this->hwcontrol = au1550_hwcontrol; this->dev_ready = au1550_device_ready; /* 30 us command delay time */ this->chip_delay = 30; - - this->cmdfunc = au1550_nand_command; - this->select_chip = au1550_nand_select_chip; - this->write_byte = au1550_nand_write_byte; - this->read_byte = au1550_nand_read_byte; - this->write_buf = au1550_nand_write_buf; - this->read_buf = au1550_nand_read_buf; - this->verify_buf = au1550_nand_verify_buf; this->eccmode = NAND_ECC_SOFT; - /* Set internal data buffer */ - this->data_buf = data_buf; - this->oob_buf = oob_buf; + this->options = NAND_NO_AUTOINCR; + + if (!nand_width) + this->options |= NAND_BUSWIDTH_16; + + this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; + this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; + this->write_word = au_write_word; + this->read_word = au_read_word; + this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; + this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; + this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf; /* Scan to find existence of the device */ if (nand_scan (au1550_mtd, 1)) { - kfree (au1550_mtd); - return -ENXIO; + retval = -ENXIO; + goto outio; } /* Register the partitions */ add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS); return 0; + + outio: + iounmap ((void *)p_nand); + + outmem: + kfree (au1550_mtd); + return retval; } module_init(au1550_init); @@ -375,16 +460,14 @@ { struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1]; - iounmap ((void *)p_nand); - - /* Unregister partitions */ - del_mtd_partitions(au1550_mtd); - - /* Unregister the device */ - del_mtd_device (au1550_mtd); + /* Release resources, unregister device */ + nand_release (au1550_mtd); /* Free the MTD device structure */ kfree (au1550_mtd); + + /* Unmap */ + iounmap ((void *)p_nand); } module_exit(au1550_cleanup); #endif diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/autcpu12.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/autcpu12.c --- linux-2.6.9-intc1/drivers/mtd/nand/autcpu12.c 2004-10-18 17:53:22.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/autcpu12.c 2005-03-21 21:28:43.000000000 -0500 @@ -6,7 +6,7 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.20 2004/07/20 02:44:26 dwmw2 Exp $ + * $Id: autcpu12.c,v 1.22 2004/11/04 12:53:10 gleixner Exp $ * * 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 @@ -48,17 +48,7 @@ static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC; static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET; static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET; -static int autcpu12_fio_base; - -#ifdef MODULE -MODULE_PARM(autcpu12_fio_pbase, "i"); -MODULE_PARM(autcpu12_fio_ctrl, "i"); -MODULE_PARM(autcpu12_pedr, "i"); - -__setup("autcpu12_fio_pbase=",autcpu12_fio_pbase); -__setup("autcpu12_fio_ctrl=",autcpu12_fio_ctrl); -__setup("autcpu12_pedr=",autcpu12_pedr); -#endif +static void __iomem * autcpu12_fio_base; /* * Define partitions for flash devices @@ -150,7 +140,7 @@ } /* map physical adress */ - autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K); + autcpu12_fio_base = ioremap(autcpu12_fio_pbase,SZ_1K); if(!autcpu12_fio_base){ printk("Ioremap autcpu12 SmartMedia Card failed\n"); err = -EIO; diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/diskonchip.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/diskonchip.c --- linux-2.6.9-intc1/drivers/mtd/nand/diskonchip.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/diskonchip.c 2005-03-21 21:28:43.000000000 -0500 @@ -8,16 +8,23 @@ * Author: David Woodhouse * Additional Diskonchip 2000 and Millennium support by Dan Brown * Diskonchip Millennium Plus support by Kalev Lember - * + * + * Error correction code lifted from the old docecc code + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * converted to the generic Reed-Solomon library by Thomas Gleixner + * * Interface to generic NAND code for M-Systems DiskOnChip devices * - * $Id: diskonchip.c,v 1.34 2004/08/09 19:41:12 dbrown Exp $ + * $Id: diskonchip.c,v 1.49 2005/02/22 21:48:21 gleixner Exp $ */ #include #include #include #include +#include +#include #include #include @@ -28,13 +35,13 @@ #include /* Where to look for the devices? */ -#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS -#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 #endif static unsigned long __initdata doc_locations[] = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, @@ -62,7 +69,7 @@ static struct mtd_info *doclist = NULL; struct doc_priv { - unsigned long virtadr; + void __iomem *virtadr; unsigned long physadr; u_char ChipID; u_char CDSNControl; @@ -96,28 +103,136 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip); static int debug=0; -MODULE_PARM(debug, "i"); +module_param(debug, int, 0); static int try_dword=1; -MODULE_PARM(try_dword, "i"); +module_param(try_dword, int, 0); static int no_ecc_failures=0; -MODULE_PARM(no_ecc_failures, "i"); +module_param(no_ecc_failures, int, 0); +#ifdef CONFIG_MTD_PARTITIONS static int no_autopart=0; -MODULE_PARM(no_autopart, "i"); +module_param(no_autopart, int, 0); +#endif #ifdef MTD_NAND_DISKONCHIP_BBTWRITE static int inftl_bbt_write=1; #else static int inftl_bbt_write=0; #endif -MODULE_PARM(inftl_bbt_write, "i"); +module_param(inftl_bbt_write, int, 0); -static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; -MODULE_PARM(doc_config_location, "l"); +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; +module_param(doc_config_location, ulong, 0); MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); + +/* Sector size for HW ECC */ +#define SECTOR_SIZE 512 +/* The sector bytes are packed into NB_DATA 10 bit words */ +#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) +/* Number of roots */ +#define NROOTS 4 +/* First consective root */ +#define FCR 510 +/* Number of symbols */ +#define NN 1023 + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * The HW decoder in the DoC ASIC's provides us a error syndrome, + * which we must convert to a standard syndrom usable by the generic + * Reed-Solomon library code. + * + * Fabrice Bellard figured this out in the old docecc code. I added + * some comments, improved a minor bit and converted it to make use + * of the generic Reed-Solomon libary. tglx + */ +static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) +{ + int i, j, nerr, errpos[8]; + uint8_t parity; + uint16_t ds[4], s[5], tmp, errval[8], syn[4]; + + /* Convert the ecc bytes into words */ + ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); + ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); + ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); + ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); + parity = ecc[1]; + + /* Initialize the syndrom buffer */ + for (i = 0; i < NROOTS; i++) + s[i] = ds[0]; + /* + * Evaluate + * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] + * where x = alpha^(FCR + i) + */ + for(j = 1; j < NROOTS; j++) { + if(ds[j] == 0) + continue; + tmp = rs->index_of[ds[j]]; + for(i = 0; i < NROOTS; i++) + s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; + } + + /* Calc s[i] = s[i] / alpha^(v + i) */ + for (i = 0; i < NROOTS; i++) { + if (syn[i]) + syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); + } + /* Call the decoder library */ + nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); + + /* Incorrectable errors ? */ + if (nerr < 0) + return nerr; + + /* + * Correct the errors. The bitpositions are a bit of magic, + * but they are given by the design of the de/encoder circuit + * in the DoC ASIC's. + */ + for(i = 0;i < nerr; i++) { + int index, bitpos, pos = 1015 - errpos[i]; + uint8_t val; + if (pos >= NB_DATA && pos < 1019) + continue; + if (pos < NB_DATA) { + /* extract bit position (MSB first) */ + pos = 10 * (NB_DATA - 1 - pos) - 6; + /* now correct the following 10 bits. At most two bytes + can be modified since pos is even */ + index = (pos >> 3) ^ 1; + bitpos = pos & 7; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] >> (2 + bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + index = ((pos >> 3) + 1) ^ 1; + bitpos = (bitpos + 10) & 7; + if (bitpos == 0) + bitpos = 8; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = (uint8_t)(errval[i] << (8 - bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + } + } + /* If the parity is wrong, no rescue possible */ + return parity ? -1 : nerr; +} + static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) { volatile char dummy; @@ -139,7 +254,7 @@ /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ static int _DoC_WaitReady(struct doc_priv *doc) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; unsigned long timeo = jiffies + (HZ * 10); if(debug) printk("_DoC_WaitReady...\n"); @@ -169,7 +284,7 @@ static inline int DoC_WaitReady(struct doc_priv *doc) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; int ret = 0; if (DoC_is_MillenniumPlus(doc)) { @@ -194,8 +309,8 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; if(debug)printk("write_byte %02x\n", datum); WriteDOC(datum, docptr, CDSNSlowIO); @@ -205,8 +320,8 @@ static u_char doc2000_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; u_char ret; ReadDOC(docptr, CDSNSlowIO); @@ -220,8 +335,8 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; if (debug)printk("writebuf of %d bytes: ", len); for (i=0; i < len; i++) { @@ -236,8 +351,8 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; if (debug)printk("readbuf of %d bytes: ", len); @@ -251,8 +366,8 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; if (debug) printk("readbuf_dword of %d bytes: ", len); @@ -272,8 +387,8 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; for (i=0; i < len; i++) @@ -285,7 +400,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; uint16_t ret; doc200x_select_chip(mtd, nr); @@ -295,7 +410,12 @@ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); this->write_byte(mtd, 0); doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); - + + /* We cant' use dev_ready here, but at least we wait for the + * command to complete + */ + udelay(50); + ret = this->read_byte(mtd) << 8; ret |= this->read_byte(mtd); @@ -305,7 +425,7 @@ uint32_t dword; uint8_t byte[4]; } ident; - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); doc2000_write_byte(mtd, NAND_CMD_READID); @@ -314,6 +434,8 @@ doc2000_write_byte(mtd, 0); doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + udelay(50); + ident.dword = readl(docptr + DoC_2k_CDSN_IO); if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); @@ -327,7 +449,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; uint16_t mfrid; int i; @@ -348,7 +470,7 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) { - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int status; @@ -363,8 +485,8 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; WriteDOC(datum, docptr, CDSNSlowIO); WriteDOC(datum, docptr, Mil_CDSN_IO); @@ -374,8 +496,8 @@ static u_char doc2001_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; //ReadDOC(docptr, CDSNSlowIO); /* 11.4.5 -- delay twice to allow extended length cycle */ @@ -389,8 +511,8 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; for (i=0; i < len; i++) @@ -403,8 +525,8 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; /* Start read pipeline */ @@ -421,8 +543,8 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; /* Start read pipeline */ @@ -441,8 +563,8 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; u_char ret; ReadDOC(docptr, Mplus_ReadPipeInit); @@ -456,8 +578,8 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; if (debug)printk("writebuf of %d bytes: ", len); @@ -473,8 +595,8 @@ u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; if (debug)printk("readbuf of %d bytes: ", len); @@ -503,8 +625,8 @@ const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; if (debug)printk("verifybuf of %d bytes: ", len); @@ -529,8 +651,8 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int floor = 0; if(debug)printk("select chip (%d)\n", chip); @@ -555,8 +677,8 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int floor = 0; if(debug)printk("select chip (%d)\n", chip); @@ -582,8 +704,8 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; switch(cmd) { case NAND_CTL_SETNCE: @@ -620,8 +742,8 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; /* * Must terminate write pipeline before sending any commands @@ -724,8 +846,8 @@ static int doc200x_dev_ready(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; if (DoC_is_MillenniumPlus(doc)) { /* 11.4.2 -- must NOP four times before checking FR/B# */ @@ -762,8 +884,8 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ switch(mode) { @@ -781,8 +903,8 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ switch(mode) { @@ -802,8 +924,8 @@ unsigned char *ecc_code) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; int i; int emptymatch = 1; @@ -860,8 +982,8 @@ { int i, ret = 0; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; - unsigned long docptr = doc->virtadr; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; volatile u_char dummy; int emptymatch = 1; @@ -914,7 +1036,7 @@ erased block, in which case the ECC will not come out right. We'll suppress the error and tell the caller everything's OK. Because it is. */ - if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc); + if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); if (ret > 0) printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); } @@ -948,7 +1070,7 @@ const char *id, int findmirror) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); int ret; size_t retlen; @@ -991,7 +1113,7 @@ struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct NFTLMediaHeader *mh; @@ -1007,8 +1129,10 @@ if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; mh = (struct NFTLMediaHeader *) buf; -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); + mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); + mh->FormattedSize = le32_to_cpu(mh->FormattedSize); + printk(KERN_INFO " DataOrgID = %s\n" " NumEraseUnits = %d\n" " FirstPhysicalEUN = %d\n" @@ -1017,7 +1141,6 @@ mh->DataOrgID, mh->NumEraseUnits, mh->FirstPhysicalEUN, mh->FormattedSize, mh->UnitSizeFactor); -//#endif blocks = mtd->size >> this->phys_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); @@ -1060,10 +1183,6 @@ offs <<= this->page_shift; offs += mtd->erasesize; - //parts[0].name = " DiskOnChip Boot / Media Header partition"; - //parts[0].offset = 0; - //parts[0].size = offs; - parts[0].name = " DiskOnChip BDTL partition"; parts[0].offset = offs; parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; @@ -1087,7 +1206,7 @@ struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct INFTLMediaHeader *mh; @@ -1118,8 +1237,6 @@ mh->FormatFlags = le32_to_cpu(mh->FormatFlags); mh->PercentUsed = le32_to_cpu(mh->PercentUsed); -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) printk(KERN_INFO " bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" @@ -1137,7 +1254,6 @@ ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf, mh->PercentUsed); -//#endif vshift = this->phys_erase_shift + mh->BlockMultiplierBits; @@ -1163,8 +1279,6 @@ ip->spareUnits = le32_to_cpu(ip->spareUnits); ip->Reserved0 = le32_to_cpu(ip->Reserved0); -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) printk(KERN_INFO " PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" @@ -1174,16 +1288,15 @@ i, ip->virtualUnits, ip->firstUnit, ip->lastUnit, ip->flags, ip->spareUnits); -//#endif -/* +#if 0 if ((i == 0) && (ip->firstUnit > 0)) { parts[0].name = " DiskOnChip IPL / Media Header partition"; parts[0].offset = 0; parts[0].size = mtd->erasesize * ip->firstUnit; numparts = 1; } -*/ +#endif if (ip->flags & INFTL_BINARY) parts[numparts].name = " DiskOnChip BDK partition"; @@ -1212,7 +1325,7 @@ { int ret, numparts; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; struct mtd_partition parts[2]; memset((char *) parts, 0, sizeof(parts)); @@ -1251,7 +1364,7 @@ { int ret, numparts; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; struct mtd_partition parts[5]; if (this->numchips > doc->chips_per_floor) { @@ -1310,7 +1423,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = doc2000_write_byte; this->read_byte = doc2000_read_byte; @@ -1328,7 +1441,7 @@ static inline int __init doc2001_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = doc2001_write_byte; this->read_byte = doc2001_read_byte; @@ -1360,7 +1473,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = NULL; this->read_byte = doc2001plus_read_byte; @@ -1385,13 +1498,13 @@ struct mtd_info *mtd; struct nand_chip *nand; struct doc_priv *doc; - unsigned long virtadr; + void __iomem *virtadr; unsigned char save_control; unsigned char tmp, tmpb, tmpc; int reg, len, numchips; int ret = 0; - virtadr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN); + virtadr = ioremap(physadr, DOC_IOREMAP_LEN); if (!virtadr) { printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); return -EIO; @@ -1482,7 +1595,7 @@ unsigned char oldval; unsigned char newval; nand = mtd->priv; - doc = (void *)nand->priv; + doc = nand->priv; /* Use the alias resolution register to determine if this is in fact the same DOC aliased to a new address. If writes to one chip's alias resolution register change the value on @@ -1518,7 +1631,7 @@ sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); - mtd = kmalloc(len, GFP_KERNEL); + mtd = kmalloc(len, GFP_KERNEL); if (!mtd) { printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); ret = -ENOMEM; @@ -1531,10 +1644,10 @@ nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); nand->bbt_md = nand->bbt_td + 1; - mtd->priv = (void *) nand; + mtd->priv = nand; mtd->owner = THIS_MODULE; - nand->priv = (void *) doc; + nand->priv = doc; nand->select_chip = doc200x_select_chip; nand->hwcontrol = doc200x_hwcontrol; nand->dev_ready = doc200x_dev_ready; @@ -1543,7 +1656,7 @@ nand->enable_hwecc = doc200x_enable_hwecc; nand->calculate_ecc = doc200x_calculate_ecc; nand->correct_data = doc200x_correct_data; - //nand->data_buf + nand->autooob = &doc200x_oobinfo; nand->eccmode = NAND_ECC_HW6_512; nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; @@ -1585,17 +1698,51 @@ actually a DiskOnChip. */ WriteDOC(save_control, virtadr, DOCControl); fail: - iounmap((void *)virtadr); + iounmap(virtadr); return ret; } -int __init init_nanddoc(void) +static void release_nanddoc(void) { - int i; + struct mtd_info *mtd, *nextmtd; + struct nand_chip *nand; + struct doc_priv *doc; + + for (mtd = doclist; mtd; mtd = nextmtd) { + nand = mtd->priv; + doc = nand->priv; + + nextmtd = doc->nextdoc; + nand_release(mtd); + iounmap(doc->virtadr); + kfree(mtd); + } +} + +static int __init init_nanddoc(void) +{ + int i, ret = 0; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 510 + * primitve element to generate roots = 1 + * generator polinomial degree = 4 + */ + rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); + if (!rs_decoder) { + printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + return -ENOMEM; + } if (doc_config_location) { printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); - return doc_probe(doc_config_location); + ret = doc_probe(doc_config_location); + if (ret < 0) + goto outerr; } else { for (i=0; (doc_locations[i] != 0xffffffff); i++) { doc_probe(doc_locations[i]); @@ -1605,25 +1752,23 @@ found, so the user knows we at least tried. */ if (!doclist) { printk(KERN_INFO "No valid DiskOnChip devices found\n"); - return -ENODEV; + ret = -ENODEV; + goto outerr; } return 0; +outerr: + free_rs(rs_decoder); + return ret; } -void __exit cleanup_nanddoc(void) +static void __exit cleanup_nanddoc(void) { - struct mtd_info *mtd, *nextmtd; - struct nand_chip *nand; - struct doc_priv *doc; - - for (mtd = doclist; mtd; mtd = nextmtd) { - nand = mtd->priv; - doc = (void *)nand->priv; + /* Cleanup the nand/DoC resources */ + release_nanddoc(); - nextmtd = doc->nextdoc; - nand_release(mtd); - iounmap((void *)doc->virtadr); - kfree(mtd); + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); } } diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/edb7312.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/edb7312.c --- linux-2.6.9-intc1/drivers/mtd/nand/edb7312.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/edb7312.c 2005-03-21 21:28:43.000000000 -0500 @@ -6,7 +6,7 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.8 2004/07/12 15:03:26 dwmw2 Exp $ + * $Id: edb7312.c,v 1.11 2004/11/04 12:53:10 gleixner Exp $ * * 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 @@ -53,19 +53,9 @@ * Module stuff */ -static int ep7312_fio_pbase = EP7312_FIO_PBASE; -static int ep7312_pxdr = EP7312_PXDR; -static int ep7312_pxddr = EP7312_PXDDR; - -#ifdef MODULE -MODULE_PARM(ep7312_fio_pbase, "i"); -MODULE_PARM(ep7312_pxdr, "i"); -MODULE_PARM(ep7312_pxddr, "i"); - -__setup("ep7312_fio_pbase=",ep7312_fio_pbase); -__setup("ep7312_pxdr=",ep7312_pxdr); -__setup("ep7312_pxddr=",ep7312_pxddr); -#endif +static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE; +static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR; +static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR; #ifdef CONFIG_MTD_PARTITIONS /* @@ -131,7 +121,7 @@ const char *part_type = 0; int mtd_parts_nb = 0; struct mtd_partition *mtd_parts = 0; - int ep7312_fio_base; + void __iomem * ep7312_fio_base; /* Allocate memory for MTD device structure and private data */ ep7312_mtd = kmalloc(sizeof(struct mtd_info) + @@ -143,7 +133,7 @@ } /* map physical adress */ - ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K); + ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K); if(!ep7312_fio_base) { printk("ioremap EDB7312 NAND flash failed\n"); kfree(ep7312_mtd); @@ -181,16 +171,7 @@ return -ENXIO; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk("Unable to allocate NAND data buffer for EDB7312.\n"); - iounmap((void *)ep7312_fio_base); - kfree (ep7312_mtd); - return -ENOMEM; - } - -#ifdef CONFIG_PARTITIONS +#ifdef CONFIG_MTD_PARTITIONS ep7312_mtd->name = "edb7312-nand"; mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, &mtd_parts, 0); @@ -221,8 +202,8 @@ { struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1]; - /* Unregister the device */ - del_mtd_device (ep7312_mtd); + /* Release resources, unregister device */ + nand_release (ap7312_mtd); /* Free internal data buffer */ kfree (this->data_buf); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/h1910.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/h1910.c --- linux-2.6.9-intc1/drivers/mtd/nand/h1910.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/h1910.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,208 @@ +/* + * drivers/mtd/nand/h1910.c + * + * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com) + * + * Derived from drivers/mtd/nand/edb7312.c + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: h1910.c,v 1.5 2004/11/04 12:53:10 gleixner Exp $ + * + * 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. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is + * a 128Mibit (16MiB x 8 bits) NAND flash device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for CLPS7111_VIRT_BASE */ +#include +#include +#include + +/* + * MTD structure for EDB7312 board + */ +static struct mtd_info *h1910_nand_mtd = NULL; + +/* + * Module stuff + */ + +#ifdef CONFIG_MTD_PARTITIONS +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info[] = { + { name: "h1910 NAND Flash", + offset: 0, + size: 16*1024*1024 } +}; +#define NUM_PARTITIONS 1 + +#endif + + +/* + * hardware specific access to control-lines + */ +static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip* this = (struct nand_chip *) (mtd->priv); + + switch(cmd) { + + case NAND_CTL_SETCLE: + this->IO_ADDR_R |= (1 << 2); + this->IO_ADDR_W |= (1 << 2); + break; + case NAND_CTL_CLRCLE: + this->IO_ADDR_R &= ~(1 << 2); + this->IO_ADDR_W &= ~(1 << 2); + break; + + case NAND_CTL_SETALE: + this->IO_ADDR_R |= (1 << 3); + this->IO_ADDR_W |= (1 << 3); + break; + case NAND_CTL_CLRALE: + this->IO_ADDR_R &= ~(1 << 3); + this->IO_ADDR_W &= ~(1 << 3); + break; + + case NAND_CTL_SETNCE: + break; + case NAND_CTL_CLRNCE: + break; + } +} + +/* + * read device ready pin + */ +#if 0 +static int h1910_device_ready(struct mtd_info *mtd) +{ + return (GPLR(55) & GPIO_bit(55)); +} +#endif + +/* + * Main initialization routine + */ +static int __init h1910_init (void) +{ + struct nand_chip *this; + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + void __iomem *nandaddr; + + if (!machine_is_h1900()) + return -ENODEV; + + nandaddr = __ioremap(0x08000000, 0x1000, 0, 1); + if (!nandaddr) { + printk("Failed to ioremap nand flash.\n"); + return -ENOMEM; + } + + /* Allocate memory for MTD device structure and private data */ + h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), + GFP_KERNEL); + if (!h1910_nand_mtd) { + printk("Unable to allocate h1910 NAND MTD device structure.\n"); + iounmap ((void *) nandaddr); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&h1910_nand_mtd[1]); + + /* Initialize structures */ + memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + h1910_nand_mtd->priv = this; + + /* + * Enable VPEN + */ + GPSR(37) = GPIO_bit(37); + + /* insert callbacks */ + this->IO_ADDR_R = nandaddr; + this->IO_ADDR_W = nandaddr; + this->hwcontrol = h1910_hwcontrol; + this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ + /* 15 us command delay time */ + this->chip_delay = 50; + this->eccmode = NAND_ECC_SOFT; + this->options = NAND_NO_AUTOINCR; + + /* Scan to find existence of the device */ + if (nand_scan (h1910_nand_mtd, 1)) { + printk(KERN_NOTICE "No NAND device - returning -ENXIO\n"); + kfree (h1910_nand_mtd); + iounmap ((void *) nandaddr); + return -ENXIO; + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, + "h1910-nand"); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb); + + /* Return happy */ + return 0; +} +module_init(h1910_init); + +/* + * Clean up routine + */ +static void __exit h1910_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1]; + + /* Release resources, unregister device */ + nand_release (h1910_nand_mtd); + + /* Release io resource */ + iounmap ((void *) this->IO_ADDR_W); + + /* Free the MTD device structure */ + kfree (h1910_nand_mtd); +} +module_exit(h1910_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joshua Wise "); +MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910"); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/Kconfig linux-2.6.9-intc1-mih1/drivers/mtd/nand/Kconfig --- linux-2.6.9-intc1/drivers/mtd/nand/Kconfig 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/Kconfig 2005-03-23 11:56:45.000000000 -0500 @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.17 2004/08/10 14:24:07 dwmw2 Exp $ +# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -7,6 +7,7 @@ config MTD_NAND tristate "NAND Device Support" depends on MTD + select MTD_NAND_IDS help This enables support for accessing all type of NAND flash devices. For further information see @@ -36,6 +37,12 @@ This enables the driver for the Cirrus Logic EBD7312 evaluation board to access the onboard NAND Flash. +config MTD_NAND_H1900 + tristate "iPAQ H1900 flash" + depends on ARM && MTD_NAND && ARCH_PXA && MTD_PARTITIONS + help + This enables the driver for the iPAQ h1900 flash. + config MTD_NAND_SPIA tristate "NAND Flash device on SPIA board" depends on ARM && ARCH_P720T && MTD_NAND @@ -50,8 +57,6 @@ config MTD_NAND_IDS tristate - default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y - default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m config MTD_NAND_TX4925NDFMC tristate "SmartMedia Card on Toshiba RBTX4925 reference board" @@ -74,15 +79,54 @@ This enables the driver for the NAND flash controller on the AMD/Alchemy 1550 SOC. +config MTD_NAND_RTC_FROM4 + tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" + depends on MTD_NAND && SH_SOLUTION_ENGINE +# select REED_SOLOMON +# select REED_SOLOMON_DEC8 + help + This enables the driver for the Renesas Technology AG-AND + flash interface board (FROM_BOARD4) + config MTD_NAND_PPCHAMELEONEVB tristate "NAND Flash device on PPChameleonEVB board" depends on PPCHAMELEONEVB && MTD_NAND help - This enables the NAND flash driver on the PPChameleon EVB Board. + This enables the NAND flash driver on the PPChameleon EVB Board. + +config MTD_NAND_S3C2410 + tristate "NAND Flash support for S3C2410 SoC" + depends on ARCH_S3C2410 && MTD_NAND + help + This enables the NAND flash controller on the S3C2410. + + No board specfic support is done by this driver, each board + must advertise a platform_device for the driver to attach. + +config MTD_NAND_S3C2410_DEBUG + bool "S3C2410 NAND driver debug" + depends on MTD_NAND_S3C2410 + help + Enable debugging of the S3C2410 NAND driver + +config MTD_NAND_S3C2410_HWECC + bool "S3C2410 NAND Hardware ECC" + depends on MTD_NAND_S3C2410 + help + Enable the use of the S3C2410's internal ECC generator when + using NAND. Early versions of the chip have had problems with + incorrect ECC generation, and if using these, the default of + software ECC is preferable. + + If you lay down a device with the hardware ECC, then you will + currently not be able to switch to software, as there is no + implementation for ECC method used by the S3C2410 config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" depends on MTD_NAND && EXPERIMENTAL +# select REED_SOLOMON +# select REED_SOLOMON_DEC16 help This is a reimplementation of M-Systems DiskOnChip 2000, Millennium and Millennium Plus as a standard NAND device driver, @@ -147,4 +191,17 @@ Even if you leave this disabled, you can enable BBT writes at module load time (assuming you build diskonchip as a module) with the module parameter "inftl_bbt_write=1". + + config MTD_NAND_SHARPSL + bool "Support for NAND Flash on Sharp SL Series (C7xx + others)" + depends on MTD_NAND && ARCH_PXA + + config MTD_NAND_NANDSIM + bool "Support for NAND Flash Simulator" + depends on MTD_NAND && MTD_PARTITIONS + + help + The simulator may simulate verious NAND flash chips for the + MTD nand layer. + endmenu diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/Makefile linux-2.6.9-intc1-mih1/drivers/mtd/nand/Makefile --- linux-2.6.9-intc1/drivers/mtd/nand/Makefile 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/Makefile 2005-03-21 21:28:43.000000000 -0500 @@ -1,7 +1,7 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $ +# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -14,6 +14,11 @@ obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o +obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +obj-$(CONFIG_MTD_NAND_H1900) += h1910.o +obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o +obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o nand-objs = nand_base.o nand_bbt.o diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/nand_base.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/nand_base.c --- linux-2.6.9-intc1/drivers/mtd/nand/nand_base.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/nand_base.c 2005-03-21 21:28:43.000000000 -0500 @@ -24,6 +24,28 @@ * * 05-19-2004 tglx: Basic support for Renesas AG-AND chips * + * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared + * among multiple independend devices. Suggestions and initial patch + * from Ben Dooks + * + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. + * Basically, any block not rewritten may lose data when surrounding blocks + * are rewritten many times. JFFS2 ensures this doesn't happen for blocks + * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they + * do not lose data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby block (which + * could itself go bad), use a page address 'mask' to select several blocks + * in the same area, and rewrite the BBT when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas + * AG-AND chips. If there was a sudden loss of power during an erase operation, + * a "device recovery" operation must be performed when power is restored + * to ensure correct operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to + * perform extra error status checks on erase and write failures. This required + * adding a wrapper function for nand_read_ecc. + * * Credits: * David Woodhouse for adding multichip support * @@ -37,7 +59,7 @@ * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * - * $Id: nand_base.c,v 1.115 2004/08/09 13:19:45 dwmw2 Exp $ + * $Id: nand_base.c,v 1.136 2005/03/14 18:30:44 bjd Exp $ * * 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 @@ -131,25 +153,31 @@ #define nand_verify_pages(...) (0) #endif -static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state); +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); /** - * nand_release_chip - [GENERIC] release chip + * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */ -static void nand_release_chip (struct mtd_info *mtd) +static void nand_release_device (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; /* De-select the NAND device */ this->select_chip(mtd, -1); + /* Do we have a hardware controller ? */ + if (this->controller) { + spin_lock(&this->controller->lock); + this->controller->active = NULL; + spin_unlock(&this->controller->lock); + } /* Release the chip */ - spin_lock_bh (&this->chip_lock); + spin_lock (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); + spin_unlock (&this->chip_lock); } /** @@ -388,7 +416,7 @@ chipnr = (int)(ofs >> this->chip_shift); /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd, FL_READING); + nand_get_device (this, mtd, FL_READING); /* Select the NAND device */ this->select_chip(mtd, chipnr); @@ -410,7 +438,7 @@ if (getchip) { /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); } return res; @@ -433,7 +461,8 @@ /* Get block number */ block = ((int) ofs) >> this->bbt_erase_shift; - this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + if (this->bbt) + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ if (this->options & NAND_USE_FLASH_BBT) @@ -456,7 +485,7 @@ struct nand_chip *this = mtd->priv; /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); - return (this->read_byte(mtd) & 0x80) ? 0 : 1; + return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } /** @@ -480,6 +509,22 @@ return nand_isbad_bbt (mtd, ofs, allowbbt); } +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +static void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + unsigned long timeo = jiffies + 2; + + /* wait until command is processed or timeout occures */ + do { + if (this->dev_ready(mtd)) + return; + } while (time_before(jiffies, timeo)); +} + /** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure @@ -533,8 +578,8 @@ if (page_addr != -1) { this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); - /* One more address cycle for higher density devices */ - if (this->chipsize & 0x0c000000) + /* One more address cycle for devices > 32MiB */ + if (this->chipsize > (32 << 20)) this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); } /* Latch in address */ @@ -561,7 +606,7 @@ this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; /* This applies to read commands */ @@ -575,12 +620,11 @@ return; } } - /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + + nand_wait_ready(mtd); } /** @@ -609,7 +653,7 @@ /* Begin command latch cycle */ this->hwcontrol(mtd, NAND_CTL_SETCLE); /* Write out the command to the device. */ - this->write_byte(mtd, command); + this->write_byte(mtd, (command & 0xff)); /* End command latch cycle */ this->hwcontrol(mtd, NAND_CTL_CLRCLE); @@ -637,8 +681,8 @@ /* * program and erase have their own busy handlers - * status and sequential in needs no delay - */ + * status, sequential in, and deplete1 need no delay + */ switch (command) { case NAND_CMD_CACHEDPROG: @@ -647,8 +691,19 @@ case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: return; + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(this->chip_delay); + return; case NAND_CMD_RESET: if (this->dev_ready) @@ -657,7 +712,7 @@ this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; case NAND_CMD_READ0: @@ -680,24 +735,25 @@ return; } } - + /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + + nand_wait_ready(mtd); } /** - * nand_get_chip - [GENERIC] Get chip for selected access + * nand_get_device - [GENERIC] Get chip for selected access * @this: the nand chip descriptor * @mtd: MTD device structure * @new_state: the state which is requested * * Get the device and lock it for exclusive access */ -static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state) +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) { + struct nand_chip *active = this; DECLARE_WAITQUEUE (wait, current); @@ -705,19 +761,29 @@ * Grab the lock and see if the device is available */ retry: - spin_lock_bh (&this->chip_lock); - - if (this->state == FL_READY) { - this->state = new_state; - spin_unlock_bh (&this->chip_lock); - return; + /* Hardware controller shared among independend devices */ + if (this->controller) { + spin_lock (&this->controller->lock); + if (this->controller->active) + active = this->controller->active; + else + this->controller->active = this; + spin_unlock (&this->controller->lock); } - + + if (active == this) { + spin_lock (&this->chip_lock); + if (this->state == FL_READY) { + this->state = new_state; + spin_unlock (&this->chip_lock); + return; + } + } set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); + add_wait_queue (&active->wq, &wait); + spin_unlock (&active->chip_lock); schedule (); - remove_wait_queue (&this->wq, &wait); + remove_wait_queue (&active->wq, &wait); goto retry; } @@ -747,7 +813,6 @@ * any case on any machine. */ ndelay (100); - spin_lock_bh (&this->chip_lock); if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); else @@ -755,24 +820,19 @@ while (time_before(jiffies, timeo)) { /* Check, if we were interrupted */ - if (this->state != state) { - spin_unlock_bh (&this->chip_lock); + if (this->state != state) return 0; - } + if (this->dev_ready) { if (this->dev_ready(mtd)) + break; + } else { + if (this->read_byte(mtd) & NAND_STATUS_READY) break; } - if (this->read_byte(mtd) & NAND_STATUS_READY) - break; - - spin_unlock_bh (&this->chip_lock); - yield (); - spin_lock_bh (&this->chip_lock); + cond_resched(); } status = (int) this->read_byte(mtd); - spin_unlock_bh (&this->chip_lock); - return status; } @@ -795,7 +855,7 @@ u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) { int i, status; - u_char ecc_code[8]; + u_char ecc_code[32]; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; int *oob_config = oobsel->eccpos; int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; @@ -825,18 +885,8 @@ } this->write_buf(mtd, this->data_poi, mtd->oobblock); break; - - /* Hardware ecc 8 byte / 512 byte data */ - case NAND_ECC_HW8_512: - eccbytes += 2; - /* Hardware ecc 6 byte / 512 byte data */ - case NAND_ECC_HW6_512: - eccbytes += 3; - /* Hardware ecc 3 byte / 256 data */ - /* Hardware ecc 3 byte / 512 byte data */ - case NAND_ECC_HW3_256: - case NAND_ECC_HW3_512: - eccbytes += 3; + default: + eccbytes = this->eccbytes; for (; eccsteps; eccsteps--) { /* enable hardware ecc logic for write */ this->enable_hwecc(mtd, NAND_ECC_WRITE); @@ -849,14 +899,9 @@ * the data bytes (words) */ if (this->options & NAND_HWECC_SYNDROME) this->write_buf(mtd, ecc_code, eccbytes); - datidx += this->eccsize; } break; - - default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); - BUG(); } /* Write out OOB data */ @@ -871,8 +916,14 @@ if (!cached) { /* call wait ready function */ status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_WRITING, status, page); + } + /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); return -EIO; } @@ -975,7 +1026,7 @@ if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); /* All done, return happy */ if (!numpages) @@ -997,23 +1048,24 @@ #endif /** - * nand_read - [MTD Interface] MTD compability function for nand_read_ecc + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * - * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL -*/ + * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL + * and flags = 0xff + */ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { - return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); + return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, NULL, 0xff); } /** - * nand_read_ecc - [MTD Interface] Read data with ECC + * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read @@ -1022,11 +1074,35 @@ * @oob_buf: filesystem supplied oob data buffer * @oobsel: oob selection structure * - * NAND read with ECC + * This function simply calls nand_do_read_ecc with flags = 0xff */ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) { + return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); +} + + +/** + * nand_do_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed + * and how many corrected error bits are acceptable: + * bits 0..7 - number of tolerable errors + * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * + * NAND read with ECC + */ +int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags) +{ int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; @@ -1036,7 +1112,7 @@ int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; - int eccbytes = 3; + int eccbytes; int compareecc = 1; int oobreadlen; @@ -1051,7 +1127,8 @@ } /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd ,FL_READING); + if (flags & NAND_GET_DEVICE) + nand_get_device (this, mtd, FL_READING); /* use userspace supplied oobinfo, if zero */ if (oobsel == NULL) @@ -1077,19 +1154,9 @@ end = mtd->oobblock; ecc = this->eccsize; - switch (eccmode) { - case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */ - eccbytes = 6; - break; - case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */ - eccbytes = 8; - break; - case NAND_ECC_NONE: - compareecc = 0; - break; - } - - if (this->options & NAND_HWECC_SYNDROME) + eccbytes = this->eccbytes; + + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; @@ -1149,13 +1216,10 @@ for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); break; - - case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */ - case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */ - case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */ - case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */ + + default: for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { - this->enable_hwecc(mtd, NAND_ECC_READ); + this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, &data_poi[datidx], ecc); /* HW ecc with syndrome calculation must read the @@ -1168,7 +1232,8 @@ /* We calc error correction directly, it checks the hw * generator for an error, reads back the syndrome and * does the error correction on the fly */ - if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { + ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); ecc_failed++; @@ -1178,10 +1243,6 @@ } } break; - - default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); - BUG(); } /* read oobdata */ @@ -1211,7 +1272,7 @@ p[i] = ecc_status; } - if (ecc_status == -1) { + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } @@ -1256,7 +1317,7 @@ if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); if (read == len) break; @@ -1281,7 +1342,8 @@ } /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + if (flags & NAND_GET_DEVICE) + nand_release_device(mtd); /* * Return success, if no ECC failures, else -EBADMSG @@ -1328,7 +1390,7 @@ } /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd , FL_READING); + nand_get_device (this, mtd , FL_READING); /* Select the NAND device */ this->select_chip(mtd, chipnr); @@ -1354,7 +1416,7 @@ if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); /* Read more ? */ if (i < len) { @@ -1379,7 +1441,7 @@ } /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); /* Return happy */ *retlen = len; @@ -1413,7 +1475,7 @@ } /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd , FL_READING); + nand_get_device (this, mtd , FL_READING); this->select_chip (mtd, chip); @@ -1434,7 +1496,7 @@ if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); /* Check, if the chip supports auto page increment */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) @@ -1442,7 +1504,7 @@ } /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); return 0; } @@ -1564,7 +1626,7 @@ } /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd, FL_WRITING); + nand_get_device (this, mtd, FL_WRITING); /* Calculate chipnr */ chipnr = (int)(to >> this->chip_shift); @@ -1669,7 +1731,7 @@ out: /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); return ret; } @@ -1709,7 +1771,7 @@ } /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd, FL_WRITING); + nand_get_device (this, mtd, FL_WRITING); /* Select the NAND device */ this->select_chip(mtd, chipnr); @@ -1750,7 +1812,7 @@ status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); ret = -EIO; goto out; @@ -1771,7 +1833,7 @@ ret = 0; out: /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); return ret; } @@ -1838,7 +1900,7 @@ } /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd, FL_WRITING); + nand_get_device (this, mtd, FL_WRITING); /* Get the current chip-nr */ chipnr = (int) (to >> this->chip_shift); @@ -1952,7 +2014,7 @@ ret = 0; out: /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); *retlen = written; return ret; @@ -2004,6 +2066,7 @@ return nand_erase_nand (mtd, instr, 0); } +#define BBT_PAGE_MASK 0xffffff3f /** * nand_erase_intern - [NAND Interface] erase block(s) * @mtd: MTD device structure @@ -2016,6 +2079,10 @@ { int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *this = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ + unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ + /* It is used to see if the current page is in the same */ + /* 256 block group and the same bank as the bbt. */ DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); @@ -2041,7 +2108,7 @@ instr->fail_addr = 0xffffffff; /* Grab the lock and see if the device is available */ - nand_get_chip (this, mtd, FL_ERASING); + nand_get_device (this, mtd, FL_ERASING); /* Shift to get first page */ page = (int) (instr->addr >> this->page_shift); @@ -2061,6 +2128,13 @@ goto erase_exit; } + /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ + if (this->options & BBT_AUTO_REFRESH) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } else { + bbt_masked_page = 0xffffffff; /* should not match anything */ + } + /* Loop through the pages */ len = instr->len; @@ -2083,13 +2157,26 @@ status = this->waitfunc (mtd, this, FL_ERASING); + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_ERASING, status, page); + } + /* See if block erase succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << this->page_shift); goto erase_exit; } + + /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ + if (this->options & BBT_AUTO_REFRESH) { + if (((page & BBT_PAGE_MASK) == bbt_masked_page) && + (page != this->bbt_td->pages[chipnr])) { + rewrite_bbt[chipnr] = (page << this->page_shift); + } + } /* Increment page address and decrement length */ len -= (1 << this->phys_erase_shift); @@ -2100,6 +2187,13 @@ chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); + + /* if BBT requires refresh and BBT-PERCHIP, + * set the BBT page mask to see if this BBT should be rewritten */ + if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } + } } instr->state = MTD_ERASE_DONE; @@ -2112,7 +2206,19 @@ mtd_erase_callback(instr); /* Deselect and wake up anyone waiting on the device */ - nand_release_chip(mtd); + nand_release_device(mtd); + + /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ + if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { + for (chipnr = 0; chipnr < this->numchips; chipnr++) { + if (rewrite_bbt[chipnr]) { + /* update the BBT for chip */ + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); + nand_update_bbt (mtd, rewrite_bbt[chipnr]); + } + } + } /* Return more or less happy */ return ret; @@ -2127,43 +2233,13 @@ static void nand_sync (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE (wait, current); DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); -retry: - /* Grab the spinlock */ - spin_lock_bh (&this->chip_lock); - - /* See what's going on */ - switch (this->state) { - case FL_READY: - case FL_SYNCING: - this->state = FL_SYNCING; - spin_unlock_bh (&this->chip_lock); - break; - - default: - /* Not an idle state */ - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule (); - - remove_wait_queue (&this->wq, &wait); - goto retry; - } - - /* Lock the device */ - spin_lock_bh (&this->chip_lock); - - /* Set the device to be ready again */ - if (this->state == FL_SYNCING) { - this->state = FL_READY; - wake_up (&this->wq); - } - - /* Unlock the device */ - spin_unlock_bh (&this->chip_lock); + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device (mtd); } @@ -2215,7 +2291,7 @@ */ int nand_scan (struct mtd_info *mtd, int maxchips) { - int i, j, nand_maf_id, nand_dev_id, busw; + int i, nand_maf_id, nand_dev_id, busw, maf_id; struct nand_chip *this = mtd->priv; /* Get buswidth to select the correct functions*/ @@ -2303,12 +2379,18 @@ busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; } + /* Try to identify manufacturer */ + for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { + if (nand_manuf_ids[maf_id].id == nand_maf_id) + break; + } + /* Check, if buswidth is correct. Hardware drivers should set * this correct ! */ if (busw != (this->options & NAND_BUSWIDTH_16)) { printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, - nand_manuf_ids[i].name , mtd->name); + nand_manuf_ids[maf_id].name , mtd->name); printk (KERN_WARNING "NAND bus width %d instead %d bit\n", (this->options & NAND_BUSWIDTH_16) ? 16 : 8, @@ -2347,14 +2429,9 @@ if (mtd->oobblock > 512 && this->cmdfunc == nand_command) this->cmdfunc = nand_command_lp; - /* Try to identify manufacturer */ - for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { - if (nand_manuf_ids[j].id == nand_maf_id) - break; - } printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, - nand_manuf_ids[j].name , nand_flash_ids[i].name); + nand_manuf_ids[maf_id].name , nand_flash_ids[i].name); break; } @@ -2448,8 +2525,19 @@ * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; switch (this->eccmode) { + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + mtd->oobblock); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: @@ -2459,16 +2547,13 @@ this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; - break; } else - this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ - + this->eccsize = 512; /* set eccsize to 512 */ + break; + case NAND_ECC_HW3_256: - if (this->calculate_ecc && this->correct_data && this->enable_hwecc) - break; - printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); - BUG(); - + break; + case NAND_ECC_NONE: printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); this->eccmode = NAND_ECC_NONE; @@ -2483,11 +2568,32 @@ printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); BUG(); } - + + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; + case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); + BUG(); + } + mtd->eccsize = this->eccsize; /* Set the number of read / write steps for one page to ensure ECC generation */ switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: @@ -2542,6 +2648,10 @@ memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); mtd->owner = THIS_MODULE; + + /* Check, if we should skip the bad block table scan */ + if (this->options & NAND_SKIP_BBTSCAN) + return 0; /* Build bad block table */ return this->scan_bbt (mtd); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/nand_bbt.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/nand_bbt.c --- linux-2.6.9-intc1/drivers/mtd/nand/nand_bbt.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/nand_bbt.c 2005-03-21 21:28:43.000000000 -0500 @@ -6,7 +6,7 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.24 2004/06/28 08:25:35 gleixner Exp $ + * $Id: nand_bbt.c,v 1.31 2005/02/16 17:09:36 dedekind Exp $ * * 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 @@ -77,17 +77,17 @@ */ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int i, end; + int i, end = 0; uint8_t *p = buf; - end = paglen + td->offs; if (td->options & NAND_BBT_SCANEMPTY) { + end = paglen + td->offs; for (i = 0; i < end; i++) { if (p[i] != 0xff) return -1; } + p += end; } - p += end; /* Compare the pattern */ for (i = 0; i < td->len; i++) { @@ -95,9 +95,9 @@ return -1; } - p += td->len; - end += td->len; if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; for (i = end; i < len; i++) { if (*p++ != 0xff) return -1; @@ -114,6 +114,7 @@ * @num: the number of bbt descriptors to read * @bits: number of bits per block * @offs: offset in the memory table + * @reserved_block_code: Pattern to identify reserved blocks * * Read the bad block table starting from page. * @@ -251,7 +252,7 @@ * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; int i, j, numblocks, len, scanlen; @@ -269,9 +270,17 @@ else len = 1; } - scanlen = mtd->oobblock + mtd->oobsize; - readlen = len * mtd->oobblock; - ooblen = len * mtd->oobsize; + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->oobblock + mtd->oobsize; + readlen = len * mtd->oobblock; + ooblen = len * mtd->oobsize; + } if (chip == -1) { /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it @@ -283,7 +292,7 @@ if (chip >= this->numchips) { printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", chip + 1, this->numchips); - return; + return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; @@ -292,8 +301,22 @@ } for (i = startblock; i < numblocks;) { - nand_read_raw (mtd, buf, from, readlen, ooblen); + int ret; + + if (bd->options & NAND_BBT_SCANEMPTY) + if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) + return ret; + for (j = 0; j < len; j++) { + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + size_t retlen; + + /* No need to read pages fully, just read required OOB bytes */ + ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, + readlen, &retlen, &buf[0]); + if (ret) + return ret; + } if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", @@ -304,6 +327,8 @@ i += 2; from += (1 << this->bbt_erase_shift); } + + return 0; } /** @@ -588,14 +613,12 @@ * The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ -static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - /* Ensure that we only scan for the pattern and nothing else */ - bd->options = 0; - create_bbt (mtd, this->data_buf, bd, -1); - return 0; + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt (mtd, this->data_buf, bd, -1); } /** @@ -796,7 +819,7 @@ len = mtd->size >> (this->bbt_erase_shift + 2); /* Allocate memory (2bit per block) */ - this->bbt = (uint8_t *) kmalloc (len, GFP_KERNEL); + this->bbt = kmalloc (len, GFP_KERNEL); if (!this->bbt) { printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); return -ENOMEM; @@ -807,8 +830,14 @@ /* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ - if (!td) - return nand_memory_bbt(mtd, bd); + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree (this->bbt); + this->bbt = NULL; + } + return res; + } /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); @@ -903,14 +932,11 @@ } /* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - * - * The memory based patterns just - */ + * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static struct nand_bbt_descr smallpage_memorybased = { - .options = 0, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern @@ -1000,25 +1026,27 @@ return nand_scan_bbt (mtd, &agand_flashbased); } + /* Is a flash based bad block table requested ? */ if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; - } - if (mtd->oobblock > 512) - return nand_scan_bbt (mtd, &largepage_flashbased); - else - return nand_scan_bbt (mtd, &smallpage_flashbased); + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_flashbased : &smallpage_flashbased; + } } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (mtd->oobblock > 512) - return nand_scan_bbt (mtd, &largepage_memorybased); - else - return nand_scan_bbt (mtd, &smallpage_memorybased); + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } } + return nand_scan_bbt (mtd, this->badblock_pattern); } /** @@ -1039,7 +1067,7 @@ res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, res, block >> 1); + (unsigned int)offs, block >> 1, res); switch ((int)res) { case 0x00: return 0; diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/nand_ids.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/nand_ids.c --- linux-2.6.9-intc1/drivers/mtd/nand/nand_ids.c 2004-10-18 17:53:50.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/nand_ids.c 2005-03-21 21:28:43.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ + * $Id: nand_ids.c,v 1.12 2005/02/16 09:33:27 gleixner Exp $ * * 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 @@ -62,8 +62,6 @@ {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0}, - /* These are the new chips with large page size. The pagesize * and the erasesize is determined from the extended id bytes */ @@ -103,7 +101,7 @@ * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go * There are more speed improvements for reads and writes possible, but not implemented now */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, {NULL,} }; diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/nandsim.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/nandsim.c --- linux-2.6.9-intc1/drivers/mtd/nand/nandsim.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/nandsim.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,1596 @@ +/* + * NAND flash simulator. + * + * Author: Artem B. Bityuckiy , + * + * Copyright (C) 2004 Nokia Corporation + * + * Note: NS means "NAND Simulator". + * Note: Input means input TO flash chip, output means output FROM chip. + * + * 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, 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 + * + * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_NS_ABS_POS +#include +#endif + + +/* Default simulator parameters values */ +#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) +#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 +#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 +#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ +#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ +#endif + +#ifndef CONFIG_NANDSIM_ACCESS_DELAY +#define CONFIG_NANDSIM_ACCESS_DELAY 25 +#endif +#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY +#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 +#endif +#ifndef CONFIG_NANDSIM_ERASE_DELAY +#define CONFIG_NANDSIM_ERASE_DELAY 2 +#endif +#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE +#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 +#endif +#ifndef CONFIG_NANDSIM_INPUT_CYCLE +#define CONFIG_NANDSIM_INPUT_CYCLE 50 +#endif +#ifndef CONFIG_NANDSIM_BUS_WIDTH +#define CONFIG_NANDSIM_BUS_WIDTH 8 +#endif +#ifndef CONFIG_NANDSIM_DO_DELAYS +#define CONFIG_NANDSIM_DO_DELAYS 0 +#endif +#ifndef CONFIG_NANDSIM_LOG +#define CONFIG_NANDSIM_LOG 0 +#endif +#ifndef CONFIG_NANDSIM_DBG +#define CONFIG_NANDSIM_DBG 0 +#endif + +static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; +static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; +static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE; +static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE; +static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; +static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; +static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; +static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; +static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; +static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; +static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; +static uint log = CONFIG_NANDSIM_LOG; +static uint dbg = CONFIG_NANDSIM_DBG; + +module_param(first_id_byte, uint, 0400); +module_param(second_id_byte, uint, 0400); +module_param(third_id_byte, uint, 0400); +module_param(fourth_id_byte, uint, 0400); +module_param(access_delay, uint, 0400); +module_param(programm_delay, uint, 0400); +module_param(erase_delay, uint, 0400); +module_param(output_cycle, uint, 0400); +module_param(input_cycle, uint, 0400); +module_param(bus_width, uint, 0400); +module_param(do_delays, uint, 0400); +module_param(log, uint, 0400); +module_param(dbg, uint, 0400); + +MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); +MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); +MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); +MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); +MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); +MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); +MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); +MODULE_PARM_DESC(log, "Perform logging if not zero"); +MODULE_PARM_DESC(dbg, "Output debug information if not zero"); + +/* The largest possible page size */ +#define NS_LARGEST_PAGE_SIZE 2048 + +/* The prefix for simulator output */ +#define NS_OUTPUT_PREFIX "[nandsim]" + +/* Simulator's output macros (logging, debugging, warning, error) */ +#define NS_LOG(args...) \ + do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) +#define NS_DBG(args...) \ + do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) +#define NS_WARN(args...) \ + do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0) +#define NS_ERR(args...) \ + do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0) + +/* Busy-wait delay macros (microseconds, milliseconds) */ +#define NS_UDELAY(us) \ + do { if (do_delays) udelay(us); } while(0) +#define NS_MDELAY(us) \ + do { if (do_delays) mdelay(us); } while(0) + +/* Is the nandsim structure initialized ? */ +#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) + +/* Good operation completion status */ +#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) + +/* Operation failed completion status */ +#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) + +/* Calculate the page offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET(ns) \ + (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) + +/* Calculate the OOB offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) + +/* After a command is input, the simulator goes to one of the following states */ +#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ +#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ +#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ +#define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */ +#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ +#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ +#define STATE_CMD_STATUS 0x00000007 /* read status */ +#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */ +#define STATE_CMD_SEQIN 0x00000009 /* sequential data imput */ +#define STATE_CMD_READID 0x0000000A /* read ID */ +#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ +#define STATE_CMD_RESET 0x0000000C /* reset */ +#define STATE_CMD_MASK 0x0000000F /* command states mask */ + +/* After an addres is input, the simulator goes to one of these states */ +#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ +#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ +#define STATE_ADDR_ZERO 0x00000030 /* one byte zero address was accepted */ +#define STATE_ADDR_MASK 0x00000030 /* address states mask */ + +/* Durind data input/output the simulator is in these states */ +#define STATE_DATAIN 0x00000100 /* waiting for data input */ +#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ + +#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ +#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ +#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ +#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */ +#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ + +/* Previous operation is done, ready to accept new requests */ +#define STATE_READY 0x00000000 + +/* This state is used to mark that the next state isn't known yet */ +#define STATE_UNKNOWN 0x10000000 + +/* Simulator's actions bit masks */ +#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ +#define ACTION_PRGPAGE 0x00200000 /* programm the internal buffer to flash */ +#define ACTION_SECERASE 0x00300000 /* erase sector */ +#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ +#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ +#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ +#define ACTION_MASK 0x00700000 /* action mask */ + +#define NS_OPER_NUM 12 /* Number of operations supported by the simulator */ +#define NS_OPER_STATES 6 /* Maximum number of states in operation */ + +#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ +#define OPT_PAGE256 0x00000001 /* 256-byte page chips */ +#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ +#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ +#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ +#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */ +#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ +#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */ +#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ + +/* Remove action bits ftom state */ +#define NS_STATE(x) ((x) & ~ACTION_MASK) + +/* + * Maximum previous states which need to be saved. Currently saving is + * only needed for page programm operation with preceeded read command + * (which is only valid for 512-byte pages). + */ +#define NS_MAX_PREVSTATES 1 + +/* + * The structure which describes all the internal simulator data. + */ +struct nandsim { + struct mtd_partition part; + + uint busw; /* flash chip bus width (8 or 16) */ + u_char ids[4]; /* chip's ID bytes */ + uint32_t options; /* chip's characteristic bits */ + uint32_t state; /* current chip state */ + uint32_t nxstate; /* next expected state */ + + uint32_t *op; /* current operation, NULL operations isn't known yet */ + uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ + uint16_t npstates; /* number of previous states saved */ + uint16_t stateidx; /* current state index */ + + /* The simulated NAND flash image */ + union flash_media { + u_char *byte; + uint16_t *word; + } mem; + + /* Internal buffer of page + OOB size bytes */ + union internal_buffer { + u_char *byte; /* for byte access */ + uint16_t *word; /* for 16-bit word access */ + } buf; + + /* NAND flash "geometry" */ + struct nandsin_geometry { + uint32_t totsz; /* total flash size, bytes */ + uint32_t secsz; /* flash sector (erase block) size, bytes */ + uint pgsz; /* NAND flash page size, bytes */ + uint oobsz; /* page OOB area size, bytes */ + uint32_t totszoob; /* total flash size including OOB, bytes */ + uint pgszoob; /* page size including OOB , bytes*/ + uint secszoob; /* sector size including OOB, bytes */ + uint pgnum; /* total number of pages */ + uint pgsec; /* number of pages per sector */ + uint secshift; /* bits number in sector size */ + uint pgshift; /* bits number in page size */ + uint oobshift; /* bits number in OOB size */ + uint pgaddrbytes; /* bytes per page address */ + uint secaddrbytes; /* bytes per sector address */ + uint idbytes; /* the number ID bytes that this chip outputs */ + } geom; + + /* NAND flash internal registers */ + struct nandsim_regs { + unsigned command; /* the command register */ + u_char status; /* the status register */ + uint row; /* the page number */ + uint column; /* the offset within page */ + uint count; /* internal counter */ + uint num; /* number of bytes which must be processed */ + uint off; /* fixed page offset */ + } regs; + + /* NAND flash lines state */ + struct ns_lines_status { + int ce; /* chip Enable */ + int cle; /* command Latch Enable */ + int ale; /* address Latch Enable */ + int wp; /* write Protect */ + } lines; +}; + +/* + * Operations array. To perform any operation the simulator must pass + * through the correspondent states chain. + */ +static struct nandsim_operations { + uint32_t reqopts; /* options which are required to perform the operation */ + uint32_t states[NS_OPER_STATES]; /* operation's states */ +} ops[NS_OPER_NUM] = { + /* Read page + OOB from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read page + OOB from the second half */ + {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Programm page starting from the beginning */ + {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, + STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm page starting from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm page starting from the second half */ + {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Erase sector */ + {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, + /* Read status */ + {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, + /* Read multi-plane status */ + {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}}, + /* Read ID */ + {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, + /* Large page devices read page */ + {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, + STATE_DATAOUT, STATE_READY}} +}; + +/* MTD structure for NAND controller */ +static struct mtd_info *nsmtd; + +static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; + +/* + * Initialize the nandsim structure. + * + * RETURNS: 0 if success, -ERRNO if failure. + */ +static int +init_nandsim(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct nandsim *ns = (struct nandsim *)(chip->priv); + int i; + + if (NS_IS_INITIALIZED(ns)) { + NS_ERR("init_nandsim: nandsim is already initialized\n"); + return -EIO; + } + + /* Force mtd to not do delays */ + chip->chip_delay = 0; + + /* Initialize the NAND flash parameters */ + ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; + ns->geom.totsz = mtd->size; + ns->geom.pgsz = mtd->oobblock; + ns->geom.oobsz = mtd->oobsize; + ns->geom.secsz = mtd->erasesize; + ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; + ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz; + ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz; + ns->geom.secshift = ffs(ns->geom.secsz) - 1; + ns->geom.pgshift = chip->page_shift; + ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; + ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; + ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; + ns->options = 0; + + if (ns->geom.pgsz == 256) { + ns->options |= OPT_PAGE256; + } + else if (ns->geom.pgsz == 512) { + ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + if (ns->busw == 8) + ns->options |= OPT_PAGE512_8BIT; + } else if (ns->geom.pgsz == 2048) { + ns->options |= OPT_PAGE2048; + } else { + NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); + return -EIO; + } + + if (ns->options & OPT_SMALLPAGE) { + if (ns->geom.totsz < (64 << 20)) { + ns->geom.pgaddrbytes = 3; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 4; + ns->geom.secaddrbytes = 3; + } + } else { + if (ns->geom.totsz <= (128 << 20)) { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 3; + } + } + + /* Detect how many ID bytes the NAND chip outputs */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (second_id_byte != nand_flash_ids[i].id) + continue; + if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) + ns->options |= OPT_AUTOINCR; + } + + if (ns->busw == 16) + NS_WARN("16-bit flashes support wasn't tested\n"); + + printk("flash size: %u MiB\n", ns->geom.totsz >> 20); + printk("page size: %u bytes\n", ns->geom.pgsz); + printk("OOB area size: %u bytes\n", ns->geom.oobsz); + printk("sector size: %u KiB\n", ns->geom.secsz >> 10); + printk("pages number: %u\n", ns->geom.pgnum); + printk("pages per sector: %u\n", ns->geom.pgsec); + printk("bus width: %u\n", ns->busw); + printk("bits in sector size: %u\n", ns->geom.secshift); + printk("bits in page size: %u\n", ns->geom.pgshift); + printk("bits in OOB size: %u\n", ns->geom.oobshift); + printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10); + printk("page address bytes: %u\n", ns->geom.pgaddrbytes); + printk("sector address bytes: %u\n", ns->geom.secaddrbytes); + printk("options: %#x\n", ns->options); + + /* Map / allocate and initialize the flash image */ +#ifdef CONFIG_NS_ABS_POS + ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob); + if (!ns->mem.byte) { + NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", + (void *)CONFIG_NS_ABS_POS); + return -ENOMEM; + } +#else + ns->mem.byte = vmalloc(ns->geom.totszoob); + if (!ns->mem.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", + ns->geom.totszoob); + return -ENOMEM; + } + memset(ns->mem.byte, 0xFF, ns->geom.totszoob); +#endif + + /* Allocate / initialize the internal buffer */ + ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->buf.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", + ns->geom.pgszoob); + goto error; + } + memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); + + /* Fill the partition_info structure */ + ns->part.name = "NAND simulator partition"; + ns->part.offset = 0; + ns->part.size = ns->geom.totsz; + + return 0; + +error: +#ifdef CONFIG_NS_ABS_POS + iounmap(ns->mem.byte); +#else + vfree(ns->mem.byte); +#endif + + return -ENOMEM; +} + +/* + * Free the nandsim structure. + */ +static void +free_nandsim(struct nandsim *ns) +{ + kfree(ns->buf.byte); + +#ifdef CONFIG_NS_ABS_POS + iounmap(ns->mem.byte); +#else + vfree(ns->mem.byte); +#endif + + return; +} + +/* + * Returns the string representation of 'state' state. + */ +static char * +get_state_name(uint32_t state) +{ + switch (NS_STATE(state)) { + case STATE_CMD_READ0: + return "STATE_CMD_READ0"; + case STATE_CMD_READ1: + return "STATE_CMD_READ1"; + case STATE_CMD_PAGEPROG: + return "STATE_CMD_PAGEPROG"; + case STATE_CMD_READOOB: + return "STATE_CMD_READOOB"; + case STATE_CMD_READSTART: + return "STATE_CMD_READSTART"; + case STATE_CMD_ERASE1: + return "STATE_CMD_ERASE1"; + case STATE_CMD_STATUS: + return "STATE_CMD_STATUS"; + case STATE_CMD_STATUS_M: + return "STATE_CMD_STATUS_M"; + case STATE_CMD_SEQIN: + return "STATE_CMD_SEQIN"; + case STATE_CMD_READID: + return "STATE_CMD_READID"; + case STATE_CMD_ERASE2: + return "STATE_CMD_ERASE2"; + case STATE_CMD_RESET: + return "STATE_CMD_RESET"; + case STATE_ADDR_PAGE: + return "STATE_ADDR_PAGE"; + case STATE_ADDR_SEC: + return "STATE_ADDR_SEC"; + case STATE_ADDR_ZERO: + return "STATE_ADDR_ZERO"; + case STATE_DATAIN: + return "STATE_DATAIN"; + case STATE_DATAOUT: + return "STATE_DATAOUT"; + case STATE_DATAOUT_ID: + return "STATE_DATAOUT_ID"; + case STATE_DATAOUT_STATUS: + return "STATE_DATAOUT_STATUS"; + case STATE_DATAOUT_STATUS_M: + return "STATE_DATAOUT_STATUS_M"; + case STATE_READY: + return "STATE_READY"; + case STATE_UNKNOWN: + return "STATE_UNKNOWN"; + } + + NS_ERR("get_state_name: unknown state, BUG\n"); + return NULL; +} + +/* + * Check if command is valid. + * + * RETURNS: 1 if wrong command, 0 if right. + */ +static int +check_command(int cmd) +{ + switch (cmd) { + + case NAND_CMD_READ0: + case NAND_CMD_READSTART: + case NAND_CMD_PAGEPROG: + case NAND_CMD_READOOB: + case NAND_CMD_ERASE1: + case NAND_CMD_STATUS: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + case NAND_CMD_ERASE2: + case NAND_CMD_RESET: + case NAND_CMD_READ1: + return 0; + + case NAND_CMD_STATUS_MULTI: + default: + return 1; + } +} + +/* + * Returns state after command is accepted by command number. + */ +static uint32_t +get_state_by_command(unsigned command) +{ + switch (command) { + case NAND_CMD_READ0: + return STATE_CMD_READ0; + case NAND_CMD_READ1: + return STATE_CMD_READ1; + case NAND_CMD_PAGEPROG: + return STATE_CMD_PAGEPROG; + case NAND_CMD_READSTART: + return STATE_CMD_READSTART; + case NAND_CMD_READOOB: + return STATE_CMD_READOOB; + case NAND_CMD_ERASE1: + return STATE_CMD_ERASE1; + case NAND_CMD_STATUS: + return STATE_CMD_STATUS; + case NAND_CMD_STATUS_MULTI: + return STATE_CMD_STATUS_M; + case NAND_CMD_SEQIN: + return STATE_CMD_SEQIN; + case NAND_CMD_READID: + return STATE_CMD_READID; + case NAND_CMD_ERASE2: + return STATE_CMD_ERASE2; + case NAND_CMD_RESET: + return STATE_CMD_RESET; + } + + NS_ERR("get_state_by_command: unknown command, BUG\n"); + return 0; +} + +/* + * Move an address byte to the correspondent internal register. + */ +static inline void +accept_addr_byte(struct nandsim *ns, u_char bt) +{ + uint byte = (uint)bt; + + if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) + ns->regs.column |= (byte << 8 * ns->regs.count); + else { + ns->regs.row |= (byte << 8 * (ns->regs.count - + ns->geom.pgaddrbytes + + ns->geom.secaddrbytes)); + } + + return; +} + +/* + * Switch to STATE_READY state. + */ +static inline void +switch_to_ready_state(struct nandsim *ns, u_char status) +{ + NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); + + ns->state = STATE_READY; + ns->nxstate = STATE_UNKNOWN; + ns->op = NULL; + ns->npstates = 0; + ns->stateidx = 0; + ns->regs.num = 0; + ns->regs.count = 0; + ns->regs.off = 0; + ns->regs.row = 0; + ns->regs.column = 0; + ns->regs.status = status; +} + +/* + * If the operation isn't known yet, try to find it in the global array + * of supported operations. + * + * Operation can be unknown because of the following. + * 1. New command was accepted and this is the firs call to find the + * correspondent states chain. In this case ns->npstates = 0; + * 2. There is several operations which begin with the same command(s) + * (for example program from the second half and read from the + * second half operations both begin with the READ1 command). In this + * case the ns->pstates[] array contains previous states. + * + * Thus, the function tries to find operation containing the following + * states (if the 'flag' parameter is 0): + * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state + * + * If (one and only one) matching operation is found, it is accepted ( + * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is + * zeroed). + * + * If there are several maches, the current state is pushed to the + * ns->pstates. + * + * The operation can be unknown only while commands are input to the chip. + * As soon as address command is accepted, the operation must be known. + * In such situation the function is called with 'flag' != 0, and the + * operation is searched using the following pattern: + * ns->pstates[0], ... ns->pstates[ns->npstates],
+ * + * It is supposed that this pattern must either match one operation on + * none. There can't be ambiguity in that case. + * + * If no matches found, the functions does the following: + * 1. if there are saved states present, try to ignore them and search + * again only using the last command. If nothing was found, switch + * to the STATE_READY state. + * 2. if there are no saved states, switch to the STATE_READY state. + * + * RETURNS: -2 - no matched operations found. + * -1 - several matches. + * 0 - operation is found. + */ +static int +find_operation(struct nandsim *ns, uint32_t flag) +{ + int opsfound = 0; + int i, j, idx = 0; + + for (i = 0; i < NS_OPER_NUM; i++) { + + int found = 1; + + if (!(ns->options & ops[i].reqopts)) + /* Ignore operations we can't perform */ + continue; + + if (flag) { + if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) + continue; + } else { + if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) + continue; + } + + for (j = 0; j < ns->npstates; j++) + if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) + && (ns->options & ops[idx].reqopts)) { + found = 0; + break; + } + + if (found) { + idx = i; + opsfound += 1; + } + } + + if (opsfound == 1) { + /* Exact match */ + ns->op = &ops[idx].states[0]; + if (flag) { + /* + * In this case the find_operation function was + * called when address has just began input. But it isn't + * yet fully input and the current state must + * not be one of STATE_ADDR_*, but the STATE_ADDR_* + * state must be the next state (ns->nxstate). + */ + ns->stateidx = ns->npstates - 1; + } else { + ns->stateidx = ns->npstates; + } + ns->npstates = 0; + ns->state = ns->op[ns->stateidx]; + ns->nxstate = ns->op[ns->stateidx + 1]; + NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", + idx, get_state_name(ns->state), get_state_name(ns->nxstate)); + return 0; + } + + if (opsfound == 0) { + /* Nothing was found. Try to ignore previous commands (if any) and search again */ + if (ns->npstates != 0) { + NS_DBG("find_operation: no operation found, try again with state %s\n", + get_state_name(ns->state)); + ns->npstates = 0; + return find_operation(ns, 0); + + } + NS_DBG("find_operation: no operations found\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return -2; + } + + if (flag) { + /* This shouldn't happen */ + NS_DBG("find_operation: BUG, operation must be known if address is input\n"); + return -2; + } + + NS_DBG("find_operation: there is still ambiguity\n"); + + ns->pstates[ns->npstates++] = ns->state; + + return -1; +} + +/* + * If state has any action bit, perform this action. + * + * RETURNS: 0 if success, -1 if error. + */ +static int +do_state_action(struct nandsim *ns, uint32_t action) +{ + int i, num; + int busdiv = ns->busw == 8 ? 1 : 2; + + action &= ACTION_MASK; + + /* Check that page address input is correct */ + if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { + NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); + return -1; + } + + switch (action) { + + case ACTION_CPY: + /* + * Copy page data to the internal buffer. + */ + + /* Column shouldn't be very large */ + if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { + NS_ERR("do_state_action: column number is too large\n"); + break; + } + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); + + NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", + num, NS_RAW_OFFSET(ns) + ns->regs.off); + + if (ns->regs.off == 0) + NS_LOG("read page %d\n", ns->regs.row); + else if (ns->regs.off < ns->geom.pgsz) + NS_LOG("read page %d (second half)\n", ns->regs.row); + else + NS_LOG("read OOB of page %d\n", ns->regs.row); + + NS_UDELAY(access_delay); + NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_SECERASE: + /* + * Erase sector. + */ + + if (ns->lines.wp) { + NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); + return -1; + } + + if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec + || (ns->regs.row & ~(ns->geom.secsz - 1))) { + NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); + return -1; + } + + ns->regs.row = (ns->regs.row << + 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; + ns->regs.column = 0; + + NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", + ns->regs.row, NS_RAW_OFFSET(ns)); + NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); + + memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); + + NS_MDELAY(erase_delay); + + break; + + case ACTION_PRGPAGE: + /* + * Programm page - move internal buffer data to the page. + */ + + if (ns->lines.wp) { + NS_WARN("do_state_action: device is write-protected, programm\n"); + return -1; + } + + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + if (num != ns->regs.count) { + NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", + ns->regs.count, num); + return -1; + } + + for (i = 0; i < num; i++) + ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; + + NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", + num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); + NS_LOG("programm page %d\n", ns->regs.row); + + NS_UDELAY(programm_delay); + NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_ZEROOFF: + NS_DBG("do_state_action: set internal offset to 0\n"); + ns->regs.off = 0; + break; + + case ACTION_HALFOFF: + if (!(ns->options & OPT_PAGE512_8BIT)) { + NS_ERR("do_state_action: BUG! can't skip half of page for non-512" + "byte page size 8x chips\n"); + return -1; + } + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); + ns->regs.off = ns->geom.pgsz/2; + break; + + case ACTION_OOBOFF: + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); + ns->regs.off = ns->geom.pgsz; + break; + + default: + NS_DBG("do_state_action: BUG! unknown action\n"); + } + + return 0; +} + +/* + * Switch simulator's state. + */ +static void +switch_state(struct nandsim *ns) +{ + if (ns->op) { + /* + * The current operation have already been identified. + * Just follow the states chain. + */ + + ns->stateidx += 1; + ns->state = ns->nxstate; + ns->nxstate = ns->op[ns->stateidx + 1]; + + NS_DBG("switch_state: operation is known, switch to the next state, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* See, whether we need to do some action */ + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + } else { + /* + * We don't yet know which operation we perform. + * Try to identify it. + */ + + /* + * The only event causing the switch_state function to + * be called with yet unknown operation is new command. + */ + ns->state = get_state_by_command(ns->regs.command); + + NS_DBG("switch_state: operation is unknown, try to find it\n"); + + if (find_operation(ns, 0) != 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + } + + /* For 16x devices column means the page offset in words */ + if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { + NS_DBG("switch_state: double the column number for 16x device\n"); + ns->regs.column <<= 1; + } + + if (NS_STATE(ns->nxstate) == STATE_READY) { + /* + * The current state is the last. Return to STATE_READY + */ + + u_char status = NS_STATUS_OK(ns); + + /* In case of data states, see if all bytes were input/output */ + if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) + && ns->regs.count != ns->regs.num) { + NS_WARN("switch_state: not all bytes were processed, %d left\n", + ns->regs.num - ns->regs.count); + status = NS_STATUS_FAILED(ns); + } + + NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); + + switch_to_ready_state(ns, status); + + return; + } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { + /* + * If the next state is data input/output, switch to it now + */ + + ns->state = ns->nxstate; + ns->nxstate = ns->op[++ns->stateidx + 1]; + ns->regs.num = ns->regs.count = 0; + + NS_DBG("switch_state: the next state is data I/O, switch, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* + * Set the internal register to the count of bytes which + * are expected to be input or output + */ + switch (NS_STATE(ns->state)) { + case STATE_DATAIN: + case STATE_DATAOUT: + ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + break; + + case STATE_DATAOUT_ID: + ns->regs.num = ns->geom.idbytes; + break; + + case STATE_DATAOUT_STATUS: + case STATE_DATAOUT_STATUS_M: + ns->regs.count = ns->regs.num = 0; + break; + + default: + NS_ERR("switch_state: BUG! unknown data state\n"); + } + + } else if (ns->nxstate & STATE_ADDR_MASK) { + /* + * If the next state is address input, set the internal + * register to the number of expected address bytes + */ + + ns->regs.count = 0; + + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + + default: + NS_ERR("switch_state: BUG! unknown address state\n"); + } + } else { + /* + * Just reset internal counters. + */ + + ns->regs.num = 0; + ns->regs.count = 0; + } +} + +static void +ns_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + switch (cmd) { + + /* set CLE line high */ + case NAND_CTL_SETCLE: + NS_DBG("ns_hwcontrol: start command latch cycles\n"); + ns->lines.cle = 1; + break; + + /* set CLE line low */ + case NAND_CTL_CLRCLE: + NS_DBG("ns_hwcontrol: stop command latch cycles\n"); + ns->lines.cle = 0; + break; + + /* set ALE line high */ + case NAND_CTL_SETALE: + NS_DBG("ns_hwcontrol: start address latch cycles\n"); + ns->lines.ale = 1; + break; + + /* set ALE line low */ + case NAND_CTL_CLRALE: + NS_DBG("ns_hwcontrol: stop address latch cycles\n"); + ns->lines.ale = 0; + break; + + /* set WP line high */ + case NAND_CTL_SETWP: + NS_DBG("ns_hwcontrol: enable write protection\n"); + ns->lines.wp = 1; + break; + + /* set WP line low */ + case NAND_CTL_CLRWP: + NS_DBG("ns_hwcontrol: disable write protection\n"); + ns->lines.wp = 0; + break; + + /* set CE line low */ + case NAND_CTL_SETNCE: + NS_DBG("ns_hwcontrol: enable chip\n"); + ns->lines.ce = 1; + break; + + /* set CE line high */ + case NAND_CTL_CLRNCE: + NS_DBG("ns_hwcontrol: disable chip\n"); + ns->lines.ce = 0; + break; + + default: + NS_ERR("hwcontrol: unknown command\n"); + } + + return; +} + +static u_char +ns_nand_read_byte(struct mtd_info *mtd) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + u_char outb = 0x00; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); + return outb; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); + return outb; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_byte: unexpected data output cycle, state is %s " + "return %#x\n", get_state_name(ns->state), (uint)outb); + return outb; + } + + /* Status register may be read as many times as it is wanted */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { + NS_DBG("read_byte: return %#x status\n", ns->regs.status); + return ns->regs.status; + } + + /* Check if there is any data in the internal buffer which may be read */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); + return outb; + } + + switch (NS_STATE(ns->state)) { + case STATE_DATAOUT: + if (ns->busw == 8) { + outb = ns->buf.byte[ns->regs.count]; + ns->regs.count += 1; + } else { + outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); + ns->regs.count += 2; + } + break; + case STATE_DATAOUT_ID: + NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); + outb = ns->ids[ns->regs.count]; + ns->regs.count += 1; + break; + default: + BUG(); + } + + if (ns->regs.count == ns->regs.num) { + NS_DBG("read_byte: all bytes were read\n"); + + /* + * The OPT_AUTOINCR allows to read next conseqitive pages without + * new read operation cycle. + */ + if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { + ns->regs.count = 0; + if (ns->regs.row + 1 < ns->geom.pgnum) + ns->regs.row += 1; + NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); + do_state_action(ns, ACTION_CPY); + } + else if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + + } + + return outb; +} + +static void +ns_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("write_byte: chip is disabled, ignore write\n"); + return; + } + if (ns->lines.ale && ns->lines.cle) { + NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); + return; + } + + if (ns->lines.cle == 1) { + /* + * The byte written is a command. + */ + + if (byte == NAND_CMD_RESET) { + NS_LOG("reset chip\n"); + switch_to_ready_state(ns, NS_STATUS_OK(ns)); + return; + } + + /* + * Chip might still be in STATE_DATAOUT + * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or + * STATE_DATAOUT_STATUS_M state. If so, switch state. + */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS + || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M + || ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT)) + switch_state(ns); + + /* Check if chip is expecting command */ + if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { + /* + * We are in situation when something else (not command) + * was expected but command was input. In this case ignore + * previous command(s)/state(s) and accept the last one. + */ + NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " + "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + } + + /* Check that the command byte is correct */ + if (check_command(byte)) { + NS_ERR("write_byte: unknown command %#x\n", (uint)byte); + return; + } + + NS_DBG("command byte corresponding to %s state accepted\n", + get_state_name(get_state_by_command(byte))); + ns->regs.command = byte; + switch_state(ns); + + } else if (ns->lines.ale == 1) { + /* + * The byte written is an address. + */ + + if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { + + NS_DBG("write_byte: operation isn't known yet, identify it\n"); + + if (find_operation(ns, 1) < 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + ns->regs.count = 0; + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + default: + BUG(); + } + } + + /* Check that chip is expecting address */ + if (!(ns->nxstate & STATE_ADDR_MASK)) { + NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " + "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_ERR("write_byte: no more address bytes expected\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + accept_addr_byte(ns, byte); + + ns->regs.count += 1; + + NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", + (uint)byte, ns->regs.count, ns->regs.num); + + if (ns->regs.count == ns->regs.num) { + NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); + switch_state(ns); + } + + } else { + /* + * The byte written is an input data. + */ + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " + "switch to %s\n", (uint)byte, + get_state_name(ns->state), get_state_name(STATE_READY)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", + ns->regs.num); + return; + } + + if (ns->busw == 8) { + ns->buf.byte[ns->regs.count] = byte; + ns->regs.count += 1; + } else { + ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); + ns->regs.count += 2; + } + } + + return; +} + +static int +ns_device_ready(struct mtd_info *mtd) +{ + NS_DBG("device_ready\n"); + return 1; +} + +static uint16_t +ns_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + + NS_DBG("read_word\n"); + + return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); +} + +static void +ns_nand_write_word(struct mtd_info *mtd, uint16_t word) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + + NS_DBG("write_word\n"); + + chip->write_byte(mtd, word & 0xFF); + chip->write_byte(mtd, word >> 8); +} + +static void +ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_buf: data input isn't expected, state is %s, " + "switch to STATE_READY\n", get_state_name(ns->state)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("write_buf: too many input bytes\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(ns->buf.byte + ns->regs.count, buf, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); + } +} + +static void +ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_buf: chip is disabled\n"); + return; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_buf: ALE or CLE pin is high\n"); + return; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", + get_state_name(ns->state)); + return; + } + + if (NS_STATE(ns->state) != STATE_DATAOUT) { + int i; + + for (i = 0; i < len; i++) + buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd); + + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("read_buf: too many bytes to read\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(buf, ns->buf.byte + ns->regs.count, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { + ns->regs.count = 0; + if (ns->regs.row + 1 < ns->geom.pgnum) + ns->regs.row += 1; + NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); + do_state_action(ns, ACTION_CPY); + } + else if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + } + + return; +} + +static int +ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); + + if (!memcmp(buf, &ns_verify_buf[0], len)) { + NS_DBG("verify_buf: the buffer is OK\n"); + return 0; + } else { + NS_DBG("verify_buf: the buffer is wrong\n"); + return -EFAULT; + } +} + +/* + * Module initialization function + */ +int __init ns_init_module(void) +{ + struct nand_chip *chip; + struct nandsim *nand; + int retval = -ENOMEM; + + if (bus_width != 8 && bus_width != 16) { + NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); + return -EINVAL; + } + + /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ + nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct nandsim), GFP_KERNEL); + if (!nsmtd) { + NS_ERR("unable to allocate core structures.\n"); + return -ENOMEM; + } + memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct nandsim)); + chip = (struct nand_chip *)(nsmtd + 1); + nsmtd->priv = (void *)chip; + nand = (struct nandsim *)(chip + 1); + chip->priv = (void *)nand; + + /* + * Register simulator's callbacks. + */ + chip->hwcontrol = ns_hwcontrol; + chip->read_byte = ns_nand_read_byte; + chip->dev_ready = ns_device_ready; + chip->write_byte = ns_nand_write_byte; + chip->write_buf = ns_nand_write_buf; + chip->read_buf = ns_nand_read_buf; + chip->verify_buf = ns_nand_verify_buf; + chip->write_word = ns_nand_write_word; + chip->read_word = ns_nand_read_word; + chip->eccmode = NAND_ECC_SOFT; + chip->options |= NAND_SKIP_BBTSCAN; + + /* + * Perform minimum nandsim structure initialization to handle + * the initial ID read command correctly + */ + if (third_id_byte != 0xFF || fourth_id_byte != 0xFF) + nand->geom.idbytes = 4; + else + nand->geom.idbytes = 2; + nand->regs.status = NS_STATUS_OK(nand); + nand->nxstate = STATE_UNKNOWN; + nand->options |= OPT_PAGE256; /* temporary value */ + nand->ids[0] = first_id_byte; + nand->ids[1] = second_id_byte; + nand->ids[2] = third_id_byte; + nand->ids[3] = fourth_id_byte; + if (bus_width == 16) { + nand->busw = 16; + chip->options |= NAND_BUSWIDTH_16; + } + + if ((retval = nand_scan(nsmtd, 1)) != 0) { + NS_ERR("can't register NAND Simulator\n"); + if (retval > 0) + retval = -ENXIO; + goto error; + } + + if ((retval = init_nandsim(nsmtd)) != 0) { + NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); + goto error; + } + + if ((retval = nand_default_bbt(nsmtd)) != 0) { + free_nandsim(nand); + goto error; + } + + /* Register NAND as one big partition */ + add_mtd_partitions(nsmtd, &nand->part, 1); + + return 0; + +error: + kfree(nsmtd); + + return retval; +} + +module_init(ns_init_module); + +/* + * Module clean-up function + */ +static void __exit ns_cleanup_module(void) +{ + struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); + + free_nandsim(ns); /* Free nandsim private resources */ + nand_release(nsmtd); /* Unregisterd drived */ + kfree(nsmtd); /* Free other structures */ +} + +module_exit(ns_cleanup_module); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Artem B. Bityuckiy"); +MODULE_DESCRIPTION ("The NAND flash simulator"); + diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/ppchameleonevb.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/ppchameleonevb.c --- linux-2.6.9-intc1/drivers/mtd/nand/ppchameleonevb.c 2004-10-18 17:54:40.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/ppchameleonevb.c 2005-03-21 21:28:43.000000000 -0500 @@ -6,7 +6,7 @@ * Derived from drivers/mtd/nand/edb7312.c * * - * $Id: ppchameleonevb.c,v 1.2 2004/05/05 22:09:54 gleixner Exp $ + * $Id: ppchameleonevb.c,v 1.6 2004/11/05 16:07:16 kalev Exp $ * * 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 @@ -64,22 +64,17 @@ /* * Module stuff */ -static int ppchameleon_fio_pbase = CFG_NAND0_PADDR; -static int ppchameleonevb_fio_pbase = CFG_NAND1_PADDR; +static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR; +static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR; #ifdef MODULE -MODULE_PARM(ppchameleon_fio_pbase, "i"); +module_param(ppchameleon_fio_pbase, ulong, 0); +module_param(ppchameleonevb_fio_pbase, ulong, 0); +#else __setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase); -MODULE_PARM(ppchameleonevb_fio_pbase, "i"); __setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase); #endif -/* Internal buffers. Page buffer and oob buffer for one block */ -static u_char data_buf[2048 + 64]; -static u_char oob_buf[64 * 64]; -static u_char data_buf_evb[512 + 16]; -static u_char oob_buf_evb[16 * 32]; - #ifdef CONFIG_MTD_PARTITIONS /* * Define static partitions for flash devices @@ -196,8 +191,8 @@ const char *part_type = 0; int mtd_parts_nb = 0; struct mtd_partition *mtd_parts = 0; - int ppchameleon_fio_base; - int ppchameleonevb_fio_base; + void __iomem *ppchameleon_fio_base; + void __iomem *ppchameleonevb_fio_base; /********************************* @@ -205,15 +200,14 @@ *********************************/ /* Allocate memory for MTD device structure and private data */ ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + - sizeof(struct nand_chip), - GFP_KERNEL); + sizeof(struct nand_chip), GFP_KERNEL); if (!ppchameleon_mtd) { printk("Unable to allocate PPChameleon NAND MTD device structure.\n"); return -ENOMEM; } /* map physical address */ - ppchameleon_fio_base = (unsigned long)ioremap(ppchameleon_fio_pbase, SZ_4M); + ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M); if(!ppchameleon_fio_base) { printk("ioremap PPChameleon NAND flash failed\n"); kfree(ppchameleon_mtd); @@ -264,10 +258,6 @@ /* ECC mode */ this->eccmode = NAND_ECC_SOFT; - /* Set internal data buffer */ - this->data_buf = data_buf; - this->oob_buf = oob_buf; - /* Scan to find existence of the device (it could not be mounted) */ if (nand_scan (ppchameleon_mtd, 1)) { iounmap((void *)ppchameleon_fio_base); @@ -309,15 +299,14 @@ ****************************/ /* Allocate memory for MTD device structure and private data */ ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + - sizeof(struct nand_chip), - GFP_KERNEL); + sizeof(struct nand_chip), GFP_KERNEL); if (!ppchameleonevb_mtd) { printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n"); return -ENOMEM; } /* map physical address */ - ppchameleonevb_fio_base = (unsigned long)ioremap(ppchameleonevb_fio_pbase, SZ_4M); + ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M); if(!ppchameleonevb_fio_base) { printk("ioremap PPChameleonEVB NAND flash failed\n"); kfree(ppchameleonevb_mtd); @@ -349,7 +338,8 @@ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0); out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF); /* enable output driver */ - out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN); + out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | + NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN); #ifdef USE_READY_BUSY_PIN /* three-state select */ out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC); @@ -359,7 +349,6 @@ out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001); #endif - /* insert callbacks */ this->IO_ADDR_R = ppchameleonevb_fio_base; this->IO_ADDR_W = ppchameleonevb_fio_base; @@ -372,10 +361,6 @@ /* ECC mode */ this->eccmode = NAND_ECC_SOFT; - /* Set internal data buffer */ - this->data_buf = data_buf_evb; - this->oob_buf = oob_buf_evb; - /* Scan to find existence of the device */ if (nand_scan (ppchameleonevb_mtd, 1)) { iounmap((void *)ppchameleonevb_fio_base); @@ -412,15 +397,20 @@ */ static void __exit ppchameleonevb_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) &ppchameleonevb_mtd[1]; - - /* Unregister the device */ - del_mtd_device (ppchameleonevb_mtd); + struct nand_chip *this; - /* Free internal data buffer */ - kfree (this->data_buf); + /* Release resources, unregister device(s) */ + nand_release (ppchameleon_mtd); + nand_release (ppchameleonevb_mtd); + + /* Release iomaps */ + this = (struct nand_chip *) &ppchameleon_mtd[1]; + iounmap((void *) this->IO_ADDR_R; + this = (struct nand_chip *) &ppchameleonevb_mtd[1]; + iounmap((void *) this->IO_ADDR_R; /* Free the MTD device structure */ + kfree (ppchameleon_mtd); kfree (ppchameleonevb_mtd); } module_exit(ppchameleonevb_cleanup); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/rtc_from4.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/rtc_from4.c --- linux-2.6.9-intc1/drivers/mtd/nand/rtc_from4.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/rtc_from4.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,683 @@ +/* + * drivers/mtd/nand/rtc_from4.c + * + * Copyright (C) 2004 Red Hat, Inc. + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin Exp $ + * + * 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. + * + * Overview: + * This is a device driver for the AG-AND flash device found on the + * Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4), + * which utilizes the Renesas HN29V1G91T-30 part. + * This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * MTD structure for Renesas board + */ +static struct mtd_info *rtc_from4_mtd = NULL; + +#define RTC_FROM4_MAX_CHIPS 2 + +/* HS77x9 processor register defines */ +#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60)) +#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62)) +#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64)) +#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66)) +#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68)) +#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C)) +#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80)) + +/* + * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor) + */ +/* Address where flash is mapped */ +#define RTC_FROM4_FIO_BASE 0x14000000 + +/* CLE and ALE are tied to address lines 5 & 4, respectively */ +#define RTC_FROM4_CLE (1 << 5) +#define RTC_FROM4_ALE (1 << 4) + +/* address lines A24-A22 used for chip selection */ +#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000) +#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000) +#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000) +/* mask address lines A24-A22 used for chip selection */ +#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA) + +/* FPGA status register for checking device ready (bit zero) */ +#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002) +#define RTC_FROM4_DEVICE_READY 0x0001 + +/* FPGA Reed-Solomon ECC Control register */ + +#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050) +#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7) +#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6) +#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5) + +/* FPGA Reed-Solomon ECC code base */ +#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060) +#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080) + +/* FPGA Reed-Solomon ECC check register */ +#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070) +#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7) + +#define ERR_STAT_ECC_AVAILABLE 0x20 + +/* Undefine for software ECC */ +#define RTC_FROM4_HWECC 1 + +/* Define as 1 for no virtual erase blocks (in JFFS2) */ +#define RTC_FROM4_NO_VIRTBLOCKS 0 + +/* + * Module stuff + */ +static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); + +const static struct mtd_partition partition_info[] = { + { + .name = "Renesas flash partition 1", + .offset = 0, + .size = MTDPART_SIZ_FULL + }, +}; +#define NUM_PARTITIONS 1 + +/* + * hardware specific flash bbt decriptors + * Note: this is to allow debugging by disabling + * NAND_BBT_CREATE and/or NAND_BBT_WRITE + * + */ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr rtc_from4_bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 40, + .len = 4, + .veroffs = 44, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 40, + .len = 4, + .veroffs = 44, + .maxblocks = 4, + .pattern = mirror_pattern +}; + + + +#ifdef RTC_FROM4_HWECC + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * hardware specific Out Of Band information + */ +static struct nand_oobinfo rtc_from4_nand_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 32, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31}, + .oobfree = { {32, 32} } +}; + +/* Aargh. I missed the reversed bit order, when I + * was talking to Renesas about the FPGA. + * + * The table is used for bit reordering and inversion + * of the ecc byte which we get from the FPGA + */ +static uint8_t revbits[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +#endif + + + +/* + * rtc_from4_hwcontrol - hardware specific access to control-lines + * @mtd: MTD device structure + * @cmd: hardware control command + * + * Address lines (A5 and A4) are used to control Command and Address Latch + * Enable on this board, so set the read/write address appropriately. + * + * Chip Enable is also controlled by the Chip Select (CS5) and + * Address lines (A24-A22), so no action is required here. + * + */ +static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip* this = (struct nand_chip *) (mtd->priv); + + switch(cmd) { + + case NAND_CTL_SETCLE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE); + break; + case NAND_CTL_CLRCLE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE); + break; + + case NAND_CTL_SETALE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE); + break; + case NAND_CTL_CLRALE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE); + break; + + case NAND_CTL_SETNCE: + break; + case NAND_CTL_CLRNCE: + break; + + } +} + + +/* + * rtc_from4_nand_select_chip - hardware specific chip select + * @mtd: MTD device structure + * @chip: Chip to select (0 == slot 3, 1 == slot 4) + * + * The chip select is based on address lines A24-A22. + * This driver uses flash slots 3 and 4 (A23-A22). + * + */ +static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + + this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK); + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK); + + switch(chip) { + + case 0: /* select slot 3 chip */ + this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3); + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3); + break; + case 1: /* select slot 4 chip */ + this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4); + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4); + break; + + } +} + + +/* + * rtc_from4_nand_device_ready - hardware specific ready/busy check + * @mtd: MTD device structure + * + * This board provides the Ready/Busy state in the status register + * of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal. + * + */ +static int rtc_from4_nand_device_ready(struct mtd_info *mtd) +{ + unsigned short status; + + status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR)); + + return (status & RTC_FROM4_DEVICE_READY); + +} + + +/* + * deplete - code to perform device recovery in case there was a power loss + * @mtd: MTD device structure + * @chip: Chip to select (0 == slot 3, 1 == slot 4) + * + * If there was a sudden loss of power during an erase operation, a + * "device recovery" operation must be performed when power is restored + * to ensure correct operation. This routine performs the required steps + * for the requested chip. + * + * See page 86 of the data sheet for details. + * + */ +static void deplete(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + + /* wait until device is ready */ + while (!this->dev_ready(mtd)); + + this->select_chip(mtd, chip); + + /* Send the commands for device recovery, phase 1 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + + /* Send the commands for device recovery, phase 2 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + +} + + +#ifdef RTC_FROM4_HWECC +/* + * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function + * @mtd: MTD device structure + * @mode: I/O mode; read or write + * + * enable hardware ECC for data read or write + * + */ +static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode) +{ + volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL); + unsigned short status; + + switch (mode) { + case NAND_ECC_READ : + status = RTC_FROM4_RS_ECC_CTL_CLR + | RTC_FROM4_RS_ECC_CTL_FD_E; + + *rs_ecc_ctl = status; + break; + + case NAND_ECC_READSYN : + status = 0x00; + + *rs_ecc_ctl = status; + break; + + case NAND_ECC_WRITE : + status = RTC_FROM4_RS_ECC_CTL_CLR + | RTC_FROM4_RS_ECC_CTL_GEN + | RTC_FROM4_RS_ECC_CTL_FD_E; + + *rs_ecc_ctl = status; + break; + + default: + BUG(); + break; + } + +} + + +/* + * rtc_from4_calculate_ecc - hardware specific code to read ECC code + * @mtd: MTD device structure + * @dat: buffer containing the data to generate ECC codes + * @ecc_code ECC codes calculated + * + * The ECC code is calculated by the FPGA. All we have to do is read the values + * from the FPGA registers. + * + * Note: We read from the inverted registers, since data is inverted before + * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code + * + */ +static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN); + unsigned short value; + int i; + + for (i = 0; i < 8; i++) { + value = *rs_eccn; + ecc_code[i] = (unsigned char)value; + rs_eccn++; + } + ecc_code[7] |= 0x0f; /* set the last four bits (not used) */ +} + + +/* + * rtc_from4_correct_data - hardware specific code to correct data using ECC code + * @mtd: MTD device structure + * @buf: buffer containing the data to generate ECC codes + * @ecc1 ECC codes read + * @ecc2 ECC codes calculated + * + * The FPGA tells us fast, if there's an error or not. If no, we go back happy + * else we read the ecc results from the fpga and call the rs library to decode + * and hopefully correct the error. + * + */ +static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2) +{ + int i, j, res; + unsigned short status; + uint16_t par[6], syn[6]; + uint8_t ecc[8]; + volatile unsigned short *rs_ecc; + + status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK)); + + if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) { + return 0; + } + + /* Read the syndrom pattern from the FPGA and correct the bitorder */ + rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC); + for (i = 0; i < 8; i++) { + ecc[i] = revbits[(*rs_ecc) & 0xFF]; + rs_ecc++; + } + + /* convert into 6 10bit syndrome fields */ + par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) | + (((uint16_t)ecc[1] << 8) & 0x300)]; + par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) | + (((uint16_t)ecc[2] << 6) & 0x3c0)]; + par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) | + (((uint16_t)ecc[3] << 4) & 0x3f0)]; + par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) | + (((uint16_t)ecc[4] << 2) & 0x3fc)]; + par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) | + (((uint16_t)ecc[6] << 8) & 0x300)]; + par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0); + + /* Convert to computable syndrome */ + for (i = 0; i < 6; i++) { + syn[i] = par[0]; + for (j = 1; j < 6; j++) + if (par[j] != rs_decoder->nn) + syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)]; + + /* Convert to index form */ + syn[i] = rs_decoder->index_of[syn[i]]; + } + + /* Let the library code do its magic.*/ + res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL); + if (res > 0) { + DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " + "ECC corrected %d errors on read\n", res); + } + return res; +} + + +/** + * rtc_from4_errstat - perform additional error status checks + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state or the operation + * @status: status code returned from read status + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * + * Perform additional error status checks on erase and write failures + * to determine if errors are correctable. For this device, correctable + * 1-bit errors on erase and write are considered acceptable. + * + * note: see pages 34..37 of data sheet for details. + * + */ +static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) +{ + int er_stat=0; + int rtn, retlen; + size_t len; + uint8_t *buf; + int i; + + this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1); + + if (state == FL_ERASING) { + for (i=0; i<4; i++) { + if (status & 1<<(i+1)) { + this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<(i+1); /* err_ecc_not_avail */ + } + } + } + } else if (state == FL_WRITING) { + /* single bank write logic */ + this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<1; /* err_ecc_not_avail */ + } else { + len = mtd->oobblock; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n"); + er_stat = 1; /* if we can't check, assume failed */ + } else { + /* recovery read */ + /* page read */ + rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1); + if (rtn) { /* if read failed or > 1-bit error corrected */ + er_stat |= 1<<1; /* ECC read failed */ + } + kfree(buf); + } + } + } + + rtn = status; + if (er_stat == 0) { /* if ECC is available */ + rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */ + } + + return rtn; +} +#endif + + +/* + * Main initialization routine + */ +int __init rtc_from4_init (void) +{ + struct nand_chip *this; + unsigned short bcr1, bcr2, wcr2; + int i; + + /* Allocate memory for MTD device structure and private data */ + rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!rtc_from4_mtd) { + printk ("Unable to allocate Renesas NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&rtc_from4_mtd[1]); + + /* Initialize structures */ + memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + rtc_from4_mtd->priv = this; + + /* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */ + bcr1 = *SH77X9_BCR1 & ~0x0002; + bcr1 |= 0x0002; + *SH77X9_BCR1 = bcr1; + + /* set */ + bcr2 = *SH77X9_BCR2 & ~0x0c00; + bcr2 |= 0x0800; + *SH77X9_BCR2 = bcr2; + + /* set area 5 wait states */ + wcr2 = *SH77X9_WCR2 & ~0x1c00; + wcr2 |= 0x1c00; + *SH77X9_WCR2 = wcr2; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = rtc_from4_fio_base; + this->IO_ADDR_W = rtc_from4_fio_base; + /* Set address of hardware control function */ + this->hwcontrol = rtc_from4_hwcontrol; + /* Set address of chip select function */ + this->select_chip = rtc_from4_nand_select_chip; + /* command delay time (in us) */ + this->chip_delay = 100; + /* return the status of the Ready/Busy line */ + this->dev_ready = rtc_from4_nand_device_ready; + +#ifdef RTC_FROM4_HWECC + printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n"); + + this->eccmode = NAND_ECC_HW8_512; + this->options |= NAND_HWECC_SYNDROME; + /* return the status of extra status and ECC checks */ + this->errstat = rtc_from4_errstat; + /* set the nand_oobinfo to support FPGA H/W error detection */ + this->autooob = &rtc_from4_nand_oobinfo; + this->enable_hwecc = rtc_from4_enable_hwecc; + this->calculate_ecc = rtc_from4_calculate_ecc; + this->correct_data = rtc_from4_correct_data; +#else + printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); + + this->eccmode = NAND_ECC_SOFT; +#endif + + /* set the bad block tables to support debugging */ + this->bbt_td = &rtc_from4_bbt_main_descr; + this->bbt_md = &rtc_from4_bbt_mirror_descr; + + /* Scan to find existence of the device */ + if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) { + kfree(rtc_from4_mtd); + return -ENXIO; + } + + /* Perform 'device recovery' for each chip in case there was a power loss. */ + for (i=0; i < this->numchips; i++) { + deplete(rtc_from4_mtd, i); + } + +#if RTC_FROM4_NO_VIRTBLOCKS + /* use a smaller erase block to minimize wasted space when a block is bad */ + /* note: this uses eight times as much RAM as using the default and makes */ + /* mounts take four times as long. */ + rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS; +#endif + + /* Register the partitions */ + add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + +#ifdef RTC_FROM4_HWECC + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 0 + * primitve element to generate roots = 1 + * generator polinomial degree = 6 + */ + rs_decoder = init_rs(10, 0x409, 0, 1, 6); + if (!rs_decoder) { + printk (KERN_ERR "Could not create a RS decoder\n"); + nand_release(rtc_from4_mtd); + kfree(rtc_from4_mtd); + return -ENOMEM; + } +#endif + /* Return happy */ + return 0; +} +module_init(rtc_from4_init); + + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit rtc_from4_cleanup (void) +{ + /* Release resource, unregister partitions */ + nand_release(rtc_from4_mtd); + + /* Free the MTD device structure */ + kfree (rtc_from4_mtd); + +#ifdef RTC_FROM4_HWECC + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); + } +#endif +} +module_exit(rtc_from4_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("d.marlin + * + * Samsung S3C2410 NAND driver + * + * Changelog: + * 21-Sep-2004 BJD Initial version + * 23-Sep-2004 BJD Mulitple device support + * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode + * 12-Oct-2004 BJD Fixed errors in use of platform data + * 18-Feb-2005 BJD Fix sparse errors + * 14-Mar-2005 BJD Applied tglx's code reduction patch + * + * $Id: s3c2410.c,v 1.12 2005/03/17 11:31:26 bjd Exp $ + * + * 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 + +#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define PFX "s3c2410-nand: " + +#ifdef CONFIG_MTD_NAND_S3C2410_HWECC +static int hardware_ecc = 1; +#else +static int hardware_ecc = 0; +#endif + +/* new oob placement block for use with hardware ecc generation + */ + +static struct nand_oobinfo nand_hw_eccoob = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2 }, + .oobfree = { {8, 8} } +}; + +/* controller and mtd information */ + +struct s3c2410_nand_info; + +struct s3c2410_nand_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct s3c2410_nand_set *set; + struct s3c2410_nand_info *info; + int scan_res; +}; + +/* overview of the s3c2410 nand state */ + +struct s3c2410_nand_info { + /* mtd info */ + struct nand_hw_control controller; + struct s3c2410_nand_mtd *mtds; + struct s3c2410_platform_nand *platform; + + /* device info */ + struct device *device; + struct resource *area; + struct clk *clk; + void __iomem *regs; + int mtd_count; +}; + +/* conversion functions */ + +static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) +{ + return container_of(mtd, struct s3c2410_nand_mtd, mtd); +} + +static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) +{ + return s3c2410_nand_mtd_toours(mtd)->info; +} + +static struct s3c2410_nand_info *to_nand_info(struct device *dev) +{ + return dev_get_drvdata(dev); +} + +static struct s3c2410_platform_nand *to_nand_plat(struct device *dev) +{ + return dev->platform_data; +} + +/* timing calculations */ + +#define NS_IN_KHZ 10000000 + +static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) +{ + int result; + + result = (wanted * NS_IN_KHZ) / clk; + result++; + + pr_debug("result %d from %ld, %d\n", result, clk, wanted); + + if (result > max) { + printk("%d ns is too big for current clock rate %ld\n", + wanted, clk); + return -1; + } + + if (result < 1) + result = 1; + + return result; +} + +#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ) + +/* controller setup */ + +static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, + struct device *dev) +{ + struct s3c2410_platform_nand *plat = to_nand_plat(dev); + unsigned int tacls, twrph0, twrph1; + unsigned long clkrate = clk_get_rate(info->clk); + unsigned long cfg; + + /* calculate the timing information for the controller */ + + if (plat != NULL) { + tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); + twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); + twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); + } else { + /* default timings */ + tacls = 8; + twrph0 = 8; + twrph1 = 8; + } + + if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { + printk(KERN_ERR PFX "cannot get timings suitable for board\n"); + return -EINVAL; + } + + printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n", + to_ns(tacls, clkrate), + to_ns(twrph0, clkrate), + to_ns(twrph1, clkrate)); + + cfg = S3C2410_NFCONF_EN; + cfg |= S3C2410_NFCONF_TACLS(tacls-1); + cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); + cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); + + pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); + + writel(cfg, info->regs + S3C2410_NFCONF); + return 0; +} + +/* select chip */ + +static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct s3c2410_nand_info *info; + struct s3c2410_nand_mtd *nmtd; + struct nand_chip *this = mtd->priv; + unsigned long cur; + + nmtd = this->priv; + info = nmtd->info; + + cur = readl(info->regs + S3C2410_NFCONF); + + if (chip == -1) { + cur |= S3C2410_NFCONF_nFCE; + } else { + if (chip > nmtd->set->nr_chips) { + printk(KERN_ERR PFX "chip %d out of range\n", chip); + return; + } + + if (info->platform != NULL) { + if (info->platform->select_chip != NULL) + (info->platform->select_chip)(nmtd->set, chip); + } + + cur &= ~S3C2410_NFCONF_nFCE; + } + + writel(cur, info->regs + S3C2410_NFCONF); +} + +/* command and control functions */ + +static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + struct nand_chip *chip = mtd->priv; + unsigned long cur; + + switch (cmd) { + case NAND_CTL_SETNCE: + cur = readl(info->regs + S3C2410_NFCONF); + cur &= ~S3C2410_NFCONF_nFCE; + writel(cur, info->regs + S3C2410_NFCONF); + break; + + case NAND_CTL_CLRNCE: + cur = readl(info->regs + S3C2410_NFCONF); + cur |= S3C2410_NFCONF_nFCE; + writel(cur, info->regs + S3C2410_NFCONF); + break; + + case NAND_CTL_SETCLE: + chip->IO_ADDR_W = info->regs + S3C2410_NFCMD; + break; + + case NAND_CTL_SETALE: + chip->IO_ADDR_W = info->regs + S3C2410_NFADDR; + break; + + /* NAND_CTL_CLRCLE: */ + /* NAND_CTL_CLRALE: */ + default: + chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; + break; + } +} + +/* s3c2410_nand_devready() + * + * returns 0 if the nand is busy, 1 if it is ready +*/ + +static int s3c2410_nand_devready(struct mtd_info *mtd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; +} + +/* ECC handling functions */ + +static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", + mtd, dat, read_ecc, calc_ecc); + + pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", + read_ecc[0], read_ecc[1], read_ecc[2], + calc_ecc[0], calc_ecc[1], calc_ecc[2]); + + if (read_ecc[0] == calc_ecc[0] && + read_ecc[1] == calc_ecc[1] && + read_ecc[2] == calc_ecc[2]) + return 0; + + /* we curently have no method for correcting the error */ + + return -1; +} + +static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2410_NFCONF); + ctrl |= S3C2410_NFCONF_INITECC; + writel(ctrl, info->regs + S3C2410_NFCONF); +} + +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); + ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); + ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); + + pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", + ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + + +/* over-ride the standard functions for a little more speed? */ + +static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + readsb(this->IO_ADDR_R, buf, len); +} + +static void s3c2410_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + writesb(this->IO_ADDR_W, buf, len); +} + +/* device management functions */ + +static int s3c2410_nand_remove(struct device *dev) +{ + struct s3c2410_nand_info *info = to_nand_info(dev); + + dev_set_drvdata(dev, NULL); + + if (info == NULL) + return 0; + + /* first thing we need to do is release all our mtds + * and their partitions, then go through freeing the + * resources used + */ + + if (info->mtds != NULL) { + struct s3c2410_nand_mtd *ptr = info->mtds; + int mtdno; + + for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { + pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); + nand_release(&ptr->mtd); + } + + kfree(info->mtds); + } + + /* free the common resources */ + + if (info->clk != NULL && !IS_ERR(info->clk)) { + clk_disable(info->clk); + clk_unuse(info->clk); + clk_put(info->clk); + } + + if (info->regs != NULL) { + iounmap(info->regs); + info->regs = NULL; + } + + if (info->area != NULL) { + release_resource(info->area); + kfree(info->area); + info->area = NULL; + } + + kfree(info); + + return 0; +} + +#ifdef CONFIG_MTD_PARTITIONS +static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *mtd, + struct s3c2410_nand_set *set) +{ + if (set == NULL) + return add_mtd_device(&mtd->mtd); + + if (set->nr_partitions > 0 && set->partitions != NULL) { + return add_mtd_partitions(&mtd->mtd, + set->partitions, + set->nr_partitions); + } + + return add_mtd_device(&mtd->mtd); +} +#else +static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *mtd, + struct s3c2410_nand_set *set) +{ + return add_mtd_device(&mtd->mtd); +} +#endif + +/* s3c2410_nand_init_chip + * + * init a single instance of an chip +*/ + +static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd, + struct s3c2410_nand_set *set) +{ + struct nand_chip *chip = &nmtd->chip; + + chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; + chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; + chip->hwcontrol = s3c2410_nand_hwcontrol; + chip->dev_ready = s3c2410_nand_devready; + chip->write_buf = s3c2410_nand_write_buf; + chip->read_buf = s3c2410_nand_read_buf; + chip->select_chip = s3c2410_nand_select_chip; + chip->chip_delay = 50; + chip->priv = nmtd; + chip->options = 0; + chip->controller = &info->controller; + + nmtd->info = info; + nmtd->mtd.priv = chip; + nmtd->set = set; + + if (hardware_ecc) { + chip->correct_data = s3c2410_nand_correct_data; + chip->enable_hwecc = s3c2410_nand_enable_hwecc; + chip->calculate_ecc = s3c2410_nand_calculate_ecc; + chip->eccmode = NAND_ECC_HW3_512; + chip->autooob = &nand_hw_eccoob; + } else { + chip->eccmode = NAND_ECC_SOFT; + } +} + +/* s3c2410_nand_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices +*/ + +static int s3c2410_nand_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c2410_platform_nand *plat = to_nand_plat(dev); + struct s3c2410_nand_info *info; + struct s3c2410_nand_mtd *nmtd; + struct s3c2410_nand_set *sets; + struct resource *res; + int err = 0; + int size; + int nr_sets; + int setno; + + pr_debug("s3c2410_nand_probe(%p)\n", dev); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + printk(KERN_ERR PFX "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info, sizeof(*info)); + dev_set_drvdata(dev, info); + + spin_lock_init(&info->controller.lock); + + /* get the clock source and enable it */ + + info->clk = clk_get(dev, "nand"); + if (IS_ERR(info->clk)) { + printk(KERN_ERR PFX "failed to get clock"); + err = -ENOENT; + goto exit_error; + } + + clk_use(info->clk); + clk_enable(info->clk); + + /* allocate and map the resource */ + + res = pdev->resource; /* assume that the flash has one resource */ + size = res->end - res->start + 1; + + info->area = request_mem_region(res->start, size, pdev->name); + + if (info->area == NULL) { + printk(KERN_ERR PFX "cannot reserve register region\n"); + err = -ENOENT; + goto exit_error; + } + + info->device = dev; + info->platform = plat; + info->regs = ioremap(res->start, size); + + if (info->regs == NULL) { + printk(KERN_ERR PFX "cannot reserve register region\n"); + err = -EIO; + goto exit_error; + } + + printk(KERN_INFO PFX "mapped registers at %p\n", info->regs); + + /* initialise the hardware */ + + err = s3c2410_nand_inithw(info, dev); + if (err != 0) + goto exit_error; + + sets = (plat != NULL) ? plat->sets : NULL; + nr_sets = (plat != NULL) ? plat->nr_sets : 1; + + info->mtd_count = nr_sets; + + /* allocate our information */ + + size = nr_sets * sizeof(*info->mtds); + info->mtds = kmalloc(size, GFP_KERNEL); + if (info->mtds == NULL) { + printk(KERN_ERR PFX "failed to allocate mtd storage\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info->mtds, size); + + /* initialise all possible chips */ + + nmtd = info->mtds; + + for (setno = 0; setno < nr_sets; setno++, nmtd++) { + pr_debug("initialising set %d (%p, info %p)\n", + setno, nmtd, info); + + s3c2410_nand_init_chip(info, nmtd, sets); + + nmtd->scan_res = nand_scan(&nmtd->mtd, + (sets) ? sets->nr_chips : 1); + + if (nmtd->scan_res == 0) { + s3c2410_nand_add_partition(info, nmtd, sets); + } + + if (sets != NULL) + sets++; + } + + pr_debug("initialised ok\n"); + return 0; + + exit_error: + s3c2410_nand_remove(dev); + + if (err == 0) + err = -EINVAL; + return err; +} + +static struct device_driver s3c2410_nand_driver = { + .name = "s3c2410-nand", + .bus = &platform_bus_type, + .probe = s3c2410_nand_probe, + .remove = s3c2410_nand_remove, +}; + +static int __init s3c2410_nand_init(void) +{ + printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n"); + return driver_register(&s3c2410_nand_driver); +} + +static void __exit s3c2410_nand_exit(void) +{ + driver_unregister(&s3c2410_nand_driver); +} + +module_init(s3c2410_nand_init); +module_exit(s3c2410_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("S3C2410 MTD NAND driver"); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/sharpsl.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/sharpsl.c --- linux-2.6.9-intc1/drivers/mtd/nand/sharpsl.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/sharpsl.c 2005-03-21 21:28:43.000000000 -0500 @@ -0,0 +1,260 @@ +/* + * drivers/mtd/nand/sharpsl.c + * + * Copyright (C) 2004 Richard Purdie + * + * $Id: sharpsl.c,v 1.4 2005/01/23 11:09:19 rpurdie Exp $ + * + * Based on Sharp's NAND driver sharp_sl.c + * + * 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 + +static void __iomem *sharpsl_io_base; +static int sharpsl_phys_base = 0x0C000000; + +/* register offset */ +#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ +#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ +#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ +#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ +#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ +#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ +#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ + +/* Flash control bit */ +#define FLRYBY (1 << 5) +#define FLCE1 (1 << 4) +#define FLWP (1 << 3) +#define FLALE (1 << 2) +#define FLCLE (1 << 1) +#define FLCE0 (1 << 0) + + +/* + * MTD structure for SharpSL + */ +static struct mtd_info *sharpsl_mtd = NULL; + +/* + * Define partitions for flash device + */ +#define DEFAULT_NUM_PARTITIONS 3 + +static int nr_partitions; +static struct mtd_partition sharpsl_nand_default_partition_info[] = { + { + .name = "System Area", + .offset = 0, + .size = 7 * 1024 * 1024, + }, + { + .name = "Root Filesystem", + .offset = 7 * 1024 * 1024, + .size = 30 * 1024 * 1024, + }, + { + .name = "Home Filesystem", + .offset = MTDPART_OFS_APPEND , + .size = MTDPART_SIZ_FULL , + }, +}; + +/* + * hardware specific access to control-lines + */ +static void +sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd) +{ + switch (cmd) { + case NAND_CTL_SETCLE: + writeb(readb(FLASHCTL) | FLCLE, FLASHCTL); + break; + case NAND_CTL_CLRCLE: + writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL); + break; + + case NAND_CTL_SETALE: + writeb(readb(FLASHCTL) | FLALE, FLASHCTL); + break; + case NAND_CTL_CLRALE: + writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL); + break; + + case NAND_CTL_SETNCE: + writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL); + break; + case NAND_CTL_CLRNCE: + writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL); + break; + } +} + +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr sharpsl_bbt = { + .options = 0, + .offs = 4, + .len = 2, + .pattern = scan_ff_pattern +}; + +static int +sharpsl_nand_dev_ready(struct mtd_info* mtd) +{ + return !((readb(FLASHCTL) & FLRYBY) == 0); +} + +static void +sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode) +{ + writeb(0 ,ECCCLRR); +} + +static int +sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + ecc_code[0] = ~readb(ECCLPUB); + ecc_code[1] = ~readb(ECCLPLB); + ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; + return readb(ECCCNTR) != 0; +} + + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + + +/* + * Main initialization routine + */ +int __init +sharpsl_nand_init(void) +{ + struct nand_chip *this; + struct mtd_partition* sharpsl_partition_info; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), + GFP_KERNEL); + if (!sharpsl_mtd) { + printk ("Unable to allocate SharpSL NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* map physical adress */ + sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); + if(!sharpsl_io_base){ + printk("ioremap to access Sharp SL NAND chip failed\n"); + kfree(sharpsl_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&sharpsl_mtd[1]); + + /* Initialize structures */ + memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + sharpsl_mtd->priv = this; + + /* + * PXA initialize + */ + writeb(readb(FLASHCTL) | FLWP, FLASHCTL); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = FLASHIO; + this->IO_ADDR_W = FLASHIO; + /* Set address of hardware control function */ + this->hwcontrol = sharpsl_nand_hwcontrol; + this->dev_ready = sharpsl_nand_dev_ready; + /* 15 us command delay time */ + this->chip_delay = 15; + /* set eccmode using hardware ECC */ + this->eccmode = NAND_ECC_HW3_256; + this->enable_hwecc = sharpsl_nand_enable_hwecc; + this->calculate_ecc = sharpsl_nand_calculate_ecc; + this->correct_data = nand_correct_data; + this->badblock_pattern = &sharpsl_bbt; + + /* Scan to find existence of the device */ + err=nand_scan(sharpsl_mtd,1); + if (err) { + iounmap(sharpsl_io_base); + kfree(sharpsl_mtd); + return err; + } + + /* Register the partitions */ + sharpsl_mtd->name = "sharpsl-nand"; + nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, + &sharpsl_partition_info, 0); + + if (nr_partitions <= 0) { + nr_partitions = DEFAULT_NUM_PARTITIONS; + sharpsl_partition_info = sharpsl_nand_default_partition_info; + if (machine_is_poodle()) { + sharpsl_partition_info[1].size=30 * 1024 * 1024; + } else if (machine_is_corgi() || machine_is_shepherd()) { + sharpsl_partition_info[1].size=25 * 1024 * 1024; + } else if (machine_is_husky()) { + sharpsl_partition_info[1].size=53 * 1024 * 1024; + } + } + + if (machine_is_husky()) { + /* Need to use small eraseblock size for backward compatibility */ + sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS; + } + + add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); + + /* Return happy */ + return 0; +} +module_init(sharpsl_nand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit sharpsl_nand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1]; + + /* Release resources, unregister device */ + nand_release(sharpsl_mtd); + + iounmap(sharpsl_io_base); + + /* Free the MTD device structure */ + kfree(sharpsl_mtd); +} +module_exit(sharpsl_nand_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/spia.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/spia.c --- linux-2.6.9-intc1/drivers/mtd/nand/spia.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/spia.c 2005-03-21 21:28:43.000000000 -0500 @@ -8,7 +8,7 @@ * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $ + * $Id: spia.c,v 1.24 2004/11/04 12:53:10 gleixner Exp $ * * 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 @@ -59,10 +59,10 @@ static int spia_pedr = SPIA_PEDR; static int spia_peddr = SPIA_PEDDR; -MODULE_PARM(spia_io_base, "i"); -MODULE_PARM(spia_fio_base, "i"); -MODULE_PARM(spia_pedr, "i"); -MODULE_PARM(spia_peddr, "i"); +module_param(spia_io_base, int, 0); +module_param(spia_fio_base, int, 0); +module_param(spia_pedr, int, 0); +module_param(spia_peddr, int, 0); /* * Define partitions for flash device @@ -132,8 +132,8 @@ (*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07; /* Set address of NAND IO lines */ - this->IO_ADDR_R = spia_fio_base; - this->IO_ADDR_W = spia_fio_base; + this->IO_ADDR_R = (void __iomem *) spia_fio_base; + this->IO_ADDR_W = (void __iomem *) spia_fio_base; /* Set address of hardware control function */ this->hwcontrol = spia_hwcontrol; /* 15 us command delay time */ @@ -145,14 +145,6 @@ return -ENXIO; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk ("Unable to allocate NAND data buffer for SPIA.\n"); - kfree (spia_mtd); - return -ENOMEM; - } - /* Register the partitions */ add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); @@ -167,13 +159,8 @@ #ifdef MODULE static void __exit spia_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) &spia_mtd[1]; - - /* Unregister the device */ - del_mtd_device (spia_mtd); - - /* Free internal data buffer */ - kfree (this->data_buf); + /* Release resources, unregister device */ + nand_release (spia_mtd); /* Free the MTD device structure */ kfree (spia_mtd); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/toto.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/toto.c --- linux-2.6.9-intc1/drivers/mtd/nand/toto.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/toto.c 2005-03-21 21:28:43.000000000 -0500 @@ -15,7 +15,7 @@ * This is a device driver for the NAND flash device found on the * TI fido board. It supports 32MiB and 64MiB cards * - * $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $ + * $Id: toto.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $ */ #include @@ -37,7 +37,7 @@ */ static struct mtd_info *toto_mtd = NULL; -static int toto_io_base = OMAP_FLASH_1_BASE; +static unsigned long toto_io_base = OMAP_FLASH_1_BASE; #define CONFIG_NAND_WORKAROUND 1 @@ -155,14 +155,6 @@ goto out_mtd; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n"); - err = -ENOMEM; - goto out_mtd; - } - /* Register the partitions */ switch(toto_mtd->size){ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break; @@ -194,16 +186,8 @@ */ static void __exit toto_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) &toto_mtd[1]; - - /* Unregister partitions */ - del_mtd_partitions(toto_mtd); - - /* Unregister the device */ - del_mtd_device (toto_mtd); - - /* Free internal data buffers */ - kfree (this->data_buf); + /* Release resources, unregister device */ + nand_release (toto_mtd); /* Free the MTD device structure */ kfree (toto_mtd); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/tx4925ndfmc.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/tx4925ndfmc.c --- linux-2.6.9-intc1/drivers/mtd/nand/tx4925ndfmc.c 2004-10-18 17:54:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/tx4925ndfmc.c 2005-03-21 21:28:43.000000000 -0500 @@ -11,7 +11,7 @@ * Derived from drivers/mtd/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: tx4925ndfmc.c,v 1.3 2004/07/20 02:44:26 dwmw2 Exp $ + * $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $ * * Copyright (C) 2001 Toshiba Corporation * @@ -340,8 +340,8 @@ tx4925ndfmc_mtd->priv = this; /* Set address of NAND IO lines */ - this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr); - this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr); + this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr); + this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr); this->hwcontrol = tx4925ndfmc_hwcontrol; this->enable_hwecc = tx4925ndfmc_enable_hwecc; this->calculate_ecc = tx4925ndfmc_readecc; @@ -363,14 +363,6 @@ goto out_ior; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk ("Unable to allocate NAND data buffer for RBTX4925.\n"); - err = -ENOMEM; - goto out_ior; - } - /* Register the partitions */ #ifdef CONFIG_MTD_CMDLINE_PARTS { @@ -391,14 +383,12 @@ default: { printk ("Unsupported SmartMedia device\n"); err = -ENXIO; - goto out_buf; + goto out_ior; } } #endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */ goto out; -out_buf: - kfree (this->data_buf); out_ior: out: return err; @@ -412,16 +402,8 @@ #ifdef MODULE static void __exit tx4925ndfmc_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1]; - - /* Unregister partitions */ - del_mtd_partitions(tx4925ndfmc_mtd); - - /* Unregister the device */ - del_mtd_device (tx4925ndfmc_mtd); - - /* Free internal data buffers */ - kfree (this->data_buf); + /* Release resources, unregister device */ + nand_release (tx4925ndfmc_mtd); /* Free the MTD device structure */ kfree (tx4925ndfmc_mtd); diff -Nur linux-2.6.9-intc1/drivers/mtd/nand/tx4938ndfmc.c linux-2.6.9-intc1-mih1/drivers/mtd/nand/tx4938ndfmc.c --- linux-2.6.9-intc1/drivers/mtd/nand/tx4938ndfmc.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nand/tx4938ndfmc.c 2005-03-21 21:28:43.000000000 -0500 @@ -10,7 +10,7 @@ * * Based on spia.c by Steven J. Hill * - * $Id: tx4938ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $ + * $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $ * * Copyright (C) 2000-2001 Toshiba Corporation * @@ -365,14 +365,6 @@ return -ENXIO; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk ("Unable to allocate NAND data buffer for TX4938.\n"); - kfree (tx4938ndfmc_mtd); - return -ENOMEM; - } - if (protected) { printk(KERN_INFO "TX4938 NDFMC: write protected.\n"); tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE); @@ -401,19 +393,11 @@ */ static void __exit tx4938ndfmc_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv; - - /* Unregister the device */ -#ifdef CONFIG_MTD_CMDLINE_PARTS - del_mtd_partitions(tx4938ndfmc_mtd); -#endif - del_mtd_device (tx4938ndfmc_mtd); + /* Release resources, unregister device */ + nand_release (tx4938ndfmc_mtd); /* Free the MTD device structure */ kfree (tx4938ndfmc_mtd); - - /* Free internal data buffer */ - kfree (this->data_buf); } module_exit(tx4938ndfmc_cleanup); diff -Nur linux-2.6.9-intc1/drivers/mtd/nftlcore.c linux-2.6.9-intc1-mih1/drivers/mtd/nftlcore.c --- linux-2.6.9-intc1/drivers/mtd/nftlcore.c 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nftlcore.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,7 +1,7 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftlcore.c,v 1.96 2004/06/28 13:52:55 dbrown Exp $ */ +/* $Id: nftlcore.c,v 1.97 2004/11/16 18:28:59 dwmw2 Exp $ */ /* The contents of this file are distributed under the GNU General @@ -421,7 +421,7 @@ return targetEUN; } -u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) +static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) { /* This is the part that needs some cleverness applied. For now, I'm doing the minimum applicable to actually @@ -731,7 +731,7 @@ ****************************************************************************/ -struct mtd_blktrans_ops nftl_tr = { +static struct mtd_blktrans_ops nftl_tr = { .name = "nftl", .major = NFTL_MAJOR, .part_bits = NFTL_PARTN_BITS, @@ -747,9 +747,9 @@ extern char nftlmountrev[]; -int __init init_nftl(void) +static int __init init_nftl(void) { - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.96 $, nftlmount.c %s\n", nftlmountrev); + printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.97 $, nftlmount.c %s\n", nftlmountrev); return register_mtd_blktrans(&nftl_tr); } diff -Nur linux-2.6.9-intc1/drivers/mtd/nftlmount.c linux-2.6.9-intc1-mih1/drivers/mtd/nftlmount.c --- linux-2.6.9-intc1/drivers/mtd/nftlmount.c 2004-10-18 17:54:38.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/nftlmount.c 2005-03-21 21:28:41.000000000 -0500 @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.36 2004/06/28 13:52:55 dbrown Exp $ + * $Id: nftlmount.c,v 1.40 2004/11/22 14:38:29 kalev Exp $ * * 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 @@ -31,7 +31,7 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.36 $"; +char nftlmountrev[]="$Revision: 1.40 $"; /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update @@ -41,7 +41,6 @@ static int find_boot_record(struct NFTLrecord *nftl) { struct nftl_uci1 h1; - struct nftl_oob oob; unsigned int block, boot_record_count = 0; size_t retlen; u8 buf[SECTORSIZE]; @@ -319,6 +318,7 @@ memset(instr, 0, sizeof(struct erase_info)); /* XXX: use async erase interface, XXX: test return code */ + instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; MTD_ERASE(nftl->mbd.mtd, instr); diff -Nur linux-2.6.9-intc1/drivers/mtd/redboot.c linux-2.6.9-intc1-mih1/drivers/mtd/redboot.c --- linux-2.6.9-intc1/drivers/mtd/redboot.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/mtd/redboot.c 2005-03-21 21:28:41.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.15 2004/08/10 07:55:16 dwmw2 Exp $ + * $Id: redboot.c,v 1.17 2004/11/22 11:33:56 ijc Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -30,6 +30,9 @@ struct fis_list *next; }; +static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; +module_param(directory, int, 0); + static inline int redboot_checksum(struct fis_image_desc *img) { /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ @@ -50,6 +53,8 @@ char *nullname; int namelen = 0; int nulllen = 0; + int numslots; + unsigned long offset; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED static char nullstring[] = "unallocated"; #endif @@ -59,8 +64,15 @@ if (!buf) return -ENOMEM; - /* Read the start of the last erase block */ - ret = master->read(master, master->size - master->erasesize, + if ( directory < 0 ) + offset = master->size + directory*master->erasesize; + else + offset = directory*master->erasesize; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); + + ret = master->read(master, offset, master->erasesize, &retlen, (void *)buf); if (ret) @@ -71,12 +83,16 @@ goto out; } - /* RedBoot image could appear in any of the first three slots */ - for (i = 0; i < 3; i++) { - if (!memcmp(buf[i].name, "RedBoot", 8)) + numslots = (master->erasesize / sizeof(struct fis_image_desc)); + for (i = 0; i < numslots; i++) { + if (buf[i].name[0] == 0xff) { + i = numslots; + break; + } + if (!memcmp(buf[i].name, "FIS directory", 14)) break; } - if (i == 3) { + if (i == numslots) { /* Didn't find it */ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", master->name); @@ -84,7 +100,7 @@ goto out; } - for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) { + for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) diff -Nur linux-2.6.9-intc1/drivers/mtd/ssfdc.c linux-2.6.9-intc1-mih1/drivers/mtd/ssfdc.c --- linux-2.6.9-intc1/drivers/mtd/ssfdc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/mtd/ssfdc.c 2005-03-21 21:28:41.000000000 -0500 @@ -0,0 +1,1132 @@ +/* + * drivers/mtd/ssfdc.c + * + * Copyright (C) 2003 Simon Haynes (simon@baydel.con) + * Baydel Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This module provides a translation layer, via mtd, for smart + * media card access. It essentially enables the possibility + * of using cards on a hardware which does not have a hardware translation + * layer and interchanging them with hardware that does ie: PC card readers + * + * I had to write this module for a specific task and in a short timeframe + * for this reason I have imposed some restricions to make the job easier. + * + * To build an compile the driver I added the following lines + * to mtd/Config.in + * + * dep_tristate ' SSFDC support' CONFIG_SSFDC $CONFIG_MTD + * + * to /mtd/Makefile + * + * obj-$(CONFIG_SSFDC) += ssfdc.o + * + * and compiled the kernel via the usual methods. + * + * I am sure that there are many problems I don't know about but here are + * some that I know of + * + * Currently the driver uses MAJOR number 44 which I think is FTL or NFTL + * I did this because I wanted a static number and I didn't know + * how to go about getting a new one. This needs addressing + * The dev nodes required are like standard. I only use minor 0 + * (/dev/ssfdca), and minor 1 (/dev/ssfdca1). + * You should be able to run fdisk on /dev/ssfdca and the first partition + * is /dev/ssfdca1. There is no working code in the module for changing the + * SMC and rebuilding the maps so the card should not be changed once the + * module is loaded. At present I only look for 1 partition. But this is a + * small commented hack. + * + * There is no support cards which do not have a 512 byte page size with 16 + * bytes of oob and an erase size of 16K. + * There are no checks for this at present. In addition the MTD reported size + * must be 16M or a multiple. + * + * Code to handle multiple partitions or multiple cards is incomplete + * Need to allocate data buffer and oob buffer on a per partition basis. + * As I am only concerned with one partition I will do this if I ever need to. + * The cached physical address variable also needs this attention. + * + * Recently I have started to work on media changes. Some of this is specific + * to my hardware and you will see references to pt_ssfdc_smc and smc_status. + * This code is incomplete and does not work. I have commented it for the moment + * but it should give an indication of what I think is required. Maybe there is + * something it mtd that can help + * + * 17th August 2004 MHB + * + * Following updating CVS I noticed some single bit data corruption. I believe + * that this was down to the fact that I was using mtd->read instead of mtd->read_ecc + * and that mtd->read was applying it's own error corretion from the wrong ecc bytes + * I have now corrected this. + * + * During this time I noticed that while in allocate new I only seem to look for blocks + * in 1 zone. So this limits the partition size to 16MB with all the other SMC size + * restrictions + + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if (LINUX_VERSION_CODE >= 0x20100) +#include +#endif +#if (LINUX_VERSION_CODE >= 0x20303) +#include +#endif + +#include + +#define SSFDC_FORMAT 1 + +#define PDEBUG(fmt, args...) + +#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT + +#if (LINUX_VERSION_CODE < 0x20320) +#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +#define blk_init_queue(q, req) q = (req) +#define blk_cleanup_queue(q) q = NULL +#define request_arg_t void +#else +#define request_arg_t request_queue_t *q +#endif + +#define TRUE 1 +#define FALSE 0 + +#define SSFDC_MAJOR 44 + +#define MAJOR_NR SSFDC_MAJOR +#define DEVICE_NAME "ssfdc" +#define DEVICE_REQUEST do_ssfdc_request +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#include "/home/simon/ebony/dbwhatu/dbwhatu/smccontrol.h" + + + +#define ZONE_SIZE (16 * 1024 * 1024) +#define SMC_BLOCK_SIZE (16 * 1024) +#define SECTOR_SIZE 512 +#define SECTORS_PER_ZONE (ZONE_SIZE / SECTOR_SIZE) +#define BLOCKS_PER_ZONE (ZONE_SIZE / SMC_BLOCK_SIZE) +#define SECTORS_PER_BLOCK (SMC_BLOCK_SIZE / SECTOR_SIZE) +#define OOB_SIZE 16 + + +#define MAX_DEVICES 4 +#define MAX_PARTITIONS 8 +#define PARTITION_BITS 3 +#define MAX_ZONES 8 + + +int ssfdc_major = SSFDC_MAJOR; +unsigned int ssfdc_cached = 0xFFFFFFFF; +static unsigned char ssfdc_scratch[16384]; +static unsigned char ssfdc_buffer[16]; +static unsigned char ssfdc_ffoob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; +static unsigned char ssfdc_oob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; + + +static struct nand_oobinfo ssfdc_ffoob_info = { + .useecc = 0, +}; + + +typedef struct minor_t { + atomic_t open; + int cached; + unsigned char * pt_data; + unsigned char * pt_oob; +} minor_t; + + + +typedef struct partition_t { + int type; + struct mtd_info *mtd; + int count; + unsigned int *zone; + unsigned int zoneCount; + minor_t minor[MAX_PARTITIONS]; + unsigned int last_written[MAX_ZONES]; +} partition_t; + +partition_t SMCParts[MAX_DEVICES]; + + +static unsigned char ssfdc_ecc[] = {14, 13, 15, 9, 8, 10}; + +static struct hd_struct ssfdc_hd[MAX_DEVICES * MAX_PARTITIONS]; +static int ssfdc_sizes[MAX_DEVICES * MAX_PARTITIONS]; +static int ssfdc_blocksizes[MAX_DEVICES * MAX_PARTITIONS]; +smc_control * pt_ssfdc_smc; + + +static struct gendisk ssfdc_gendisk = { + major: SSFDC_MAJOR, + major_name: "ssfdc", + minor_shift: PARTITION_BITS, + max_p: MAX_PARTITIONS, + part: ssfdc_hd, + sizes: ssfdc_sizes, +}; + + +static int ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); +static int ssfdc_open(struct inode *inode, struct file *file); +static int ssfdc_close(struct inode *inode, struct file *file); +static int ssfdc_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); +static int ssfdc_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); +static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block); +static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset); +static int ssfdc_read_partitions(partition_t * pt_smcpart); +static void ssfdc_notify_add(struct mtd_info *mtd); +static void ssfdc_notify_remove(struct mtd_info *mtd); +static void ssfdc_tables(partition_t * pt_smcpart); +static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc); +static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone); +int ssfdc_parity(int number); +static void ssfdc_erase_callback(struct erase_info *erase); + + + +static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wq); + + +static struct mtd_notifier ssfdc_notifier = { + add: ssfdc_notify_add, + remove: ssfdc_notify_remove, +}; + + + +static struct block_device_operations ssfdc_fops = { + open: ssfdc_open, + release: ssfdc_close, + ioctl: ssfdc_ioctl, +}; + +static struct semaphore ssfdc_semaphore; + +static void ssfdc_notify_add(struct mtd_info *mtd) { + + + + + if(mtd->index >= 1) return; // Hack to limit SSFDC to 1 partition + + if( ((mtd->size % ZONE_SIZE) != 0) && (mtd->size < (ZONE_SIZE * MAX_ZONES)) ){ + PDEBUG("ssfdc_notify_add : mtd partition %d is not modulus 16M, not SSFDC\n", mtd->index); + } + else { + memset((void *)&SMCParts[mtd->index].type, 0, sizeof(partition_t)); + SMCParts[mtd->index].mtd = mtd; + SMCParts[mtd->index].count = mtd->index; + SMCParts[mtd->index].type = 1; + SMCParts[mtd->index].zoneCount = mtd->size / ZONE_SIZE; + SMCParts[mtd->index].zone = kmalloc(SMCParts[mtd->index].zoneCount * 8192, GFP_KERNEL); + + + if(!SMCParts[mtd->index].zone) { + printk(KERN_NOTICE "ssfdc_notify_add : mtd partition %d, failed to allocate mapping table\n", mtd->index); + SMCParts[mtd->index].type = 0; + } + else { + memset((void *)SMCParts[mtd->index].zone, 0xFF, SMCParts[mtd->index].zoneCount * 8192); + } + + ssfdc_read_partitions((partition_t *)&SMCParts[mtd->index].type); + } + return; + +} +static int ssfdc_read_partitions(partition_t * pt_smcpart) { + + int whole, i, j, size; + +//=printk("ssfdc_read_partitions : start\n"); + + for(i=0; iminor[i].open) > 1)) { +//=printk("ssfdc_read_partitions : part %d busy\n", i); + + return -EBUSY; + } + + +//=printk("ssfdc_read_partitions : tables start\n"); + ssfdc_tables(pt_smcpart); +//=printk("ssfdc_read_partitions : tables end\n"); + + whole = pt_smcpart->count << PARTITION_BITS; + + + j = MAX_PARTITIONS - 1; + while (j-- > 0) { + if (ssfdc_hd[whole+j].nr_sects > 0) { + kdev_t rdev = MKDEV(SSFDC_MAJOR, whole+j); + invalidate_device(rdev, 1); + } + ssfdc_hd[whole+j].start_sect = 0; + ssfdc_hd[whole+j].nr_sects = 0; + } + + + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + size /= (0x8 * 0x20); + size = size * (0x8 * 0x20); + +//=printk("ssfdc_read_partitions : register start\n"); + + register_disk(&ssfdc_gendisk, whole >> PARTITION_BITS, MAX_PARTITIONS, + &ssfdc_fops, size); + +//=printk("ssfdc_read_partitions : register end\n"); + + + return 0; +} + + +static void ssfdc_notify_remove(struct mtd_info *mtd) { +int i, j, whole; + + i=mtd->index; + whole = i << PARTITION_BITS; + if(SMCParts[i].mtd == mtd) { + if(SMCParts[i].zone)kfree(SMCParts[i].zone); + memset((void *)&SMCParts[i].type, 0, sizeof(partition_t)); + for (j = 0; j < MAX_PARTITIONS; j++) { + if (ssfdc_hd[whole+j].nr_sects > 0) { + ssfdc_hd[whole+j].start_sect = 0; + ssfdc_hd[whole+j].nr_sects=0; + } + } + return; + } + return; +} + + + +static int ssfdc_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) { + + int minor = MINOR(inode->i_rdev); + int ret = -EINVAL; + partition_t * pt_smcpart = (partition_t *)&SMCParts[(minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS].type; + struct hd_geometry geo; + int size; +/* + unsigned char smc_status; + + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + ret = 1; + goto ssfdc_ioctl_error; + } + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + switch(cmd) { + + case HDIO_GETGEO: + memset(&geo, 0, sizeof(geo)); + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + size /= (0x8 * 0x20); + geo.heads = 0x8; + geo.sectors = 0x20; + geo.cylinders = size; + geo.start = ssfdc_hd[minor].start_sect; +// printk(KERN_WARNING "ssfdc : HDIO_GETGEO heads %d, sectors %d, cylinders %d, start %lu\n", +// geo.heads, geo.sectors, geo.cylinders, geo.start); + copy_to_user((void *)arg, &geo, sizeof(geo)); + ret = 0; + break; + + case BLKGETSIZE64: + case BLKGETSIZE: + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + //=printk(KERN_WARNING "ssfdc : BLKGETSIZE %d, minor %d\n", size, minor); + ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); + break; + case BLKSSZGET: + size = 512; + ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); + break; + break; + + case BLKRRPART: + if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; + ssfdc_read_partitions(pt_smcpart); + ret=0; + break; + case BLKFLSBUF: + printk(KERN_WARNING "ssfdc : block ioctl 0x%x\n", cmd); + break; + + default: + printk(KERN_WARNING "ssfdc: unknown ioctl 0x%x\n", cmd); + } + +//ssfdc_ioctl_error: + return(ret); + +} +static int ssfdc_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *pt_smcpart; + int index; + + if (minor >= MAX_MTD_DEVICES) + return -ENODEV; + + index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + + if(SMCParts[index].type != SSFDC_FORMAT) + return -ENXIO; + + pt_smcpart = &SMCParts[index]; + + + if(!pt_smcpart->zone) + return -ENXIO; + + + BLK_INC_USE_COUNT; + + if (!get_mtd_device(pt_smcpart->mtd, -1)) { + BLK_DEC_USE_COUNT; + return -ENXIO; + } + + if ((file->f_mode & 2) && !(pt_smcpart->mtd->flags & MTD_CLEAR_BITS) ) { + put_mtd_device(pt_smcpart->mtd); + BLK_DEC_USE_COUNT; + return -EROFS; + } + + + atomic_inc(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); + + PDEBUG("ssfdc_open : device %d\n", minor); + + return(0); +} + +static void ssfdc_tables(partition_t * pt_smcpart) { + + int * logical, * physical; + int offset = 0; + int zone, block; + int i, retlen; + int block_address, parity; + int h, l; + + for(zone=0; zonezoneCount; zone++) { + logical = pt_smcpart->zone + (2048 * zone); + memset((void *)logical, 0xFF, 1024 * sizeof(int)); + physical = pt_smcpart->zone + (2048 * zone) + 1024; + memset((void *)physical, 0xFF, 1024 * sizeof(int)); + + for(block=0; block < 1024; block++) { + offset = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, offset, sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_tables : failed to read OOB\n"); + pt_smcpart->type = 0; + return; + } + + l = (ssfdc_buffer[7] & 0xFF); + h = (ssfdc_buffer[6] & 0xFF); + block_address = l + (h << 8L); + + if((block_address & ~0x7FF) != 0x1000) { + continue; + } + + parity = block_address & 0x01; + + block_address &= 0x7FF; + block_address >>= 1; + + + if(ssfdc_parity(block_address) != parity) { + printk(KERN_WARNING "ssfdc_tables : parity error offset 0x%x, block 0x%x, parity 0x%x\nOOB : " + , offset, block_address, parity); + for(i=0; i<16; i++) { + printk("0x%02x ", (unsigned char)ssfdc_buffer[i]); + } + printk("\n"); + pt_smcpart->type = 0; + return; + } + + + /* Ok we have a valid block number so insert it */ + *(logical + block_address) = (offset/SMC_BLOCK_SIZE); + PDEBUG("ssfdc_tables : logical 0x%x + 0x%x = 0x%x\n", + (unsigned int)logical, block_address, (offset/SMC_BLOCK_SIZE)); + *(physical + block) = block_address; + PDEBUG("ssfdc_tables : physical 0x%x + 0x%x = 0x%x\n", (unsigned int)physical, block, block_address); + + + } + } + return; +} +int ssfdc_parity(int number) { + int i; + int parity = 1; // the 0x1000 bit + + for(i=0; i<10; i++) { + parity += ((number >> i) & 1); + } + PDEBUG("ssfdc_parity : number 0x%x, parity 0x%x\n", number, parity); + return(parity % 2); +} +static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block) { + + unsigned int * logical; + + logical = pt_smcpart->zone + (zone * 2048); + + logical += block; + + if(*logical == 0xFFFFFFFF) { + PDEBUG("ssfdc_physical : physical for zone %d, block %d invalid\n", zone, block); + return(-1); + } + + PDEBUG("ssfdc_physical : physical for zone %d, block %d, 0x%x\n", zone, block, (*logical * SMC_BLOCK_SIZE)); + return(*logical * SMC_BLOCK_SIZE); +} + +static int ssfdc_close(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *pt_smcpart; + int index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + if (minor >= MAX_MTD_DEVICES) + return -ENODEV; + + if(SMCParts[index].type != SSFDC_FORMAT) + return -ENXIO; + + pt_smcpart = &SMCParts[index]; + atomic_dec(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); + put_mtd_device(pt_smcpart->mtd); + BLK_DEC_USE_COUNT; + + return(0); +} + + +static void do_ssfdc_request(request_arg_t) +{ + int ret, minor; + partition_t *pt_smcpart; + int index; + do { + + INIT_REQUEST; + + + + minor = MINOR(CURRENT->rq_dev); + index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + pt_smcpart = &SMCParts[index]; + if (pt_smcpart->type == SSFDC_FORMAT) { + ret = 0; + switch (CURRENT->cmd) { + case READ: + ret = ssfdc_read(pt_smcpart, CURRENT->buffer, + CURRENT->sector + ssfdc_hd[minor].start_sect, + CURRENT->current_nr_sectors); + break; + + case WRITE: + ret = ssfdc_write(pt_smcpart, CURRENT->buffer, + CURRENT->sector + ssfdc_hd[minor].start_sect, + CURRENT->current_nr_sectors); + break; + + default: + panic("do_ssfdc_request : unknown block command!\n"); + } + + } else { + ret = 1; + PDEBUG("not ssfdc partition type\n"); + } + + if (!ret) { + CURRENT->sector += CURRENT->current_nr_sectors; + } + + end_request((ret == 0) ? 1 : 0); + } while (1); +} + +static int ssfdc_write(partition_t *pt_smcpart, caddr_t buffer, + u_long sector, u_long nblocks) +{ + int zone, block, offset; + int sectors_written = 0; + int physical; + int * pt_logical; + int * pt_physical; + int new = -1; + int size; + int retlen; + int i; + int sc; + int ptr_done = 0; + unsigned char * ptr = (unsigned char *)buffer; + unsigned char ecc_code[6], ecc_calc[6]; + int do_erase; +// unsigned char smc_status; + + + + offset = (sector % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + PDEBUG("write device %d, sector %d, count %d\n", + pt_smcpart->count, sector, nblocks); +/* + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + return -ENXIO; + } + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + while(sectors_written < nblocks) { + + new = -1; + do_erase = FALSE; + + zone = (sector + sectors_written) / SECTORS_PER_ZONE; + block = ((sector + sectors_written) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; + offset = ((sector + sectors_written) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + pt_logical = pt_smcpart->zone + (zone * 2048); + pt_physical = pt_smcpart->zone + (zone * 2048) + 1024; + + size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_written)) ? + (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_written); + size *= SECTOR_SIZE; + + PDEBUG("write device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", + pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_written, size, (unsigned int)ptr); + + physical = ssfdc_physical(pt_smcpart, zone, block); + + + if(physical >= 0) { + if(ssfdc_cached != physical) { + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to read physical\n"); + return -ENXIO; + } + + for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); + return -ENXIO; + } + + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + } + + } + + for(sc=0; sc sc) { + PDEBUG("offset %d, sector %d\n", offset, sc); + continue; + } + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); + return -ENXIO; + } + + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + + /* find out if the block is being used */ + + + if(ssfdc_sector_blank(pt_smcpart, sc)) { + PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, blank, physical 0x%x\n", + zone, block, sc, sector, physical); + memcpy(&ssfdc_scratch[(sc * SECTOR_SIZE)], ptr+ptr_done, SECTOR_SIZE); + nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done), &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done + 256), &ecc_calc[3]); + for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; + i = (block << 1) | 0x1000; + i |= ssfdc_parity(block); + ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; + ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; + + pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), SECTOR_SIZE, &retlen, + ptr + ptr_done, ssfdc_buffer, &ssfdc_ffoob_info); + if(retlen != SECTOR_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to write physical 0x%x, sector 0x%x, blank, retlen %d\n" + , physical, sc, retlen); + return -ENXIO; + } + + ptr_done += SECTOR_SIZE; + if(ptr_done >= size) break; + } + else { + new = ssfdc_allocate_new(pt_smcpart, zone); + /* erase the old block */ + *(pt_physical + ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE)) = 0xFFFFFFFF; + + PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_physical, ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE), 0xFFFFFFFF); + do_erase = TRUE; + PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, written, physical 0x%x, new 0x%x\n", + zone, block, sc, sector, physical, new); + break; + } + } + } + else { + ssfdc_cached = 0xFFFFFFFF; + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + new = ssfdc_allocate_new(pt_smcpart, zone); + PDEBUG("ssfdc_write : zone %d, block %d, lbn %d, physical 0x%x, unallocated, new 0x%x\n", + zone, block, sector, physical, new); + } + + + + if(new != -1) { + + + memcpy(&ssfdc_scratch[(offset * SECTOR_SIZE)], ptr, size); + PDEBUG("ssfdc_write : new 0x%x, offset 0x%x, size 0x%x, block 0x%x\n", new, offset, size, block); + for(sc=0; scmtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; + i = (block << 1) | 0x1000; + i |= ssfdc_parity(block); + ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; + ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; + memcpy(&ssfdc_oob_buf[sc * OOB_SIZE], ssfdc_buffer, OOB_SIZE); + } + + + pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, new, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to write block, physical 0x%x, returned 0x%x\n", new, retlen); + return -ENXIO; + } + /* change the mapping table to reflect the new block placement */ + + *(pt_logical + block) = (new % ZONE_SIZE) / SMC_BLOCK_SIZE; + PDEBUG("ssfdc_write : logical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_logical, block, (new % ZONE_SIZE) / SMC_BLOCK_SIZE); + + *(pt_physical + ((new % ZONE_SIZE) / SMC_BLOCK_SIZE)) = block; + PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_physical, ((new % ZONE_SIZE) / SMC_BLOCK_SIZE), block); + + + ssfdc_cached = new; + } + + + ptr += size; + ptr_done = 0; + sectors_written += (size / SECTOR_SIZE); + if(do_erase) ssfdc_erase(pt_smcpart, physical); + + } + + + + + return(0); +} +static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc) { +int b; + + for(b=0; blast_written[zone] + 1; + int * pt_physical; + int physical; + int block; + int retlen; + unsigned char oob[16]; + + + if(new >= BLOCKS_PER_ZONE) new = 0; + + + while (new != pt_smcpart->last_written[zone]) { + block = new % BLOCKS_PER_ZONE; + pt_physical = pt_smcpart->zone + (zone * 2048) + 1024 + block; + physical = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); + + PDEBUG("ssfdc_allocate_new : zone %d, block %d, address 0x%08x, data 0x%08x\n", + zone, block, (unsigned int)pt_physical, *pt_physical); + if(*pt_physical == 0xFFFFFFFF) { + PDEBUG("ssfdc_allocate_new : physical 0x%x = 0x%x\n", (unsigned int)pt_physical, *pt_physical); + memset(oob, 0, OOB_SIZE); + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical, OOB_SIZE, &retlen, oob); + if((oob[5] == 0xFF) && (retlen == OOB_SIZE)) { // If not a bad block + pt_smcpart->last_written[zone] = new; + return((new * SMC_BLOCK_SIZE) + (zone * ZONE_SIZE)); + } + else { + PDEBUG("ssfdc_allocate_new : new 0x%x, physical 0x%x, block status 0x%x, oob length 0x%x\n", new, physical, oob[5], retlen); + } + } + new++; + if(new >= BLOCKS_PER_ZONE) new = 0; + } + + panic("ssfdc_allocate_new : cant find free block\n"); + +} + + + +static int ssfdc_read(partition_t *pt_smcpart, caddr_t buffer, + u_long sector, u_long nblocks) +{ + int zone, block, offset; + int sectors_read = 0; + int physical; + int size; + int retlen; + int i; + int sc; + unsigned char * ptr = (unsigned char *)buffer; + unsigned char ecc_code[6], ecc_calc[6]; +/* + unsigned char smc_status; + + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + return -ENXIO; + } + + + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + while(sectors_read < nblocks) { + + zone = (sector + sectors_read) / SECTORS_PER_ZONE; + block = ((sector + sectors_read) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; + offset = ((sector + sectors_read) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + + if(offset) { + size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_read)) ? + (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_read); + } + else { + size = (SECTORS_PER_BLOCK < (nblocks - sectors_read)) ? SECTORS_PER_BLOCK : nblocks - sectors_read; + } + size *= SECTOR_SIZE; + + PDEBUG("ssfdc_read : device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", + pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_read, size, (unsigned int)ptr); + + + physical = ssfdc_physical(pt_smcpart, zone, block); + if(physical >= 0) { + if(ssfdc_cached != physical) { + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_read : failed to read physical\n"); + return -ENXIO; + } + for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_read : failed to read physical oob\n"); + return -ENXIO; + } + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<3; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + for(i=3; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + } + + /* Get the ecc bytes and check that they are ok */ + + + } + ssfdc_cached = physical; + + + } + else { + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + ssfdc_cached = 0xFFFFFFFF; + } + + + memcpy(ptr, &ssfdc_scratch[(offset * SECTOR_SIZE)], size); + ptr += size; + sectors_read += (size / SECTOR_SIZE); + } + + + + return(0); +} + +static void ssfdc_erase_callback(struct erase_info *erase) { + + PDEBUG("ssfdc_erase_callback : wake erase\n"); + up(&ssfdc_semaphore); + PDEBUG("ssfdc_erase_callback : woken erase\n"); +} + +static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset) +{ + int ret = 0; + struct erase_info *erase; + unsigned char * junk; + unsigned char * oob; + int retlen; + int b, sc; + + + PDEBUG("ssfdc_erase : offset 0x%08x\n", offset); + + erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL); + junk=kmalloc(pt_smcpart->mtd->erasesize + 16, GFP_KERNEL); + oob = junk + pt_smcpart->mtd->erasesize; + + if (!erase) + return -ENOMEM; + if (!junk) + return -ENOMEM; + + erase->addr = offset; + erase->len = pt_smcpart->mtd->erasesize; + erase->callback = ssfdc_erase_callback; + ret = pt_smcpart->mtd->erase(pt_smcpart->mtd, erase); + if(ret) { + printk(KERN_WARNING "ssfdc_erase : failed status 0x%x\n", ret); + goto end; + + } + + down(&ssfdc_semaphore); + + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, offset, SMC_BLOCK_SIZE, &retlen, junk, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read returned length %d\n", offset, retlen); + goto end; + } + + + for(sc=0; sc < SECTORS_PER_BLOCK; sc++) { + for(b=0; bmtd->read_oob(pt_smcpart->mtd, offset + (sc * SECTOR_SIZE), OOB_SIZE, &retlen, oob); + if(retlen != OOB_SIZE) { + printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read oob returned length %d\n", offset, retlen); + goto end; + } + for(b=0; bsmc_status); +*/ + memset(ssfdc_ffoob_buf, 0xFF, sizeof(ssfdc_ffoob_buf)); + + for (i = 0; i < MAX_DEVICES*MAX_PARTITIONS; i++) { + ssfdc_hd[i].nr_sects = 0; + ssfdc_hd[i].start_sect = 0; + ssfdc_blocksizes[i] = 4096; + } + blksize_size[SSFDC_MAJOR] = ssfdc_blocksizes; + ssfdc_gendisk.major = SSFDC_MAJOR; + + + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + + result = register_blkdev(ssfdc_major, "ssfdc", &ssfdc_fops); + if(result != 0) { + printk(KERN_WARNING "ssfdc : failed to get a major number\n"); + return(result); + } +// if(ssfdc_major == 0) ssfdc_major = result; + + blk_init_queue(BLK_DEFAULT_QUEUE(ssfdc_major), &do_ssfdc_request); + + add_gendisk(&ssfdc_gendisk); + + + + register_mtd_user(&ssfdc_notifier); + + + init_MUTEX_LOCKED(&ssfdc_semaphore); + + + + return 0; +} + +static void __exit cleanup_ssfdc(void) +{ + int i; + + for(i=0; i"); +MODULE_DESCRIPTION("SSFDC translation layer support for MTD"); + + + + diff -Nur linux-2.6.9-intc1/drivers/net/irda/pxa_ir.c linux-2.6.9-intc1-mih1/drivers/net/irda/pxa_ir.c --- linux-2.6.9-intc1/drivers/net/irda/pxa_ir.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/net/irda/pxa_ir.c 2005-02-26 15:04:03.000000000 -0500 @@ -39,10 +39,9 @@ #include #include #include -#include - #ifdef CONFIG_MACH_MAINSTONE +#include /* If you use Bulverde, you can unremark following definition to enable LED for debug purpose */ /* #define DEBUG_LED_ON */ #define DEBUG_LED_ON @@ -157,6 +156,7 @@ } if (machine_is_mainstone()) { +#ifdef CONFIG_MACH_MAINSTONE if (irda_mode == BOARD_IRDA_SIR) { /* select SIR on Mainstone */ @@ -166,6 +166,7 @@ /* select FIR on Mainstone */ MST_MSCWR1 |= (1 << 4); } +#endif } } @@ -790,8 +791,9 @@ CKEN &= ~CKEN13_FICP; board_irda_select(BOARD_IRDA_SIR); if (machine_is_mainstone()) +#ifdef CONFIG_MACH_MAINSTONE MST_MSCWR1 &= ~(3 << 4); - +#endif /* configure GPIO46/47 */ pxa_gpio_mode(GPIO46_STRXD_MD); pxa_gpio_mode(GPIO47_STTXD_MD); diff -Nur linux-2.6.9-intc1/drivers/net/pcmcia/pcnet_cs.c linux-2.6.9-intc1-mih1/drivers/net/pcmcia/pcnet_cs.c --- linux-2.6.9-intc1/drivers/net/pcmcia/pcnet_cs.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/net/pcmcia/pcnet_cs.c 2005-02-21 15:02:29.000000000 -0500 @@ -818,6 +818,7 @@ link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: + disable_irq(dev->irq); if (link->state & DEV_CONFIG) { if (link->open) netif_device_detach(dev); @@ -828,6 +829,7 @@ link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: + enable_irq(dev->irq); if (link->state & DEV_CONFIG) { pcmcia_request_configuration(link->handle, &link->conf); if (link->open) { diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_80211.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_80211.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_80211.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_80211.h 2003-11-11 00:47:22.000000000 -0500 @@ -0,0 +1,107 @@ +#ifndef HOSTAP_80211_H +#define HOSTAP_80211_H + +struct hostap_ieee80211_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; +} __attribute__ ((packed)); + + +struct hostap_ieee80211_mgmt { + u16 frame_control; + u16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + u16 seq_ctrl; + union { + struct { + u16 auth_alg; + u16 auth_transaction; + u16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + u16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + u16 capab_info; + u16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_req; + struct { + u16 capab_info; + u16 status_code; + u16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + u16 capab_info; + u16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + u16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + } __attribute__ ((packed)) probe_req; + struct { + u8 timestamp[8]; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon, probe_resp; + } u; +} __attribute__ ((packed)); + + +#define IEEE80211_MGMT_HDR_LEN 24 +#define IEEE80211_DATA_HDR3_LEN 24 +#define IEEE80211_DATA_HDR4_LEN 30 + + +struct hostap_80211_rx_status { + u32 mac_time; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ +}; + + +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); + + +/* prism2_rx_80211 'type' argument */ +enum { + PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC, + PRISM2_RX_NULLFUNC_ACK +}; + +int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int type); +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); +void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); + +void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); +int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev); +int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev); +struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, + struct prism2_crypt_data *crypt); +int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev); + +#endif /* HOSTAP_80211_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_80211_rx.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_80211_rx.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_80211_rx.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_80211_rx.c 2004-11-22 01:03:05.000000000 -0500 @@ -0,0 +1,1082 @@ +#include + +#include "hostap_80211.h" +#include "hostap.h" + +void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d " + "jiffies=%ld\n", + name, rx_stats->signal, rx_stats->noise, rx_stats->rate, + skb->len, jiffies); + + if (skb->len < 2) + return; + + fc = le16_to_cpu(hdr->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + printk("\n"); + return; + } + + printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), + le16_to_cpu(hdr->seq_ctrl)); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR, + MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3)); + if (skb->len >= 30) + printk(" A4=" MACSTR, MAC2STR(hdr->addr4)); + printk("\n"); +} + + +/* Send RX frame to netif with 802.11 (and possible prism) header. + * Called from hardware or software IRQ context. */ +int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int type) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int hdrlen, phdrlen, head_need, tail_need; + u16 fc; + int prism_header, ret; + struct hostap_ieee80211_hdr *hdr; + + dev->last_rx = jiffies; + + if (dev->type == ARPHRD_IEEE80211_PRISM) { + if (local->monitor_type == PRISM2_MONITOR_PRISM) { + prism_header = 1; + phdrlen = sizeof(struct linux_wlan_ng_prism_hdr); + } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */ + prism_header = 2; + phdrlen = sizeof(struct linux_wlan_ng_cap_hdr); + } + } else { + prism_header = 0; + phdrlen = 0; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (type == PRISM2_RX_MGMT && (fc & WLAN_FC_PVER)) { + printk(KERN_DEBUG "%s: dropped management frame with header " + "version %d\n", dev->name, fc & WLAN_FC_PVER); + dev_kfree_skb_any(skb); + return 0; + } + + hdrlen = hostap_80211_get_hdrlen(fc); + + /* check if there is enough room for extra data; if not, expand skb + * buffer to be large enough for the changes */ + head_need = phdrlen; + tail_need = 0; +#ifdef PRISM2_ADD_BOGUS_CRC + tail_need += 4; +#endif /* PRISM2_ADD_BOGUS_CRC */ + + head_need -= skb_headroom(skb); + tail_need -= skb_tailroom(skb); + + if (head_need > 0 || tail_need > 0) { + if (pskb_expand_head(skb, head_need > 0 ? head_need : 0, + tail_need > 0 ? tail_need : 0, + GFP_ATOMIC)) { + printk(KERN_DEBUG "%s: prism2_rx_80211 failed to " + "reallocate skb buffer\n", dev->name); + dev_kfree_skb_any(skb); + return 0; + } + } + + /* We now have an skb with enough head and tail room, so just insert + * the extra data */ + +#ifdef PRISM2_ADD_BOGUS_CRC + memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */ +#endif /* PRISM2_ADD_BOGUS_CRC */ + + if (prism_header == 1) { + struct linux_wlan_ng_prism_hdr *hdr; + hdr = (struct linux_wlan_ng_prism_hdr *) + skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->msgcode = LWNG_CAP_DID_BASE; + hdr->msglen = sizeof(*hdr); + memcpy(hdr->devname, dev->name, sizeof(hdr->devname)); +#define LWNG_SETVAL(f,i,s,l,d) \ +hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \ +hdr->f.status = s; hdr->f.len = l; hdr->f.data = d + LWNG_SETVAL(hosttime, 1, 0, 4, jiffies); + LWNG_SETVAL(mactime, 2, 0, 0, rx_stats->mac_time); + LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0); + LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0); + LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0); + LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal); + LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise); + LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5); + LWNG_SETVAL(istx, 9, 0, 4, 0); + LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen); +#undef LWNG_SETVAL + } else if (prism_header == 2) { + struct linux_wlan_ng_cap_hdr *hdr; + hdr = (struct linux_wlan_ng_cap_hdr *) + skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->version = htonl(LWNG_CAPHDR_VERSION); + hdr->length = htonl(phdrlen); + hdr->mactime = __cpu_to_be64(rx_stats->mac_time); + hdr->hosttime = __cpu_to_be64(jiffies); + hdr->phytype = htonl(4); /* dss_dot11_b */ + hdr->channel = htonl(local->channel); + hdr->datarate = htonl(rx_stats->rate); + hdr->antenna = htonl(0); /* unknown */ + hdr->priority = htonl(0); /* unknown */ + hdr->ssi_type = htonl(3); /* raw */ + hdr->ssi_signal = htonl(rx_stats->signal); + hdr->ssi_noise = htonl(rx_stats->noise); + hdr->preamble = htonl(0); /* unknown */ + hdr->encoding = htonl(1); /* cck */ + } + + ret = skb->len - phdrlen; + skb->dev = dev; + skb->mac.raw = skb->data; + skb_pull(skb, hdrlen); + if (prism_header) + skb_pull(skb, phdrlen); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void monitor_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device_stats *stats; + int len; + + len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR); + stats = hostap_get_stats(dev); + stats->rx_packets++; + stats->rx_bytes += len; +} + + +/* Called only as a tasklet (software IRQ) */ +static struct prism2_frag_entry * +prism2_frag_cache_find(local_info_t *local, unsigned int seq, + unsigned int frag, u8 *src, u8 *dst) +{ + struct prism2_frag_entry *entry; + int i; + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + entry = &local->frag_cache[i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + printk(KERN_DEBUG "%s: expiring fragment cache entry " + "seq=%u last_frag=%u\n", + local->dev->name, entry->seq, entry->last_frag); + dev_kfree_skb(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + return entry; + } + + return NULL; +} + + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff * +prism2_frag_cache_get(local_info_t *local, struct hostap_ieee80211_hdr *hdr) +{ + struct sk_buff *skb = NULL; + u16 sc; + unsigned int frag, seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctrl); + frag = WLAN_GET_SEQ_FRAG(sc); + seq = WLAN_GET_SEQ_SEQ(sc); + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(local->dev->mtu + + sizeof(struct hostap_ieee80211_hdr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + ETH_ALEN /* WDS */); + if (skb == NULL) + return NULL; + + entry = &local->frag_cache[local->frag_next_idx]; + local->frag_next_idx++; + if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN) + local->frag_next_idx = 0; + + if (entry->skb != NULL) + dev_kfree_skb(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); + memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received */ + entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + + +/* Called only as a tasklet (software IRQ) */ +static int prism2_frag_cache_invalidate(local_info_t *local, + struct hostap_ieee80211_hdr *hdr) +{ + u16 sc; + unsigned int seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctrl); + seq = WLAN_GET_SEQ_SEQ(sc); + + entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1); + + if (entry == NULL) { + printk(KERN_DEBUG "%s: could not invalidate fragment cache " + "entry (seq=%u)\n", + local->dev->name, seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + + +static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + struct list_head *ptr; + struct hostap_bss_info *bss; + + list_for_each(ptr, &local->bss_list) { + bss = list_entry(ptr, struct hostap_bss_info, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && + (ssid == NULL || + (ssid_len == bss->ssid_len && + memcmp(ssid, bss->ssid, ssid_len) == 0))) { + list_move(&bss->list, &local->bss_list); + return bss; + } + } + + return NULL; +} + + +static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + struct hostap_bss_info *bss; + + if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) { + bss = list_entry(local->bss_list.prev, + struct hostap_bss_info, list); + list_del(&bss->list); + local->num_bss_info--; + } else { + bss = (struct hostap_bss_info *) + kmalloc(sizeof(*bss), GFP_ATOMIC); + if (bss == NULL) + return NULL; + } + + memset(bss, 0, sizeof(*bss)); + memcpy(bss->bssid, bssid, ETH_ALEN); + memcpy(bss->ssid, ssid, ssid_len); + bss->ssid_len = ssid_len; + local->num_bss_info++; + list_add(&bss->list, &local->bss_list); + return bss; +} + + +static void __hostap_expire_bss(local_info_t *local) +{ + struct hostap_bss_info *bss; + + while (local->num_bss_info > 0) { + bss = list_entry(local->bss_list.prev, + struct hostap_bss_info, list); + if (!time_after(jiffies, bss->last_update + 60 * HZ)) + break; + + list_del(&bss->list); + local->num_bss_info--; + kfree(bss); + } +} + + +/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so + * the same routine can be used to parse both of them. */ +static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb, + int stype) +{ + struct hostap_ieee80211_mgmt *mgmt; + int left, chan = 0; + u8 *pos; + u8 *ssid = NULL, *wpa = NULL, *rsn = NULL; + size_t ssid_len = 0, wpa_len = 0, rsn_len = 0; + struct hostap_bss_info *bss; + + if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon)) + return; + + mgmt = (struct hostap_ieee80211_mgmt *) skb->data; + pos = mgmt->u.beacon.variable; + left = skb->len - (pos - skb->data); + + while (left >= 2) { + if (2 + pos[1] > left) + return; /* parse failed */ + switch (*pos) { + case WLAN_EID_SSID: + ssid = pos + 2; + ssid_len = pos[1]; + break; + case WLAN_EID_GENERIC: + if (pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && + pos[4] == 0xf2 && pos[5] == 1) { + wpa = pos; + wpa_len = pos[1] + 2; + } + break; + case WLAN_EID_RSN: + rsn = pos; + rsn_len = pos[1] + 2; + break; + case WLAN_EID_DS_PARAMS: + if (pos[1] >= 1) + chan = pos[2]; + break; + } + left -= 2 + pos[1]; + pos += 2 + pos[1]; + } + + if (wpa_len > MAX_WPA_IE_LEN) + wpa_len = MAX_WPA_IE_LEN; + if (rsn_len > MAX_WPA_IE_LEN) + rsn_len = MAX_WPA_IE_LEN; + if (ssid_len > sizeof(bss->ssid)) + ssid_len = sizeof(bss->ssid); + + spin_lock(&local->lock); + bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len); + if (bss == NULL) + bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len); + if (bss) { + bss->last_update = jiffies; + bss->count++; + bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info); + if (wpa) { + memcpy(bss->wpa_ie, wpa, wpa_len); + bss->wpa_ie_len = wpa_len; + } else + bss->wpa_ie_len = 0; + if (rsn) { + memcpy(bss->rsn_ie, rsn, rsn_len); + bss->rsn_ie_len = rsn_len; + } else + bss->rsn_ie_len = 0; + bss->chan = chan; + } + __hostap_expire_bss(local); + spin_unlock(&local->lock); +} + + +static inline int +hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, u16 type, + u16 stype) +{ + if (local->iw_mode == IW_MODE_MASTER) { + hostap_update_sta_ps(local, (struct hostap_ieee80211_hdr *) + skb->data); + } + + if (local->hostapd && type == WLAN_FC_TYPE_MGMT) { + if (stype == WLAN_FC_STYPE_BEACON && + local->iw_mode == IW_MODE_MASTER) { + struct sk_buff *skb2; + /* Process beacon frames also in kernel driver to + * update STA(AP) table statistics */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + hostap_rx(skb2->dev, skb2, rx_stats); + } + + /* send management frames to the user space daemon for + * processing */ + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + if (local->apdev == NULL) + return -1; + prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT); + return 0; + } + + if (local->iw_mode == IW_MODE_MASTER) { + if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type, stype); + return -1; + } + + hostap_rx(skb->dev, skb, rx_stats); + return 0; + } else if (type == WLAN_FC_TYPE_MGMT && + (stype == WLAN_FC_STYPE_BEACON || + stype == WLAN_FC_STYPE_PROBE_RESP)) { + hostap_rx_sta_beacon(local, skb, stype); + return -1; + } else if (type == WLAN_FC_TYPE_MGMT && + (stype == WLAN_FC_STYPE_ASSOC_RESP || + stype == WLAN_FC_STYPE_REASSOC_RESP)) { + /* Ignore (Re)AssocResp silently since these are not currently + * needed but are still received when WPA/RSN mode is enabled. + */ + return -1; + } else { + printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled" + " management frame in non-Host AP mode (type=%d:%d)\n", + skb->dev->name, type, stype); + return -1; + } +} + + +/* Called only as a tasklet (software IRQ) */ +static inline struct net_device *prism2_rx_get_wds(local_info_t *local, + u8 *addr) +{ + struct hostap_interface *iface = NULL; + struct list_head *ptr; + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_WDS && + memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0) + break; + iface = NULL; + } + read_unlock_bh(&local->iface_lock); + + return iface ? iface->dev : NULL; +} + + +static inline int +hostap_rx_frame_wds(local_info_t *local, struct hostap_ieee80211_hdr *hdr, + u16 fc, struct net_device **wds) +{ + /* FIX: is this really supposed to accept WDS frames only in Master + * mode? What about Repeater or Managed with WDS frames? */ + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != + (WLAN_FC_TODS | WLAN_FC_FROMDS) && + (local->iw_mode != IW_MODE_MASTER || !(fc & WLAN_FC_TODS))) + return 0; /* not a WDS frame */ + + /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS) + * or own non-standard frame with 4th address after payload */ + if (memcmp(hdr->addr1, local->dev->dev_addr, ETH_ALEN) != 0 && + (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff || + hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || + hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { + /* RA (or BSSID) is not ours - drop */ + PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with " + "not own or broadcast %s=" MACSTR "\n", + local->dev->name, fc & WLAN_FC_FROMDS ? "RA" : "BSSID", + MAC2STR(hdr->addr1)); + return -1; + } + + /* check if the frame came from a registered WDS connection */ + *wds = prism2_rx_get_wds(local, hdr->addr2); + if (*wds == NULL && fc & WLAN_FC_FROMDS && + (local->iw_mode != IW_MODE_INFRA || + !(local->wds_type & HOSTAP_WDS_AP_CLIENT) || + memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) { + /* require that WDS link has been registered with TA or the + * frame is from current AP when using 'AP client mode' */ + PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame " + "from unknown TA=" MACSTR "\n", + local->dev->name, MAC2STR(hdr->addr2)); + if (local->ap && local->ap->autom_ap_wds) + hostap_wds_link_oper(local, hdr->addr2, WDS_ADD); + return -1; + } + + if (*wds && !(fc & WLAN_FC_FROMDS) && local->ap && + hostap_is_sta_assoc(local->ap, hdr->addr2)) { + /* STA is actually associated with us even though it has a + * registered WDS link. Assume it is in 'AP client' mode. + * Since this is a 3-addr frame, assume it is not (bogus) WDS + * frame and process it like any normal ToDS frame from + * associated STA. */ + *wds = NULL; + } + + return 0; +} + + +static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb) +{ + struct net_device *dev = local->dev; + u16 fc, ethertype; + struct hostap_ieee80211_hdr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* check that the frame is unicast frame to us */ + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_FROMDS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ + pos = skb->data + 24; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + + +/* Called only as a tasklet (software IRQ) */ +static inline int +hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb, + struct prism2_crypt_data *crypt) +{ + struct hostap_ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + + if (local->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "received packet from " MACSTR "\n", + local->dev->name, MAC2STR(hdr->addr2)); + } + return -1; + } + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: decryption failed (SA=" MACSTR + ") res=%d\n", + local->dev->name, MAC2STR(hdr->addr2), res); + local->comm_tallies.rx_discards_wep_undecryptable++; + return -1; + } + + return res; +} + + +/* Called only as a tasklet (software IRQ) */ +static inline int +hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb, + int keyidx, struct prism2_crypt_data *crypt) +{ + struct hostap_ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" + " (SA=" MACSTR " keyidx=%d)\n", + local->dev->name, MAC2STR(hdr->addr2), keyidx); + return -1; + } + + return 0; +} + + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). */ +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hostap_ieee80211_hdr *hdr; + size_t hdrlen; + u16 fc, type, stype, sc; + struct net_device *wds = NULL; + struct net_device_stats *stats; + unsigned int frag; + u8 *payload; + struct sk_buff *skb2 = NULL; + u16 ethertype; + int frame_authorized = 0; + int from_assoc_ap = 0; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct prism2_crypt_data *crypt = NULL; + void *sta = NULL; + int keyidx = 0; + + iface->stats.rx_packets++; + iface->stats.rx_bytes += skb->len; + + /* dev is the master radio device; change this to be the default + * virtual interface (this may be changed to WDS device below) */ + dev = local->ddev; + iface = dev->priv; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + stats = hostap_get_stats(dev); + + if (skb->len < 10) + goto rx_dropped; + + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(hdr->seq_ctrl); + frag = WLAN_GET_SEQ_FRAG(sc); + hdrlen = hostap_80211_get_hdrlen(fc); + +#if WIRELESS_EXT > 15 + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (iface->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = rx_stats->signal; + wstats.noise = rx_stats->noise; + wstats.updated = 6; /* No qual value */ + /* Update spy records */ + wireless_spy_update(dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_EXT > 15 */ + hostap_update_rx_stats(local->ap, hdr, rx_stats); + + if (local->iw_mode == IW_MODE_MONITOR) { + monitor_rx(dev, skb, rx_stats); + return; + } + + if (local->host_decrypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = local->crypt[idx]; + sta = NULL; + + /* Use station specific key to override default keys if the + * receiver address is a unicast address ("individual RA"). If + * bcrx_sta_key parameter is set, station specific key is used + * even with broad/multicast targets (this is against IEEE + * 802.11, but makes it easier to use different keys with + * stations that do not support WEP key mapping). */ + + if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) + (void) hostap_handle_sta_crypto(local, hdr, &crypt, + &sta); + + /* allow NULL decrypt to indicate an station specific override + * for default encryption */ + if (crypt && (crypt->ops == NULL || + crypt->ops->decrypt_mpdu == NULL)) + crypt = NULL; + + if (!crypt && (fc & WLAN_FC_ISWEP)) { +#if 0 + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. */ + printk(KERN_DEBUG "%s: WEP decryption failed (not set)" + " (SA=" MACSTR ")\n", + local->dev->name, MAC2STR(hdr->addr2)); +#endif + local->comm_tallies.rx_discards_wep_undecryptable++; + goto rx_dropped; + } + } + + if (type != WLAN_FC_TYPE_DATA) { + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && + fc & WLAN_FC_ISWEP && local->host_decrypt && + (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) + { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from " MACSTR "\n", dev->name, + MAC2STR(hdr->addr2)); + /* TODO: could inform hostapd about this so that it + * could send auth failure report */ + goto rx_dropped; + } + + if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } + + /* Data frame - extract src/dst addresses */ + if (skb->len < IEEE80211_DATA_HDR3_LEN) + goto rx_dropped; + + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_FROMDS: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + break; + case WLAN_FC_TODS: + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + case WLAN_FC_FROMDS | WLAN_FC_TODS: + if (skb->len < IEEE80211_DATA_HDR4_LEN) + goto rx_dropped; + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + break; + case 0: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + } + + if (hostap_rx_frame_wds(local, hdr, fc, &wds)) + goto rx_dropped; + if (wds) { + skb->dev = dev = wds; + stats = hostap_get_stats(dev); + } + + if (local->iw_mode == IW_MODE_MASTER && !wds && + (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_FROMDS && + local->stadev && + memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) { + /* Frame from BSSID of the AP for which we are a client */ + skb->dev = dev = local->stadev; + stats = hostap_get_stats(dev); + from_assoc_ap = 1; + } + + dev->last_rx = jiffies; + + if ((local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT) && + !from_assoc_ap) { + switch (hostap_handle_sta_rx(local, dev, skb, rx_stats, + wds != NULL)) { + case AP_RX_CONTINUE_NOT_AUTHORIZED: + frame_authorized = 0; + break; + case AP_RX_CONTINUE: + frame_authorized = 1; + break; + case AP_RX_DROP: + goto rx_dropped; + case AP_RX_EXIT: + goto rx_exit; + } + } + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + if (stype != WLAN_FC_STYPE_DATA && + stype != WLAN_FC_STYPE_DATA_CFACK && + stype != WLAN_FC_STYPE_DATA_CFPOLL && + stype != WLAN_FC_STYPE_DATA_CFACKPOLL) { + if (stype != WLAN_FC_STYPE_NULLFUNC) + printk(KERN_DEBUG "%s: RX: dropped data frame " + "with no data (type=0x%02x, subtype=0x%02x)\n", + dev->name, type, stype); + goto rx_dropped; + } + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ + + if (local->host_decrypt && (fc & WLAN_FC_ISWEP) && + (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) + goto rx_dropped; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + + if (local->host_decrypt && (fc & WLAN_FC_ISWEP) && + (frag != 0 || (fc & WLAN_FC_MOREFRAG))) { + int flen; + struct sk_buff *frag_skb = + prism2_frag_cache_get(local, hdr); + if (!frag_skb) { + printk(KERN_DEBUG "%s: Rx cannot get skb from " + "fragment cache (morefrag=%d seq=%u frag=%u)\n", + dev->name, (fc & WLAN_FC_MOREFRAG) != 0, + WLAN_GET_SEQ_SEQ(sc), frag); + goto rx_dropped; + } + + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + printk(KERN_WARNING "%s: host decrypted and " + "reassembled frame did not fit skb\n", + dev->name); + prism2_frag_cache_invalidate(local, hdr); + goto rx_dropped; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data, flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, + flen); + } + dev_kfree_skb(skb); + skb = NULL; + + if (fc & WLAN_FC_MOREFRAG) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received */ + goto rx_exit; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache */ + skb = frag_skb; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + prism2_frag_cache_invalidate(local, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ + + if (local->host_decrypt && (fc & WLAN_FC_ISWEP) && + hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (crypt && !(fc & WLAN_FC_ISWEP) && !local->open_wep) { + if (local->ieee_802_1x && + hostap_is_eapol_frame(local, skb)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", local->dev->name); + } else { + printk(KERN_DEBUG "%s: encryption configured, but RX " + "frame not encrypted (SA=" MACSTR ")\n", + local->dev->name, MAC2STR(hdr->addr2)); + goto rx_dropped; + } + } + + if (local->drop_unencrypted && !(fc & WLAN_FC_ISWEP) && + !hostap_is_eapol_frame(local, skb)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted RX data " + "frame from " MACSTR " (drop_unencrypted=1)\n", + dev->name, MAC2STR(hdr->addr2)); + } + goto rx_dropped; + } + + /* skb: hdr + (possible reassembled) full plaintext payload */ + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + + /* If IEEE 802.1X is used, check whether the port is authorized to send + * the received frame. */ + if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) { + if (ethertype == ETH_P_PAE) { + PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n", + dev->name); + if (local->hostapd && local->apdev) { + /* Send IEEE 802.1X frames to the user + * space daemon for processing */ + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_MGMT); + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", + dev->name, ethertype); + goto rx_dropped; + } + } + + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, rfc1042_header, 6) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, bridge_tunnel_header, 6) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + 6); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + u16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + + if (wds && ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS) && + skb->len >= ETH_HLEN + ETH_ALEN) { + /* Non-standard frame: get addr4 from its bogus location after + * the payload */ + memcpy(skb->data + ETH_ALEN, + skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_trim(skb, skb->len - ETH_ALEN); + } + + stats->rx_packets++; + stats->rx_bytes += skb->len; + + if (local->iw_mode == IW_MODE_MASTER && !wds && + local->ap->bridge_packets) { + if (dst[0] & 0x01) { + /* copy multicast frame both to the higher layers and + * to the wireless media */ + local->ap->bridged_multicast++; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + printk(KERN_DEBUG "%s: skb_clone failed for " + "multicast frame\n", dev->name); + } else if (hostap_is_sta_authorized(local->ap, dst)) { + /* send frame directly to the associated STA using + * wireless media and not passing to higher layers */ + local->ap->bridged_unicast++; + skb2 = skb; + skb = NULL; + } + } + + if (skb2 != NULL) { + /* send to wireless media */ + skb2->protocol = __constant_htons(ETH_P_802_3); + skb2->mac.raw = skb2->nh.raw = skb2->data; + /* skb2->nh.raw = skb2->data + ETH_HLEN; */ + skb2->dev = dev; + dev_queue_xmit(skb2); + } + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = dev; + netif_rx(skb); + } + + rx_exit: + if (sta) + hostap_handle_sta_release(sta); + return; + + rx_dropped: + dev_kfree_skb(skb); + + stats->rx_dropped++; + goto rx_exit; +} + + +EXPORT_SYMBOL(hostap_80211_rx); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_80211_tx.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_80211_tx.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_80211_tx.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_80211_tx.c 2004-07-05 19:45:01.000000000 -0400 @@ -0,0 +1,510 @@ +void hostap_dump_tx_80211(const char *name, struct sk_buff *skb) +{ + struct hostap_ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n", + name, skb->len, jiffies); + + if (skb->len < 2) + return; + + fc = le16_to_cpu(hdr->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + printk("\n"); + return; + } + + printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), + le16_to_cpu(hdr->seq_ctrl)); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR, + MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3)); + if (skb->len >= 30) + printk(" A4=" MACSTR, MAC2STR(hdr->addr4)); + printk("\n"); +} + + +/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta) + * Convert Ethernet header into a suitable IEEE 802.11 header depending on + * device configuration. */ +int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int need_headroom, need_tailroom = 0; + struct hostap_ieee80211_hdr hdr; + u16 fc, ethertype = 0; + enum { + WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME + } use_wds = WDS_NO; + u8 *encaps_data; + int hdr_len, encaps_len, skip_header_bytes; + int to_assoc_ap = 0; + struct hostap_skb_tx_data *meta; + + if (skb->len < ETH_HLEN) { + printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + kfree_skb(skb); + return 0; + } + + if (local->ddev != dev) { + use_wds = (local->iw_mode == IW_MODE_MASTER && + !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ? + WDS_OWN_FRAME : WDS_COMPLIANT_FRAME; + if (dev == local->stadev) { + to_assoc_ap = 1; + use_wds = WDS_NO; + } else if (dev == local->apdev) { + printk(KERN_DEBUG "%s: prism2_tx: trying to use " + "AP device with Ethernet net dev\n", dev->name); + kfree_skb(skb); + return 0; + } + } else { + if (local->iw_mode == IW_MODE_REPEAT) { + printk(KERN_DEBUG "%s: prism2_tx: trying to use " + "non-WDS link in Repeater mode\n", dev->name); + kfree_skb(skb); + return 0; + } else if (local->iw_mode == IW_MODE_INFRA && + (local->wds_type & HOSTAP_WDS_AP_CLIENT) && + memcmp(skb->data + ETH_ALEN, dev->dev_addr, + ETH_ALEN) != 0) { + /* AP client mode: send frames with foreign src addr + * using 4-addr WDS frames */ + use_wds = WDS_COMPLIANT_FRAME; + } + } + + /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload + * ==> + * Prism2 TX frame with 802.11 header: + * txdesc (address order depending on used mode; includes dst_addr and + * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel; + * proto[2], payload {, possible addr4[6]} */ + + ethertype = (skb->data[12] << 8) | skb->data[13]; + + memset(&hdr, 0, sizeof(hdr)); + + /* Length of data after IEEE 802.11 header */ + encaps_data = NULL; + encaps_len = 0; + skip_header_bytes = ETH_HLEN; + if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { + encaps_data = bridge_tunnel_header; + encaps_len = sizeof(bridge_tunnel_header); + skip_header_bytes -= 2; + } else if (ethertype >= 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + skip_header_bytes -= 2; + } + + fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4); + hdr_len = IEEE80211_DATA_HDR3_LEN; + + if (use_wds != WDS_NO) { + /* Note! Prism2 station firmware has problems with sending real + * 802.11 frames with four addresses; until these problems can + * be fixed or worked around, 4-addr frames needed for WDS are + * using incompatible format: FromDS flag is not set and the + * fourth address is added after the frame payload; it is + * assumed, that the receiving station knows how to handle this + * frame format */ + + if (use_wds == WDS_COMPLIANT_FRAME) { + fc |= WLAN_FC_FROMDS | WLAN_FC_TODS; + /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, + * Addr4 = SA */ + memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + hdr_len += ETH_ALEN; + } else { + /* bogus 4-addr format to workaround Prism2 station + * f/w bug */ + fc |= WLAN_FC_TODS; + /* From DS: Addr1 = DA (used as RA), + * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA), + */ + + /* SA from skb->data + ETH_ALEN will be added after + * frame payload; use hdr.addr4 as a temporary buffer + */ + memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + need_tailroom += ETH_ALEN; + } + + /* send broadcast and multicast frames to broadcast RA, if + * configured; otherwise, use unicast RA of the WDS link */ + if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) && + skb->data[0] & 0x01) + memset(&hdr.addr1, 0xff, ETH_ALEN); + else if (iface->type == HOSTAP_INTERFACE_WDS) + memcpy(&hdr.addr1, iface->u.wds.remote_addr, + ETH_ALEN); + else + memcpy(&hdr.addr1, local->bssid, ETH_ALEN); + memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&hdr.addr3, skb->data, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { + fc |= WLAN_FC_FROMDS; + /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ + memcpy(&hdr.addr1, skb->data, ETH_ALEN); + memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { + fc |= WLAN_FC_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ + memcpy(&hdr.addr1, to_assoc_ap ? + local->assoc_ap_addr : local->bssid, ETH_ALEN); + memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(&hdr.addr3, skb->data, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ + memcpy(&hdr.addr1, skb->data, ETH_ALEN); + memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(&hdr.addr3, local->bssid, ETH_ALEN); + } + + hdr.frame_control = cpu_to_le16(fc); + + skb_pull(skb, skip_header_bytes); + need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len; + if (skb_tailroom(skb) < need_tailroom) { + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + iface->stats.tx_dropped++; + return 0; + } + if (pskb_expand_head(skb, need_headroom, need_tailroom, + GFP_ATOMIC)) { + kfree_skb(skb); + iface->stats.tx_dropped++; + return 0; + } + } else if (skb_headroom(skb) < need_headroom) { + struct sk_buff *tmp = skb; + skb = skb_realloc_headroom(skb, need_headroom); + kfree_skb(tmp); + if (skb == NULL) { + iface->stats.tx_dropped++; + return 0; + } + } else { + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + iface->stats.tx_dropped++; + return 0; + } + } + + if (encaps_data) + memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); + memcpy(skb_push(skb, hdr_len), &hdr, hdr_len); + if (use_wds == WDS_OWN_FRAME) { + memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN); + } + + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + + skb->mac.raw = skb->data; + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->wds = use_wds; + meta->ethertype = ethertype; + meta->iface = iface; + + /* Send IEEE 802.11 encapsulated frame using the master radio device */ + skb->dev = local->dev; + dev_queue_xmit(skb); + return 0; +} + + +/* hard_start_xmit function for hostapd wlan#ap interfaces */ +int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hostap_skb_tx_data *meta; + struct hostap_ieee80211_hdr *hdr; + u16 fc; + + if (skb->len < 10) { + printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + kfree_skb(skb); + return 0; + } + + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = iface; + + if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) { + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_DATA) { + u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN + + sizeof(rfc1042_header)]; + meta->ethertype = (pos[0] << 8) | pos[1]; + } + } + + /* Send IEEE 802.11 encapsulated frame using the master radio device */ + skb->dev = local->dev; + dev_queue_xmit(skb); + return 0; +} + + +/* Called only from software IRQ */ +struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, + struct prism2_crypt_data *crypt) +{ + struct hostap_interface *iface = skb->dev->priv; + local_info_t *local = iface->local; + struct hostap_ieee80211_hdr *hdr; + u16 fc; + int hdr_len, res; + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + kfree_skb(skb); + return NULL; + } + + if (local->tkip_countermeasures && + crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) { + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "TX packet to " MACSTR "\n", + local->dev->name, MAC2STR(hdr->addr1)); + } + kfree_skb(skb); + return NULL; + } + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) + return NULL; + + if ((skb_headroom(skb) < crypt->ops->extra_prefix_len || + skb_tailroom(skb) < crypt->ops->extra_postfix_len) && + pskb_expand_head(skb, crypt->ops->extra_prefix_len, + crypt->ops->extra_postfix_len, GFP_ATOMIC)) { + kfree_skb(skb); + return NULL; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + hdr_len = hostap_80211_get_hdrlen(fc); + + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv); + if (res == 0 && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + kfree_skb(skb); + return NULL; + } + + return skb; +} + + +/* hard_start_xmit function for master radio interface wifi#. + * AP processing (TX rate control, power save buffering, etc.). + * Use hardware TX function to send the frame. */ +int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret = 1; + u16 fc; + struct hostap_tx_data tx; + ap_tx_ret tx_ret; + struct hostap_skb_tx_data *meta; + int no_encrypt = 0; + struct hostap_ieee80211_hdr *hdr; + + tx.skb = skb; + tx.sta_ptr = NULL; + + meta = (struct hostap_skb_tx_data *) skb->cb; + if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { + printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " + "expected 0x%08x)\n", + dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); + ret = 0; + iface->stats.tx_dropped++; + goto fail; + } + + if (local->host_encrypt) { + /* Set crypt to default algorithm and key; will be replaced in + * AP code if STA has own alg/key */ + tx.crypt = local->crypt[local->tx_keyidx]; + tx.host_encrypt = 1; + } else { + tx.crypt = NULL; + tx.host_encrypt = 0; + } + + if (skb->len < 24) { + printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + ret = 0; + iface->stats.tx_dropped++; + goto fail; + } + + /* FIX (?): + * Wi-Fi 802.11b test plan suggests that AP should ignore power save + * bit in authentication and (re)association frames and assume tha + * STA remains awake for the response. */ + tx_ret = hostap_handle_sta_tx(local, &tx); + skb = tx.skb; + meta = (struct hostap_skb_tx_data *) skb->cb; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + switch (tx_ret) { + case AP_TX_CONTINUE: + break; + case AP_TX_CONTINUE_NOT_AUTHORIZED: + if (local->ieee_802_1x && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + meta->ethertype != ETH_P_PAE && !meta->wds) { + printk(KERN_DEBUG "%s: dropped frame to unauthorized " + "port (IEEE 802.1X): ethertype=0x%04x\n", + dev->name, meta->ethertype); + hostap_dump_tx_80211(dev->name, skb); + + ret = 0; /* drop packet */ + iface->stats.tx_dropped++; + goto fail; + } + break; + case AP_TX_DROP: + ret = 0; /* drop packet */ + iface->stats.tx_dropped++; + goto fail; + case AP_TX_RETRY: + goto fail; + case AP_TX_BUFFERED: + /* do not free skb here, it will be freed when the + * buffered frame is sent/timed out */ + ret = 0; + goto tx_exit; + } + + /* Request TX callback if protocol version is 2 in 802.11 header; + * this version 2 is a special case used between hostapd and kernel + * driver */ + if (((fc & WLAN_FC_PVER) == BIT(1)) && + local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) { + meta->tx_cb_idx = local->ap->tx_callback_idx; + + /* remove special version from the frame header */ + fc &= ~WLAN_FC_PVER; + hdr->frame_control = cpu_to_le16(fc); + } + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA) { + no_encrypt = 1; + tx.crypt = NULL; + } + + if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt && + !(fc & WLAN_FC_ISWEP)) { + no_encrypt = 1; + PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", dev->name); + tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */ + } + + if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu)) + tx.crypt = NULL; + else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) { + /* Add ISWEP flag both for firmware and host based encryption + */ + fc |= WLAN_FC_ISWEP; + hdr->frame_control = cpu_to_le16(fc); + } else if (local->drop_unencrypted && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + meta->ethertype != ETH_P_PAE) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted TX data " + "frame (drop_unencrypted=1)\n", dev->name); + } + iface->stats.tx_dropped++; + ret = 0; + goto fail; + } + + if (tx.crypt) { + skb = hostap_tx_encrypt(skb, tx.crypt); + if (skb == NULL) { + printk(KERN_DEBUG "%s: TX - encryption failed\n", + dev->name); + ret = 0; + goto fail; + } + meta = (struct hostap_skb_tx_data *) skb->cb; + if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { + printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " + "expected 0x%08x) after hostap_tx_encrypt\n", + dev->name, meta->magic, + HOSTAP_SKB_TX_DATA_MAGIC); + ret = 0; + iface->stats.tx_dropped++; + goto fail; + } + } + + if (local->func->tx == NULL || local->func->tx(skb, dev)) { + ret = 0; + iface->stats.tx_dropped++; + } else { + ret = 0; + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + } + + fail: + if (!ret && skb) + dev_kfree_skb(skb); + tx_exit: + if (tx.sta_ptr) + hostap_handle_sta_release(tx.sta_ptr); + return ret; +} + + +EXPORT_SYMBOL(hostap_dump_tx_80211); +EXPORT_SYMBOL(hostap_tx_encrypt); +EXPORT_SYMBOL(hostap_master_start_xmit); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_ap.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_ap.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_ap.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_ap.c 2005-01-23 21:52:00.000000000 -0500 @@ -0,0 +1,3288 @@ +/* + * Intersil Prism2 driver with Host AP (software access point) support + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * This file is to be included into hostap.c when S/W AP functionality is + * compiled. + * + * AP: FIX: + * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from + * unauthenticated STA, send deauth. frame (8802.11: 5.5) + * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received + * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5) + * - if unicast Class 3 received from unauthenticated STA, send deauth. frame + * (8802.11: 5.5) + */ + +static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL, + DEF_INTS }; +MODULE_PARM(other_ap_policy, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)"); + +static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC, + DEF_INTS }; +MODULE_PARM(ap_max_inactivity, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station " + "inactivity"); + +static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +MODULE_PARM(ap_bridge_packets, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " + "stations"); + +static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS }; +MODULE_PARM(autom_ap_wds, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " + "automatically"); + + +static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta); +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta); +static void handle_add_proc_queue(void *data); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static void handle_wds_oper_queue(void *data); +static void prism2_send_mgmt(struct net_device *dev, + int type, int subtype, char *body, + int body_len, u8 *addr, u16 tx_cb_idx); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int ap_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct ap_data *ap = (struct ap_data *) data; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast); + p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast); + p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ); + p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets); + p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack); + p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds); + p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs); + p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc); + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta) +{ + sta->hnext = ap->sta_hash[STA_HASH(sta->addr)]; + ap->sta_hash[STA_HASH(sta->addr)] = sta; +} + +static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta) +{ + struct sta_info *s; + + s = ap->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { + ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) + != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printk("AP: could not remove STA " MACSTR " from hash table\n", + MAC2STR(sta->addr)); +} + +static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) +{ + if (sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + + if (ap->proc != NULL) { + char name[20]; + sprintf(name, MACSTR, MAC2STR(sta->addr)); + remove_proc_entry(name, ap->proc); + } + + if (sta->crypt) { + sta->crypt->ops->deinit(sta->crypt->priv); + kfree(sta->crypt); + sta->crypt = NULL; + } + + skb_queue_purge(&sta->tx_buf); + + ap->num_sta--; +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->aid > 0) + ap->sta_aid[sta->aid - 1] = NULL; + + if (!sta->ap && sta->u.sta.challenge) + kfree(sta->u.sta.challenge); + del_timer(&sta->timer); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + kfree(sta); +} + + +static void hostap_set_tim(local_info_t *local, int aid, int set) +{ + if (local->func->set_tim) + local->func->set_tim(local->dev, aid, set); +} + + +static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta) +{ +#if WIRELESS_EXT >= 15 + union iwreq_data wrqu; + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL); +#endif /* WIRELESS_EXT >= 15 */ +} + + +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta) +{ +#if WIRELESS_EXT >= 15 + union iwreq_data wrqu; + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL); +#endif /* WIRELESS_EXT >= 15 */ +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void ap_handle_timer(unsigned long data) +{ + struct sta_info *sta = (struct sta_info *) data; + local_info_t *local; + struct ap_data *ap; + unsigned long next_time = 0; + int was_assoc; + + if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) { + PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n"); + return; + } + + local = sta->local; + ap = local->ap; + was_assoc = sta->flags & WLAN_STA_ASSOC; + + if (atomic_read(&sta->users) != 0) + next_time = jiffies + HZ; + else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH)) + next_time = jiffies + ap->max_inactivity; + + if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) { + /* station activity detected; reset timeout state */ + sta->timeout_next = STA_NULLFUNC; + next_time = sta->last_rx + ap->max_inactivity; + } else if (sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + /* STA ACKed data nullfunc frame poll */ + sta->timeout_next = STA_NULLFUNC; + next_time = jiffies + ap->max_inactivity; + } + + if (next_time) { + sta->timer.expires = next_time; + add_timer(&sta->timer); + return; + } + + if (sta->ap) + sta->timeout_next = STA_DEAUTH; + + if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) { + spin_lock(&ap->sta_table_lock); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + spin_unlock(&ap->sta_table_lock); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + } else if (sta->timeout_next == STA_DISASSOC) + sta->flags &= ~WLAN_STA_ASSOC; + + if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + + if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 && + !skb_queue_empty(&sta->tx_buf)) { + hostap_set_tim(local, sta->aid, 0); + sta->flags &= ~WLAN_STA_TIM; + } + + if (sta->ap) { + if (ap->autom_ap_wds) { + PDEBUG(DEBUG_AP, "%s: removing automatic WDS " + "connection to AP " MACSTR "\n", + local->dev->name, MAC2STR(sta->addr)); + hostap_wds_link_oper(local, sta->addr, WDS_DEL); + } + } else if (sta->timeout_next == STA_NULLFUNC) { + /* send data frame to poll STA and check whether this frame + * is ACKed */ + /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but + * it is apparently not retried so TX Exc events are not + * received for it */ + sta->flags |= WLAN_STA_PENDING_POLL; + prism2_send_mgmt(local->dev, WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA, NULL, 0, + sta->addr, ap->tx_callback_poll); + } else { + int deauth = sta->timeout_next == STA_DEAUTH; + u16 resp; + PDEBUG(DEBUG_AP, "%s: sending %s info to STA " MACSTR + "(last=%lu, jiffies=%lu)\n", + local->dev->name, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr), sta->last_rx, jiffies); + + resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID : + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + prism2_send_mgmt(local->dev, WLAN_FC_TYPE_MGMT, + (deauth ? WLAN_FC_STYPE_DEAUTH : + WLAN_FC_STYPE_DISASSOC), + (char *) &resp, 2, sta->addr, 0); + } + + if (sta->timeout_next == STA_DEAUTH) { + if (sta->flags & WLAN_STA_PERM) { + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " would have been " + "removed, but it has 'perm' flag\n", + local->dev->name, MAC2STR(sta->addr)); + } else + ap_free_sta(ap, sta); + return; + } + + if (sta->timeout_next == STA_NULLFUNC) { + sta->timeout_next = STA_DISASSOC; + sta->timer.expires = jiffies + AP_DISASSOC_DELAY; + } else { + sta->timeout_next = STA_DEAUTH; + sta->timer.expires = jiffies + AP_DEAUTH_DELAY; + } + + add_timer(&sta->timer); +} + + +void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, + int resend) +{ + u8 addr[ETH_ALEN]; + u16 resp; + int i; + + PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name); + memset(addr, 0xff, ETH_ALEN); + + resp = __constant_cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + + /* deauth message sent; try to resend it few times; the message is + * broadcast, so it may be delayed until next DTIM; there is not much + * else we can do at this point since the driver is going to be shut + * down */ + for (i = 0; i < 5; i++) { + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH, + (char *) &resp, 2, addr, 0); + + if (!resend || ap->num_sta <= 0) + return; + + mdelay(50); + } +} + + +static int ap_control_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct ap_data *ap = (struct ap_data *) data; + char *policy_txt; + struct list_head *ptr; + struct mac_entry *entry; + + if (off != 0) { + *eof = 1; + return 0; + } + + switch (ap->mac_restrictions.policy) { + case MAC_POLICY_OPEN: + policy_txt = "open"; + break; + case MAC_POLICY_ALLOW: + policy_txt = "allow"; + break; + case MAC_POLICY_DENY: + policy_txt = "deny"; + break; + default: + policy_txt = "unknown"; + break; + }; + p += sprintf(p, "MAC policy: %s\n", policy_txt); + p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries); + p += sprintf(p, "MAC list:\n"); + spin_lock_bh(&ap->mac_restrictions.lock); + for (ptr = ap->mac_restrictions.mac_list.next; + ptr != &ap->mac_restrictions.mac_list; ptr = ptr->next) { + if (p - page > PAGE_SIZE - 80) { + p += sprintf(p, "All entries did not fit one page.\n"); + break; + } + + entry = list_entry(ptr, struct mac_entry, list); + p += sprintf(p, MACSTR "\n", MAC2STR(entry->addr)); + } + spin_unlock_bh(&ap->mac_restrictions.lock); + + return (p - page); +} + + +static int ap_control_add_mac(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct mac_entry *entry; + + entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); + if (entry == NULL) + return -1; + + memcpy(entry->addr, mac, ETH_ALEN); + + spin_lock_bh(&mac_restrictions->lock); + list_add_tail(&entry->list, &mac_restrictions->mac_list); + mac_restrictions->entries++; + spin_unlock_bh(&mac_restrictions->lock); + + return 0; +} + + +static int ap_control_del_mac(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct list_head *ptr; + struct mac_entry *entry; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next; + ptr != &mac_restrictions->mac_list; ptr = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + + if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + list_del(ptr); + kfree(entry); + mac_restrictions->entries--; + spin_unlock_bh(&mac_restrictions->lock); + return 0; + } + } + spin_unlock_bh(&mac_restrictions->lock); + return -1; +} + + +static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct list_head *ptr; + struct mac_entry *entry; + int found = 0; + + if (mac_restrictions->policy == MAC_POLICY_OPEN) + return 0; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next; + ptr != &mac_restrictions->mac_list; ptr = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + + if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + found = 1; + break; + } + } + spin_unlock_bh(&mac_restrictions->lock); + + if (mac_restrictions->policy == MAC_POLICY_ALLOW) + return !found; + else + return found; +} + + +static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions) +{ + struct list_head *ptr, *n; + struct mac_entry *entry; + + if (mac_restrictions->entries == 0) + return; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next, n = ptr->next; + ptr != &mac_restrictions->mac_list; + ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + list_del(ptr); + kfree(entry); + } + mac_restrictions->entries = 0; + spin_unlock_bh(&mac_restrictions->lock); +} + + +static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, + u8 *mac) +{ + struct sta_info *sta; + u16 resp; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, mac); + if (sta) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -EINVAL; + + resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH, + (char *) &resp, 2, sta->addr, 0); + + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(dev, sta); + + ap_free_sta(ap, sta); + + return 0; +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static void ap_control_kickall(struct ap_data *ap) +{ + struct list_head *ptr, *n; + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list; + ptr = n, n = ptr->next) { + sta = list_entry(ptr, struct sta_info, list); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + } + spin_unlock_bh(&ap->sta_table_lock); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +#define PROC_LIMIT (PAGE_SIZE - 80) + +static int prism2_ap_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct ap_data *ap = (struct ap_data *) data; + struct list_head *ptr; + int i; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + p += sprintf(p, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n"); + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + if (!sta->ap) + continue; + + p += sprintf(p, MACSTR " %d %d %d %d '", MAC2STR(sta->addr), + sta->u.ap.channel, sta->last_rx_signal, + sta->last_rx_silence, sta->last_rx_rate); + for (i = 0; i < sta->u.ap.ssid_len; i++) + p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 && + sta->u.ap.ssid[i] < 127) ? + "%c" : "<%02x>"), + sta->u.ap.ssid[i]); + p += sprintf(p, "'"); + if (sta->capability & WLAN_CAPABILITY_ESS) + p += sprintf(p, " [ESS]"); + if (sta->capability & WLAN_CAPABILITY_IBSS) + p += sprintf(p, " [IBSS]"); + if (sta->capability & WLAN_CAPABILITY_PRIVACY) + p += sprintf(p, " [WEP]"); + p += sprintf(p, "\n"); + + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "hostap: ap proc did not fit\n"); + break; + } + } + spin_unlock_bh(&ap->sta_table_lock); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver) +{ + if (!ap) + return; + + if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) { + PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - " + "firmware upgrade recommended\n"); + ap->nullfunc_ack = 1; + } else + ap->nullfunc_ack = 0; + + if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) { + printk(KERN_WARNING "%s: Warning: secondary station firmware " + "version 1.4.2 does not seem to work in Host AP mode\n", + ap->local->dev->name); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + u16 fc; + struct hostap_ieee80211_hdr *hdr; + + if (!ap->local->hostapd || !ap->local->apdev) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* Pass the TX callback frame to the hostapd; use 802.11 header version + * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */ + + fc &= ~WLAN_FC_PVER; + fc |= ok ? BIT(1) : BIT(0); + hdr->frame_control = cpu_to_le16(fc); + + skb->dev = ap->local->apdev; + skb_pull(skb, hostap_80211_get_hdrlen(fc)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct net_device *dev = ap->local->dev; + struct hostap_ieee80211_hdr *hdr; + u16 fc, *pos, auth_alg, auth_transaction, status; + struct sta_info *sta = NULL; + char *txt = NULL; + + if (ap->local->hostapd) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH || + skb->len < IEEE80211_MGMT_HDR_LEN + 6) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid " + "frame\n", dev->name); + dev_kfree_skb(skb); + return; + } + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + auth_alg = le16_to_cpu(*pos++); + auth_transaction = le16_to_cpu(*pos++); + status = le16_to_cpu(*pos++); + + if (!ok) { + txt = "frame was not ACKed"; + goto done; + } + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&ap->sta_table_lock); + + if (!sta) { + txt = "STA not found"; + goto done; + } + + if (status == WLAN_STATUS_SUCCESS && + ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { + txt = "STA authenticated"; + sta->flags |= WLAN_STA_AUTH; + sta->last_auth = jiffies; + } else if (status != WLAN_STATUS_SUCCESS) + txt = "authentication failed"; + + done: + if (sta) + atomic_dec(&sta->users); + if (txt) { + PDEBUG(DEBUG_AP, "%s: " MACSTR " auth_cb - alg=%d trans#=%d " + "status=%d - %s\n", + dev->name, MAC2STR(hdr->addr1), auth_alg, + auth_transaction, status, txt); + } + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct net_device *dev = ap->local->dev; + struct hostap_ieee80211_hdr *hdr; + u16 fc, *pos, status; + struct sta_info *sta = NULL; + char *txt = NULL; + + if (ap->local->hostapd) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ASSOC_RESP && + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_REASSOC_RESP) || + skb->len < IEEE80211_MGMT_HDR_LEN + 4) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid " + "frame\n", dev->name); + dev_kfree_skb(skb); + return; + } + + if (!ok) { + txt = "frame was not ACKed"; + goto done; + } + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&ap->sta_table_lock); + + if (!sta) { + txt = "STA not found"; + goto done; + } + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + pos++; + status = le16_to_cpu(*pos++); + if (status == WLAN_STATUS_SUCCESS) { + if (!(sta->flags & WLAN_STA_ASSOC)) + hostap_event_new_sta(dev, sta); + txt = "STA associated"; + sta->flags |= WLAN_STA_ASSOC; + sta->last_assoc = jiffies; + } else + txt = "association failed"; + + done: + if (sta) + atomic_dec(&sta->users); + if (txt) { + PDEBUG(DEBUG_AP, "%s: " MACSTR " assoc_cb - %s\n", + dev->name, MAC2STR(hdr->addr1), txt); + } + dev_kfree_skb(skb); +} + +/* Called only as a tasklet (software IRQ); TX callback for poll frames used + * in verifying whether the STA is still present. */ +static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct hostap_ieee80211_hdr *hdr; + struct sta_info *sta; + + if (skb->len < 24) + goto fail; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (ok) { + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + sta->flags &= ~WLAN_STA_PENDING_POLL; + spin_unlock(&ap->sta_table_lock); + } else { + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " did not ACK activity " + "poll frame\n", ap->local->dev->name, + MAC2STR(hdr->addr1)); + } + + fail: + dev_kfree_skb(skb); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void hostap_init_data(local_info_t *local) +{ + struct ap_data *ap = local->ap; + + if (ap == NULL) { + printk(KERN_WARNING "hostap_init_data: ap == NULL\n"); + return; + } + memset(ap, 0, sizeof(struct ap_data)); + ap->local = local; + + ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx); + ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx); + ap->max_inactivity = + GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ; + ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx); + + spin_lock_init(&ap->sta_table_lock); + INIT_LIST_HEAD(&ap->sta_list); + + /* Initialize task queue structure for AP management */ + INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue, ap); + + ap->tx_callback_idx = + hostap_tx_callback_register(local, hostap_ap_tx_cb, ap); + if (ap->tx_callback_idx == 0) + printk(KERN_WARNING "%s: failed to register TX callback for " + "AP\n", local->dev->name); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue, local); + + ap->tx_callback_auth = + hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap); + ap->tx_callback_assoc = + hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap); + ap->tx_callback_poll = + hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap); + if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 || + ap->tx_callback_poll == 0) + printk(KERN_WARNING "%s: failed to register TX callback for " + "AP\n", local->dev->name); + + spin_lock_init(&ap->mac_restrictions.lock); + INIT_LIST_HEAD(&ap->mac_restrictions.mac_list); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + ap->initialized = 1; +} + + +void hostap_init_ap_proc(local_info_t *local) +{ + struct ap_data *ap = local->ap; + + ap->proc = local->proc; + if (ap->proc == NULL) + return; + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("ap_debug", 0, ap->proc, + ap_debug_proc_read, ap); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + create_proc_read_entry("ap_control", 0, ap->proc, + ap_control_proc_read, ap); + create_proc_read_entry("ap", 0, ap->proc, + prism2_ap_proc_read, ap); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +} + + +void hostap_free_data(struct ap_data *ap) +{ + struct list_head *n, *ptr; + + if (ap == NULL || !ap->initialized) { + printk(KERN_DEBUG "hostap_free_data: ap has not yet been " + "initialized - skip resource freeing\n"); + return; + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (ap->crypt) + ap->crypt->deinit(ap->crypt_priv); + ap->crypt = ap->crypt_priv = NULL; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + list_for_each_safe(ptr, n, &ap->sta_list) { + struct sta_info *sta = list_entry(ptr, struct sta_info, list); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (ap->proc != NULL) { + remove_proc_entry("ap_debug", ap->proc); + } +#endif /* PRISM2_NO_PROCFS_DEBUG */ + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (ap->proc != NULL) { + remove_proc_entry("ap", ap->proc); + remove_proc_entry("ap_control", ap->proc); + } + ap_control_flush_macs(&ap->mac_restrictions); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + ap->initialized = 0; +} + + +/* caller should have mutex for AP STA list handling */ +static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta) +{ + struct sta_info *s; + + s = ap->sta_hash[STA_HASH(sta)]; + while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0) + s = s->hnext; + return s; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +/* Called from timer handler and from scheduled AP queue handlers */ +static void prism2_send_mgmt(struct net_device *dev, + int type, int subtype, char *body, + int body_len, u8 *addr, u16 tx_cb_idx) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hostap_ieee80211_hdr *hdr; + u16 fc; + struct sk_buff *skb; + struct hostap_skb_tx_data *meta; + int hdrlen; + + dev = local->dev; /* always use master radio device */ + iface = dev->priv; + + if (!(dev->flags & IFF_UP)) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - " + "cannot send frame\n", dev->name); + return; + } + + skb = dev_alloc_skb(sizeof(*hdr) + body_len); + if (skb == NULL) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate " + "skb\n", dev->name); + return; + } + + fc = (type << 2) | (subtype << 4); + hdrlen = hostap_80211_get_hdrlen(fc); + hdr = (struct hostap_ieee80211_hdr *) skb_put(skb, hdrlen); + if (body) + memcpy(skb_put(skb, body_len), body, body_len); + + memset(hdr, 0, hdrlen); + + /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11 + * tx_control instead of using local->tx_control */ + + + memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */ + if (type == WLAN_FC_TYPE_DATA) { + fc |= WLAN_FC_FROMDS; + memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */ + memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */ + } else if (type == WLAN_FC_TYPE_CTRL) { + /* control:ACK does not have addr2 or addr3 */ + memset(hdr->addr2, 0, ETH_ALEN); + memset(hdr->addr3, 0, ETH_ALEN); + } else { + memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */ + memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */ + } + + hdr->frame_control = cpu_to_le16(fc); + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = iface; + meta->tx_cb_idx = tx_cb_idx; + + skb->dev = dev; + skb->mac.raw = skb->nh.raw = skb->data; + dev_queue_xmit(skb); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static int prism2_sta_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct sta_info *sta = (struct sta_info *) data; + int i; + + /* FIX: possible race condition.. the STA data could have just expired, + * but proc entry was still here so that the read could have started; + * some locking should be done here.. */ + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "%s=" MACSTR "\nusers=%d\naid=%d\n" + "flags=0x%04x%s%s%s%s%s%s%s\n" + "capability=0x%02x\nlisten_interval=%d\nsupported_rates=", + sta->ap ? "AP" : "STA", + MAC2STR(sta->addr), atomic_read(&sta->users), sta->aid, + sta->flags, + sta->flags & WLAN_STA_AUTH ? " AUTH" : "", + sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "", + sta->flags & WLAN_STA_PS ? " PS" : "", + sta->flags & WLAN_STA_TIM ? " TIM" : "", + sta->flags & WLAN_STA_PERM ? " PERM" : "", + sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "", + sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "", + sta->capability, sta->listen_interval); + /* supported_rates: 500 kbit/s units with msb ignored */ + for (i = 0; i < sizeof(sta->supported_rates); i++) + if (sta->supported_rates[i] != 0) + p += sprintf(p, "%d%sMbps ", + (sta->supported_rates[i] & 0x7f) / 2, + sta->supported_rates[i] & 1 ? ".5" : ""); + p += sprintf(p, "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n" + "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n" + "tx_packets=%lu\n" + "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n" + "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n" + "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n" + "tx[11M]=%d\n" + "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n", + jiffies, sta->last_auth, sta->last_assoc, sta->last_rx, + sta->last_tx, + sta->rx_packets, sta->tx_packets, sta->rx_bytes, + sta->tx_bytes, skb_queue_len(&sta->tx_buf), + sta->last_rx_silence, + sta->last_rx_signal, sta->last_rx_rate / 10, + sta->last_rx_rate % 10 ? ".5" : "", + sta->tx_rate, sta->tx_count[0], sta->tx_count[1], + sta->tx_count[2], sta->tx_count[3], sta->rx_count[0], + sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]); + if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats) + p = sta->crypt->ops->print_stats(p, sta->crypt->priv); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->ap) { + if (sta->u.ap.channel >= 0) + p += sprintf(p, "channel=%d\n", sta->u.ap.channel); + p += sprintf(p, "ssid="); + for (i = 0; i < sta->u.ap.ssid_len; i++) + p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 && + sta->u.ap.ssid[i] < 127) ? + "%c" : "<%02x>"), + sta->u.ap.ssid[i]); + p += sprintf(p, "\n"); + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + return (p - page); +} + + +static void handle_add_proc_queue(void *data) +{ + struct ap_data *ap = (struct ap_data *) data; + struct sta_info *sta; + char name[20]; + struct add_sta_proc_data *entry, *prev; + + entry = ap->add_sta_proc_entries; + ap->add_sta_proc_entries = NULL; + + while (entry) { + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, entry->addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (sta) { + sprintf(name, MACSTR, MAC2STR(sta->addr)); + sta->proc = create_proc_read_entry( + name, 0, ap->proc, + prism2_sta_proc_read, sta); + + atomic_dec(&sta->users); + } + + prev = entry; + entry = entry->next; + kfree(prev); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr) +{ + struct sta_info *sta; + + sta = (struct sta_info *) + kmalloc(sizeof(struct sta_info), GFP_ATOMIC); + if (sta == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed\n"); + return NULL; + } + + /* initialize STA info data */ + memset(sta, 0, sizeof(struct sta_info)); + sta->local = ap->local; + skb_queue_head_init(&sta->tx_buf); + memcpy(sta->addr, addr, ETH_ALEN); + + atomic_inc(&sta->users); + spin_lock_bh(&ap->sta_table_lock); + list_add(&sta->list, &ap->sta_list); + ap->num_sta++; + ap_sta_hash_add(ap, sta); + spin_unlock_bh(&ap->sta_table_lock); + + if (ap->proc) { + struct add_sta_proc_data *entry; + /* schedule a non-interrupt context process to add a procfs + * entry for the STA since procfs code use GFP_KERNEL */ + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry) { + memcpy(entry->addr, sta->addr, ETH_ALEN); + entry->next = ap->add_sta_proc_entries; + ap->add_sta_proc_entries = entry; + PRISM2_SCHEDULE_TASK(&ap->add_sta_proc_queue); + } else + printk(KERN_DEBUG "Failed to add STA proc data\n"); + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + init_timer(&sta->timer); + sta->timer.expires = jiffies + ap->max_inactivity; + sta->timer.data = (unsigned long) sta; + sta->timer.function = ap_handle_timer; + if (!ap->local->hostapd) + add_timer(&sta->timer); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + return sta; +} + + +static int ap_tx_rate_ok(int rateidx, struct sta_info *sta, + local_info_t *local) +{ + if (rateidx > sta->tx_max_rate || + !(sta->tx_supp_rates & (1 << rateidx))) + return 0; + + if (local->tx_rate_control != 0 && + !(local->tx_rate_control & (1 << rateidx))) + return 0; + + return 1; +} + + +static void prism2_check_tx_rates(struct sta_info *sta) +{ + int i; + + sta->tx_supp_rates = 0; + for (i = 0; i < sizeof(sta->supported_rates); i++) { + if ((sta->supported_rates[i] & 0x7f) == 2) + sta->tx_supp_rates |= WLAN_RATE_1M; + if ((sta->supported_rates[i] & 0x7f) == 4) + sta->tx_supp_rates |= WLAN_RATE_2M; + if ((sta->supported_rates[i] & 0x7f) == 11) + sta->tx_supp_rates |= WLAN_RATE_5M5; + if ((sta->supported_rates[i] & 0x7f) == 22) + sta->tx_supp_rates |= WLAN_RATE_11M; + } + sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0; + if (sta->tx_supp_rates & WLAN_RATE_1M) { + sta->tx_max_rate = 0; + if (ap_tx_rate_ok(0, sta, sta->local)) { + sta->tx_rate = 10; + sta->tx_rate_idx = 0; + } + } + if (sta->tx_supp_rates & WLAN_RATE_2M) { + sta->tx_max_rate = 1; + if (ap_tx_rate_ok(1, sta, sta->local)) { + sta->tx_rate = 20; + sta->tx_rate_idx = 1; + } + } + if (sta->tx_supp_rates & WLAN_RATE_5M5) { + sta->tx_max_rate = 2; + if (ap_tx_rate_ok(2, sta, sta->local)) { + sta->tx_rate = 55; + sta->tx_rate_idx = 2; + } + } + if (sta->tx_supp_rates & WLAN_RATE_11M) { + sta->tx_max_rate = 3; + if (ap_tx_rate_ok(3, sta, sta->local)) { + sta->tx_rate = 110; + sta->tx_rate_idx = 3; + } + } +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void ap_crypt_init(struct ap_data *ap) +{ + ap->crypt = hostap_get_crypto_ops("WEP"); + + if (ap->crypt) { + if (ap->crypt->init) { + ap->crypt_priv = ap->crypt->init(0); + if (ap->crypt_priv == NULL) + ap->crypt = NULL; + else { + u8 key[WEP_KEY_LEN]; + get_random_bytes(key, WEP_KEY_LEN); + ap->crypt->set_key(key, WEP_KEY_LEN, NULL, + ap->crypt_priv); + } + } + } + + if (ap->crypt == NULL) { + printk(KERN_WARNING "AP could not initialize WEP: load module " + "hostap_crypt_wep.o\n"); + } +} + + +/* Generate challenge data for shared key authentication. IEEE 802.11 specifies + * that WEP algorithm is used for generating challange. This should be unique, + * but otherwise there is not really need for randomness etc. Initialize WEP + * with pseudo random key and then use increasing IV to get unique challenge + * streams. + * + * Called only as a scheduled task for pending AP frames. + */ +static char * ap_auth_make_challenge(struct ap_data *ap) +{ + char *tmpbuf; + struct sk_buff *skb; + + if (ap->crypt == NULL) { + ap_crypt_init(ap); + if (ap->crypt == NULL) + return NULL; + } + + tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC); + if (tmpbuf == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n"); + return NULL; + } + + skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN + + ap->crypt->extra_prefix_len + + ap->crypt->extra_postfix_len); + if (skb == NULL) { + kfree(tmpbuf); + return NULL; + } + + skb_reserve(skb, ap->crypt->extra_prefix_len); + memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0, + WLAN_AUTH_CHALLENGE_LEN); + if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) { + dev_kfree_skb(skb); + kfree(tmpbuf); + return NULL; + } + + memcpy(tmpbuf, skb->data + ap->crypt->extra_prefix_len, + WLAN_AUTH_CHALLENGE_LEN); + dev_kfree_skb(skb); + + return tmpbuf; +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_authen(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + size_t hdrlen; + struct ap_data *ap = local->ap; + char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL; + int len, olen; + u16 auth_alg, auth_transaction, status_code, *pos; + u16 resp = WLAN_STATUS_SUCCESS, fc; + struct sta_info *sta = NULL; + struct prism2_crypt_data *crypt; + char *txt = ""; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = hostap_80211_get_hdrlen(fc); + + if (len < 6) { + PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload " + "(len=%d) from " MACSTR "\n", dev->name, len, + MAC2STR(hdr->addr2)); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta && sta->crypt) + crypt = sta->crypt; + else { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = local->crypt[idx]; + } + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + auth_alg = __le16_to_cpu(*pos); + pos++; + auth_transaction = __le16_to_cpu(*pos); + pos++; + status_code = __le16_to_cpu(*pos); + pos++; + + if (memcmp(dev->dev_addr, hdr->addr2, ETH_ALEN) == 0 || + ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) { + txt = "authentication denied"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (((local->auth_algs & PRISM2_AUTH_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || + ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) && + crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) { + } else { + txt = "unsupported algorithm"; + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } + + if (len >= 8) { + u8 *u = (u8 *) pos; + if (*u == WLAN_EID_CHALLENGE) { + if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) { + txt = "invalid challenge len"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) { + txt = "challenge underflow"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + challenge = (char *) (u + 2); + } + } + + if (sta && sta->ap) { + if (time_after(jiffies, sta->u.ap.last_beacon + + (10 * sta->listen_interval * HZ) / 1024)) { + PDEBUG(DEBUG_AP, "%s: no beacons received for a while," + " assuming AP " MACSTR " is now STA\n", + dev->name, MAC2STR(sta->addr)); + sta->ap = 0; + sta->flags = 0; + sta->u.sta.challenge = NULL; + } else { + txt = "AP trying to authenticate?"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) || + (auth_alg == WLAN_AUTH_SHARED_KEY && + (auth_transaction == 1 || + (auth_transaction == 3 && sta != NULL && + sta->u.sta.challenge != NULL)))) { + } else { + txt = "unknown authentication transaction number"; + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto fail; + } + + if (sta == NULL) { + txt = "new STA"; + + if (local->ap->num_sta >= MAX_STA_COUNT) { + /* FIX: might try to remove some old STAs first? */ + txt = "no more room for new STAs"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + sta = ap_add_sta(local->ap, hdr->addr2); + if (sta == NULL) { + txt = "ap_add_sta failed"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + txt = "authOK"; + /* IEEE 802.11 standard is not completely clear about + * whether STA is considered authenticated after + * authentication OK frame has been send or after it + * has been ACKed. In order to reduce interoperability + * issues, mark the STA authenticated before ACK. */ + sta->flags |= WLAN_STA_AUTH; + break; + + case WLAN_AUTH_SHARED_KEY: + if (auth_transaction == 1) { + if (sta->u.sta.challenge == NULL) { + sta->u.sta.challenge = + ap_auth_make_challenge(local->ap); + if (sta->u.sta.challenge == NULL) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + } else { + if (sta->u.sta.challenge == NULL || + challenge == NULL || + memcmp(sta->u.sta.challenge, challenge, + WLAN_AUTH_CHALLENGE_LEN) != 0 || + !(fc & WLAN_FC_ISWEP)) { + txt = "challenge response incorrect"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + + txt = "challenge OK - authOK"; + /* IEEE 802.11 standard is not completely clear about + * whether STA is considered authenticated after + * authentication OK frame has been send or after it + * has been ACKed. In order to reduce interoperability + * issues, mark the STA authenticated before ACK. */ + sta->flags |= WLAN_STA_AUTH; + kfree(sta->u.sta.challenge); + sta->u.sta.challenge = NULL; + } + break; + } + + fail: + pos = (u16 *) body; + *pos = cpu_to_le16(auth_alg); + pos++; + *pos = cpu_to_le16(auth_transaction + 1); + pos++; + *pos = cpu_to_le16(resp); /* status_code */ + pos++; + olen = 6; + + if (resp == WLAN_STATUS_SUCCESS && sta != NULL && + sta->u.sta.challenge != NULL && + auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) { + u8 *tmp = (u8 *) pos; + *tmp++ = WLAN_EID_CHALLENGE; + *tmp++ = WLAN_AUTH_CHALLENGE_LEN; + pos++; + memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN); + olen += 2 + WLAN_AUTH_CHALLENGE_LEN; + } + + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_AUTH, + body, olen, hdr->addr2, ap->tx_callback_auth); + + if (sta) { + sta->last_rx = jiffies; + atomic_dec(&sta->users); + } + + if (resp) { + PDEBUG(DEBUG_AP, "%s: " MACSTR " auth (alg=%d trans#=%d " + "stat=%d len=%d fc=%04x) ==> %d (%s)\n", + dev->name, MAC2STR(hdr->addr2), auth_alg, + auth_transaction, status_code, len, fc, resp, txt); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_assoc(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int reassoc) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char body[12], *p, *lpos; + int len, left; + u16 *pos; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; + int send_deauth = 0; + char *txt = ""; + u8 prev_ap[ETH_ALEN]; + + left = len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < (reassoc ? 10 : 4)) { + PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload " + "(len=%d, reassoc=%d) from " MACSTR "\n", + dev->name, len, reassoc, MAC2STR(hdr->addr2)); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { + spin_unlock_bh(&local->ap->sta_table_lock); + txt = "trying to associate before authentication"; + send_deauth = 1; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + sta = NULL; /* do not decrement sta->users */ + goto fail; + } + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + sta->capability = __le16_to_cpu(*pos); + pos++; left -= 2; + sta->listen_interval = __le16_to_cpu(*pos); + pos++; left -= 2; + + if (reassoc) { + memcpy(prev_ap, pos, ETH_ALEN); + pos++; pos++; pos++; left -= 6; + } else + memset(prev_ap, 0, ETH_ALEN); + + if (left >= 2) { + unsigned int ileft; + unsigned char *u = (unsigned char *) pos; + + if (*u == WLAN_EID_SSID) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft > MAX_SSID_LEN) { + txt = "SSID overflow"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (ileft != strlen(local->essid) || + memcmp(local->essid, u, ileft) != 0) { + txt = "not our SSID"; + resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC; + goto fail; + } + + u += ileft; + left -= ileft; + } + + if (left >= 2 && *u == WLAN_EID_SUPP_RATES) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft == 0 || + ileft > WLAN_SUPP_RATES_MAX) { + txt = "SUPP_RATES len error"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + memset(sta->supported_rates, 0, + sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, u, ileft); + prism2_check_tx_rates(sta); + + u += ileft; + left -= ileft; + } + + if (left > 0) { + PDEBUG(DEBUG_AP, "%s: assoc from " MACSTR " with extra" + " data (%d bytes) [", + dev->name, MAC2STR(hdr->addr2), left); + while (left > 0) { + PDEBUG2(DEBUG_AP, "<%02x>", *u); + u++; left--; + } + PDEBUG2(DEBUG_AP, "]\n"); + } + } else { + txt = "frame underflow"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* get a unique AID */ + if (sta->aid > 0) + txt = "OK, old AID"; + else { + spin_lock_bh(&local->ap->sta_table_lock); + for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) + if (local->ap->sta_aid[sta->aid - 1] == NULL) + break; + if (sta->aid > MAX_AID_TABLE_SIZE) { + sta->aid = 0; + spin_unlock_bh(&local->ap->sta_table_lock); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + txt = "no room for more AIDs"; + } else { + local->ap->sta_aid[sta->aid - 1] = sta; + spin_unlock_bh(&local->ap->sta_table_lock); + txt = "OK, new AID"; + } + } + + fail: + pos = (u16 *) body; + + if (send_deauth) { + *pos = __constant_cpu_to_le16( + WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH); + pos++; + } else { + /* FIX: CF-Pollable and CF-PollReq should be set to match the + * values in beacons/probe responses */ + /* FIX: how about privacy and WEP? */ + /* capability */ + *pos = __constant_cpu_to_le16(WLAN_CAPABILITY_ESS); + pos++; + + /* status_code */ + *pos = __cpu_to_le16(resp); + pos++; + + *pos = __cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) | + BIT(14) | BIT(15)); /* AID */ + pos++; + + /* Supported rates (Information element) */ + p = (char *) pos; + *p++ = WLAN_EID_SUPP_RATES; + lpos = p; + *p++ = 0; /* len */ + if (local->tx_rate_control & WLAN_RATE_1M) { + *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_2M) { + *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_5M5) { + *p++ = local->basic_rates & WLAN_RATE_5M5 ? + 0x8b : 0x0b; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_11M) { + *p++ = local->basic_rates & WLAN_RATE_11M ? + 0x96 : 0x16; + (*lpos)++; + } + pos = (u16 *) p; + } + + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, + (send_deauth ? WLAN_FC_STYPE_DEAUTH : + (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : + WLAN_FC_STYPE_ASSOC_RESP)), + body, (u8 *) pos - (u8 *) body, + hdr->addr2, + send_deauth ? 0 : local->ap->tx_callback_assoc); + + if (sta) { + if (resp == WLAN_STATUS_SUCCESS) { + sta->last_rx = jiffies; + /* STA will be marked associated from TX callback, if + * AssocResp is ACKed */ + } + atomic_dec(&sta->users); + } + +#if 0 + PDEBUG(DEBUG_AP, "%s: " MACSTR " %sassoc (len=%d prev_ap=" MACSTR + ") => %d(%d) (%s)\n", + dev->name, MAC2STR(hdr->addr2), reassoc ? "re" : "", len, + MAC2STR(prev_ap), resp, send_deauth, txt); +#endif +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_deauth(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN); + int len; + u16 reason_code, *pos; + struct sta_info *sta = NULL; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 2) { + printk("handle_deauth - too short payload (len=%d)\n", len); + return; + } + + pos = (u16 *) body; + reason_code = __le16_to_cpu(*pos); + + PDEBUG(DEBUG_AP, "%s: deauthentication: " MACSTR " len=%d, " + "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len, + reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + } + spin_unlock_bh(&local->ap->sta_table_lock); + if (sta == NULL) { + printk("%s: deauthentication from " MACSTR ", " + "reason_code=%d, but STA not authenticated\n", dev->name, + MAC2STR(hdr->addr2), reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_disassoc(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char *body = skb->data + IEEE80211_MGMT_HDR_LEN; + int len; + u16 reason_code, *pos; + struct sta_info *sta = NULL; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 2) { + printk("handle_disassoc - too short payload (len=%d)\n", len); + return; + } + + pos = (u16 *) body; + reason_code = __le16_to_cpu(*pos); + + PDEBUG(DEBUG_AP, "%s: disassociation: " MACSTR " len=%d, " + "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len, + reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~WLAN_STA_ASSOC; + } + spin_unlock_bh(&local->ap->sta_table_lock); + if (sta == NULL) { + printk("%s: disassociation from " MACSTR ", " + "reason_code=%d, but STA not authenticated\n", + dev->name, MAC2STR(hdr->addr2), reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_data_nullfunc(local_info_t *local, + struct hostap_ieee80211_hdr *hdr) +{ + struct net_device *dev = local->dev; + + /* some STA f/w's seem to require control::ACK frame for + * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does + * not send this.. + * send control::ACK for the data::nullfunc */ + + printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n"); + prism2_send_mgmt(dev, WLAN_FC_TYPE_CTRL, WLAN_FC_STYPE_ACK, + NULL, 0, hdr->addr2, 0); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_dropped_data(local_info_t *local, + struct hostap_ieee80211_hdr *hdr) +{ + struct net_device *dev = local->dev; + struct sta_info *sta; + u16 reason; + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) { + PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n"); + atomic_dec(&sta->users); + return; + } + + reason = __constant_cpu_to_le16( + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, + ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ? + WLAN_FC_STYPE_DEAUTH : WLAN_FC_STYPE_DISASSOC), + (char *) &reason, sizeof(reason), hdr->addr2, 0); + + if (sta) + atomic_dec(&sta->users); +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +/* Called only as a scheduled task for pending AP frames. */ +static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta, + struct sk_buff *skb) +{ + if (!(sta->flags & WLAN_STA_PS)) { + /* Station has moved to non-PS mode, so send all buffered + * frames using normal device queue. */ + dev_queue_xmit(skb); + return; + } + + /* add a flag for hostap_handle_sta_tx() to know that this skb should + * be passed through even though STA is using PS */ + memcpy(skb->cb, AP_SKB_CB_MAGIC, AP_SKB_CB_MAGIC_LEN); + skb->cb[AP_SKB_CB_MAGIC_LEN] = AP_SKB_CB_BUFFERED_FRAME; + if (!skb_queue_empty(&sta->tx_buf)) { + /* indicate to STA that more frames follow */ + skb->cb[AP_SKB_CB_MAGIC_LEN] |= AP_SKB_CB_ADD_MOREDATA; + } + dev_queue_xmit(skb); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_pspoll(local_info_t *local, + struct hostap_ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct sta_info *sta; + u16 aid; + struct sk_buff *skb; + + PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=" MACSTR ", TA=" MACSTR + " PWRMGT=%d\n", + MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), + !!(le16_to_cpu(hdr->frame_control) & WLAN_FC_PWRMGT)); + + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MACSTR + " not own MAC\n", MAC2STR(hdr->addr1)); + return; + } + + aid = __le16_to_cpu(hdr->duration_id); + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) { + PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); + return; + } + aid &= ~BIT(15) & ~BIT(14); + if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { + PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); + return; + } + PDEBUG(DEBUG_PS2, " aid=%d\n", aid); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta == NULL) { + PDEBUG(DEBUG_PS, " STA not found\n"); + return; + } + if (sta->aid != aid) { + PDEBUG(DEBUG_PS, " received aid=%i does not match with " + "assoc.aid=%d\n", aid, sta->aid); + return; + } + + /* FIX: todo: + * - add timeout for buffering (clear aid in TIM vector if buffer timed + * out (expiry time must be longer than ListenInterval for + * the corresponding STA; "8802-11: 11.2.1.9 AP aging function" + * - what to do, if buffered, pspolled, and sent frame is not ACKed by + * sta; store buffer for later use and leave TIM aid bit set? use + * TX event to check whether frame was ACKed? + */ + + while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) { + /* send buffered frame .. */ + PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL" + " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf)); + + pspoll_send_buffered(local, sta, skb); + + if (sta->flags & WLAN_STA_PS) { + /* send only one buffered packet per PS Poll */ + /* FIX: should ignore further PS Polls until the + * buffered packet that was just sent is acknowledged + * (Tx or TxExc event) */ + break; + } + } + + if (skb_queue_empty(&sta->tx_buf)) { + /* try to clear aid from TIM */ + if (!(sta->flags & WLAN_STA_TIM)) + PDEBUG(DEBUG_PS2, "Re-unsetting TIM for aid %d\n", + aid); + hostap_set_tim(local, aid, 0); + sta->flags &= ~WLAN_STA_TIM; + } + + atomic_dec(&sta->users); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void handle_wds_oper_queue(void *data) +{ + local_info_t *local = data; + struct wds_oper_data *entry, *prev; + + spin_lock_bh(&local->lock); + entry = local->ap->wds_oper_entries; + local->ap->wds_oper_entries = NULL; + spin_unlock_bh(&local->lock); + + while (entry) { + PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection " + "to AP " MACSTR "\n", + local->dev->name, + entry->type == WDS_ADD ? "adding" : "removing", + MAC2STR(entry->addr)); + if (entry->type == WDS_ADD) + prism2_wds_add(local, entry->addr, 0); + else if (entry->type == WDS_DEL) + prism2_wds_del(local, entry->addr, 0, 1); + + prev = entry; + entry = entry->next; + kfree(prev); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_beacon(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char *body = skb->data + IEEE80211_MGMT_HDR_LEN; + int len, left; + u16 *pos, beacon_int, capability; + char *ssid = NULL; + unsigned char *supp_rates = NULL; + int ssid_len = 0, supp_rates_len = 0; + struct sta_info *sta = NULL; + int new_sta = 0, channel = -1; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 8 + 2 + 2) { + printk(KERN_DEBUG "handle_beacon - too short payload " + "(len=%d)\n", len); + return; + } + + pos = (u16 *) body; + left = len; + + /* Timestamp (8 octets) */ + pos += 4; left -= 8; + /* Beacon interval (2 octets) */ + beacon_int = __le16_to_cpu(*pos); + pos++; left -= 2; + /* Capability information (2 octets) */ + capability = __le16_to_cpu(*pos); + pos++; left -= 2; + + if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS && + capability & WLAN_CAPABILITY_IBSS) + return; + + if (left >= 2) { + unsigned int ileft; + unsigned char *u = (unsigned char *) pos; + + if (*u == WLAN_EID_SSID) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft > MAX_SSID_LEN) { + PDEBUG(DEBUG_AP, "SSID: overflow\n"); + return; + } + + if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID && + (ileft != strlen(local->essid) || + memcmp(local->essid, u, ileft) != 0)) { + /* not our SSID */ + return; + } + + ssid = u; + ssid_len = ileft; + + u += ileft; + left -= ileft; + } + + if (*u == WLAN_EID_SUPP_RATES) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft == 0 || ileft > 8) { + PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n"); + return; + } + + supp_rates = u; + supp_rates_len = ileft; + + u += ileft; + left -= ileft; + } + + if (*u == WLAN_EID_DS_PARAMS) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft != 1) { + PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n"); + return; + } + + channel = *u; + + u += ileft; + left -= ileft; + } + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta == NULL) { + /* add new AP */ + new_sta = 1; + sta = ap_add_sta(local->ap, hdr->addr2); + if (sta == NULL) { + printk(KERN_INFO "prism2: kmalloc failed for AP " + "data structure\n"); + return; + } + hostap_event_new_sta(local->dev, sta); + + /* mark APs authentication and associated for pseudo ad-hoc + * style communication */ + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + + if (local->ap->autom_ap_wds) { + hostap_wds_link_oper(local, sta->addr, WDS_ADD); + } + } + + sta->ap = 1; + if (ssid) { + sta->u.ap.ssid_len = ssid_len; + memcpy(sta->u.ap.ssid, ssid, ssid_len); + sta->u.ap.ssid[ssid_len] = '\0'; + } else { + sta->u.ap.ssid_len = 0; + sta->u.ap.ssid[0] = '\0'; + } + sta->u.ap.channel = channel; + sta->rx_packets++; + sta->rx_bytes += len; + sta->u.ap.last_beacon = sta->last_rx = jiffies; + sta->capability = capability; + sta->listen_interval = beacon_int; + + atomic_dec(&sta->users); + + if (new_sta) { + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, supp_rates, supp_rates_len); + prism2_check_tx_rates(sta); + } +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +/* Called only as a tasklet. */ +static void handle_ap_item(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + struct net_device *dev = local->dev; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + u16 fc, type, stype; + struct hostap_ieee80211_hdr *hdr; + + /* FIX: should give skb->len to handler functions and check that the + * buffer is long enough */ + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && type == WLAN_FC_TYPE_DATA) { + PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n"); + + if (!(fc & WLAN_FC_TODS) || (fc & WLAN_FC_FROMDS)) { + if (stype == WLAN_FC_STYPE_NULLFUNC) { + /* no ToDS nullfunc seems to be used to check + * AP association; so send reject message to + * speed up re-association */ + ap_handle_dropped_data(local, hdr); + goto done; + } + PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n", + fc); + goto done; + } + + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=" + MACSTR " not own MAC\n", + MAC2STR(hdr->addr1)); + goto done; + } + + if (local->ap->nullfunc_ack && stype == WLAN_FC_STYPE_NULLFUNC) + ap_handle_data_nullfunc(local, hdr); + else + ap_handle_dropped_data(local, hdr); + goto done; + } + + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_BEACON) { + handle_beacon(local, skb, rx_stats); + goto done; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL) { + handle_pspoll(local, hdr, rx_stats); + goto done; + } + + if (local->hostapd) { + PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x " + "subtype=0x%02x\n", type, stype); + goto done; + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (type != WLAN_FC_TYPE_MGMT) { + PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n"); + goto done; + } + + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MACSTR + " not own MAC\n", MAC2STR(hdr->addr1)); + goto done; + } + + if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MACSTR + " not own MAC\n", MAC2STR(hdr->addr3)); + goto done; + } + + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + handle_assoc(local, skb, rx_stats, 0); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n"); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + handle_assoc(local, skb, rx_stats, 1); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n"); + break; + case WLAN_FC_STYPE_ATIM: + PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n"); + break; + case WLAN_FC_STYPE_DISASSOC: + handle_disassoc(local, skb, rx_stats); + break; + case WLAN_FC_STYPE_AUTH: + handle_authen(local, skb, rx_stats); + break; + case WLAN_FC_STYPE_DEAUTH: + handle_deauth(local, skb, rx_stats); + break; + default: + PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n", stype); + break; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + done: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +void hostap_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 fc; + struct hostap_ieee80211_hdr *hdr; + + if (skb->len < 16) + goto drop; + + local->stats.rx_packets++; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + goto drop; + + skb->protocol = __constant_htons(ETH_P_HOSTAP); + handle_ap_item(local, skb, rx_stats); + return; + + drop: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void schedule_packet_send(local_info_t *local, struct sta_info *sta) +{ + struct sk_buff *skb; + struct hostap_ieee80211_hdr *hdr; + struct hostap_80211_rx_status rx_stats; + + if (skb_queue_empty(&sta->tx_buf)) + return; + + skb = dev_alloc_skb(16); + if (skb == NULL) { + printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc " + "failed\n", local->dev->name); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb_put(skb, 16); + + /* Generate a fake pspoll frame to start packet delivery */ + hdr->frame_control = __constant_cpu_to_le16( + (WLAN_FC_TYPE_CTRL << 2) | (WLAN_FC_STYPE_PSPOLL << 4)); + memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN); + memcpy(hdr->addr2, sta->addr, ETH_ALEN); + hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14)); + + PDEBUG(DEBUG_PS2, "%s: Scheduling buffered packet delivery for " + "STA " MACSTR "\n", local->dev->name, MAC2STR(sta->addr)); + + skb->dev = local->dev; + + memset(&rx_stats, 0, sizeof(rx_stats)); + hostap_rx(local->dev, skb, &rx_stats); +} + + +static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist) +{ + struct ap_data *ap = local->ap; + struct list_head *ptr; + int count = 0; + + spin_lock_bh(&ap->sta_table_lock); + + for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; + ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + if (aplist && !sta->ap) + continue; + addr[count].sa_family = ARPHRD_ETHER; + memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); + if (sta->last_rx_silence == 0) + qual[count].qual = sta->last_rx_signal < 27 ? + 0 : (sta->last_rx_signal - 27) * 92 / 127; + else + qual[count].qual = sta->last_rx_signal - + sta->last_rx_silence - 35; + qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); + qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); + qual[count].updated = sta->last_rx_updated; + + sta->last_rx_updated = 0; + + count++; + if (count >= buf_size) + break; + } + spin_unlock_bh(&ap->sta_table_lock); + + return count; +} + + +/* Translate our list of Access Points & Stations to a card independant + * format that the Wireless Tools will understand - Jean II */ +static int prism2_ap_translate_scan(struct net_device *dev, char *buffer) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct ap_data *ap = local->ap; + struct list_head *ptr; + struct iw_event iwe; + char *current_ev = buffer; + char *end_buf = buffer + IW_SCAN_MAX_DATA; +#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT) && (WIRELESS_EXT > 14) + char buf[64]; +#endif + + spin_lock_bh(&ap->sta_table_lock); + + for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; + ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + /* First entry *MUST* be the AP MAC address */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); + iwe.len = IW_EV_ADDR_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + /* Use the mode to indicate if it's a station or + * an Access Point */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (sta->ap) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_INFRA; + iwe.len = IW_EV_UINT_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_UINT_LEN); + + /* Some quality */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (sta->last_rx_silence == 0) + iwe.u.qual.qual = sta->last_rx_signal < 27 ? + 0 : (sta->last_rx_signal - 27) * 92 / 127; + else + iwe.u.qual.qual = sta->last_rx_signal - + sta->last_rx_silence - 35; + iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); + iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); + iwe.u.qual.updated = sta->last_rx_updated; + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->ap) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = sta->u.ap.ssid_len; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, + sta->u.ap.ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (sta->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = + IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, + sta->u.ap.ssid + /* 0 byte memcpy */); + + if (sta->u.ap.channel > 0 && + sta->u.ap.channel <= FREQ_COUNT) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = freq_list[sta->u.ap.channel - 1] + * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event( + current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + } + +#if WIRELESS_EXT > 14 + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "beacon_interval=%d", + sta->listen_interval); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); +#endif /* WIRELESS_EXT > 14 */ + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + sta->last_rx_updated = 0; + + /* To be continued, we should make good use of IWEVCUSTOM */ + } + + spin_unlock_bh(&ap->sta_table_lock); + + return current_ev - buffer; +} + + +static int prism2_hostapd_add_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (sta == NULL) { + sta = ap_add_sta(ap, param->sta_addr); + if (sta == NULL) + return -1; + } + + if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_new_sta(sta->local->dev, sta); + + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->last_rx = jiffies; + sta->aid = param->u.add_sta.aid; + sta->capability = param->u.add_sta.capability; + sta->tx_supp_rates = param->u.add_sta.tx_supp_rates; + if (sta->tx_supp_rates & WLAN_RATE_1M) + sta->supported_rates[0] = 2; + if (sta->tx_supp_rates & WLAN_RATE_2M) + sta->supported_rates[1] = 4; + if (sta->tx_supp_rates & WLAN_RATE_5M5) + sta->supported_rates[2] = 11; + if (sta->tx_supp_rates & WLAN_RATE_11M) + sta->supported_rates[3] = 22; + prism2_check_tx_rates(sta); + atomic_dec(&sta->users); + return 0; +} + + +static int prism2_hostapd_remove_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + + return 0; +} + + +static int prism2_hostapd_get_info_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ; + + atomic_dec(&sta->users); + + return 1; +} + + +static int prism2_hostapd_set_flags_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + sta->flags |= param->u.set_flags_sta.flags_or; + sta->flags &= param->u.set_flags_sta.flags_and; + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + return 0; +} + + +static int prism2_hostapd_sta_clear_stats(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + int rate; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + sta->rx_packets = sta->tx_packets = 0; + sta->rx_bytes = sta->tx_bytes = 0; + for (rate = 0; rate < WLAN_RATE_COUNT; rate++) { + sta->tx_count[rate] = 0; + sta->rx_count[rate] = 0; + } + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + return 0; +} + + +static int prism2_hostapd(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + switch (param->cmd) { + case PRISM2_HOSTAPD_FLUSH: + ap_control_kickall(ap); + return 0; + case PRISM2_HOSTAPD_ADD_STA: + return prism2_hostapd_add_sta(ap, param); + case PRISM2_HOSTAPD_REMOVE_STA: + return prism2_hostapd_remove_sta(ap, param); + case PRISM2_HOSTAPD_GET_INFO_STA: + return prism2_hostapd_get_info_sta(ap, param); + case PRISM2_HOSTAPD_SET_FLAGS_STA: + return prism2_hostapd_set_flags_sta(ap, param); + case PRISM2_HOSTAPD_STA_CLEAR_STATS: + return prism2_hostapd_sta_clear_stats(ap, param); + default: + printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n", + param->cmd); + return -EOPNOTSUPP; + } +} + + +/* Update station info for host-based TX rate control and return current + * TX rate */ +static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev) +{ + int ret = sta->tx_rate; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + sta->tx_count[sta->tx_rate_idx]++; + sta->tx_since_last_failure++; + sta->tx_consecutive_exc = 0; + if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT && + sta->tx_rate_idx < sta->tx_max_rate) { + /* use next higher rate */ + int old_rate, new_rate; + old_rate = new_rate = sta->tx_rate_idx; + while (new_rate < sta->tx_max_rate) { + new_rate++; + if (ap_tx_rate_ok(new_rate, sta, local)) { + sta->tx_rate_idx = new_rate; + break; + } + } + if (old_rate != sta->tx_rate_idx) { + switch (sta->tx_rate_idx) { + case 0: sta->tx_rate = 10; break; + case 1: sta->tx_rate = 20; break; + case 2: sta->tx_rate = 55; break; + case 3: sta->tx_rate = 110; break; + default: sta->tx_rate = 0; break; + } + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate raised to" + " %d\n", dev->name, MAC2STR(sta->addr), + sta->tx_rate); + } + sta->tx_since_last_failure = 0; + } + + return ret; +} + + +/* Called only from software IRQ. Called for each TX frame prior possible + * encryption and transmit. */ +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx) +{ + struct sta_info *sta = NULL; + struct sk_buff *skb = tx->skb; + int set_tim, ret; + struct hostap_ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + + meta = (struct hostap_skb_tx_data *) skb->cb; + ret = AP_TX_CONTINUE; + if (local->ap == NULL || skb->len < 10 || + meta->iface->type == HOSTAP_INTERFACE_STA) + goto out; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + if (hdr->addr1[0] & 0x01) { + /* broadcast/multicast frame - no AP related processing */ + goto out; + } + + /* unicast packet - check whether destination STA is associated */ + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (local->iw_mode == IW_MODE_MASTER && sta == NULL && !meta->wds && + meta->iface->type != HOSTAP_INTERFACE_MASTER && + meta->iface->type != HOSTAP_INTERFACE_AP) { +#if 0 + /* This can happen, e.g., when wlan0 is added to a bridge and + * bridging code does not know which port is the correct target + * for a unicast frame. In this case, the packet is send to all + * ports of the bridge. Since this is a valid scenario, do not + * print out any errors here. */ + if (net_ratelimit()) { + printk(KERN_DEBUG "AP: drop packet to non-associated " + "STA " MACSTR "\n", MAC2STR(hdr->addr1)); + } +#endif + local->ap->tx_drop_nonassoc++; + ret = AP_TX_DROP; + goto out; + } + + if (sta == NULL) + goto out; + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + ret = AP_TX_CONTINUE_NOT_AUTHORIZED; + + /* Set tx_rate if using host-based TX rate control */ + if (!local->fw_tx_rate_control) + local->ap->last_tx_rate = meta->rate = + ap_update_sta_tx_rate(sta, local->dev); + + if (local->iw_mode != IW_MODE_MASTER) + goto out; + + if (!(sta->flags & WLAN_STA_PS)) + goto out; + + if (memcmp(skb->cb, AP_SKB_CB_MAGIC, AP_SKB_CB_MAGIC_LEN) == 0) { + if (skb->cb[AP_SKB_CB_MAGIC_LEN] & AP_SKB_CB_ADD_MOREDATA) { + /* indicate to STA that more frames follow */ + hdr->frame_control |= + __constant_cpu_to_le16(WLAN_FC_MOREDATA); + } + + if (skb->cb[AP_SKB_CB_MAGIC_LEN] & AP_SKB_CB_BUFFERED_FRAME) { + /* packet was already buffered and now send due to + * PS poll, so do not rebuffer it */ + goto out; + } + } + + if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) { + PDEBUG(DEBUG_PS, "%s: No more space in STA (" MACSTR ")'s PS " + "mode buffer\n", local->dev->name, MAC2STR(sta->addr)); + /* Make sure that TIM is set for the station (it might not be + * after AP wlan hw reset). */ + /* FIX: should fix hw reset to restore bits based on STA + * buffer state.. */ + hostap_set_tim(local, sta->aid, 1); + sta->flags |= WLAN_STA_TIM; + ret = AP_TX_DROP; + goto out; + } + + /* STA in PS mode, buffer frame for later delivery */ + set_tim = skb_queue_empty(&sta->tx_buf); + skb_queue_tail(&sta->tx_buf, skb); + /* FIX: could save RX time to skb and expire buffered frames after + * some time if STA does not poll for them */ + + if (set_tim) { + if (sta->flags & WLAN_STA_TIM) + PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n", + sta->aid); + hostap_set_tim(local, sta->aid, 1); + sta->flags |= WLAN_STA_TIM; + } + + ret = AP_TX_BUFFERED; + + out: + if (sta != NULL) { + if (ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) { + sta->tx_packets++; + sta->tx_bytes += skb->len; + sta->last_tx = jiffies; + } + + if ((ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) && + sta->crypt && tx->host_encrypt) { + tx->crypt = sta->crypt; + tx->sta_ptr = sta; /* hostap_handle_sta_release() will + * be called to release sta info + * later */ + } else + atomic_dec(&sta->users); + } + + return ret; +} + + +void hostap_handle_sta_release(void *ptr) +{ + struct sta_info *sta = ptr; + atomic_dec(&sta->users); +} + + +/* Called only as a tasklet (software IRQ) */ +void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb) +{ + struct sta_info *sta; + struct hostap_ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + meta = (struct hostap_skb_tx_data *) skb->cb; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr1); + if (!sta) { + spin_unlock(&local->ap->sta_table_lock); + PDEBUG(DEBUG_AP, "%s: Could not find STA " MACSTR " for this " + "TX error (@%lu)\n", + local->dev->name, MAC2STR(hdr->addr1), jiffies); + return; + } + + sta->tx_since_last_failure = 0; + sta->tx_consecutive_exc++; + + if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD && + sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) { + /* use next lower rate */ + int old, rate; + old = rate = sta->tx_rate_idx; + while (rate > 0) { + rate--; + if (ap_tx_rate_ok(rate, sta, local)) { + sta->tx_rate_idx = rate; + break; + } + } + if (old != sta->tx_rate_idx) { + switch (sta->tx_rate_idx) { + case 0: sta->tx_rate = 10; break; + case 1: sta->tx_rate = 20; break; + case 2: sta->tx_rate = 55; break; + case 3: sta->tx_rate = 110; break; + default: sta->tx_rate = 0; break; + } + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate lowered " + "to %d\n", local->dev->name, MAC2STR(sta->addr), + sta->tx_rate); + } + sta->tx_consecutive_exc = 0; + } + spin_unlock(&local->ap->sta_table_lock); +} + + +static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta, + int pwrmgt, int type, int stype) +{ + if (pwrmgt && !(sta->flags & WLAN_STA_PS)) { + sta->flags |= WLAN_STA_PS; + PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to use PS " + "mode (type=0x%02X, stype=0x%02X)\n", + MAC2STR(sta->addr), type, stype); + } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) { + sta->flags &= ~WLAN_STA_PS; + PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to not use " + "PS mode (type=0x%02X, stype=0x%02X)\n", + MAC2STR(sta->addr), type, stype); + if (type != WLAN_FC_TYPE_CTRL || stype != WLAN_FC_STYPE_PSPOLL) + schedule_packet_send(local, sta); + } +} + + +/* Called only as a tasklet (software IRQ). Called for each RX frame to update + * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */ +int hostap_update_sta_ps(local_info_t *local, struct hostap_ieee80211_hdr *hdr) +{ + struct sta_info *sta; + u16 fc; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (!sta) + return -1; + + fc = le16_to_cpu(hdr->frame_control); + hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT, + WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc)); + + atomic_dec(&sta->users); + return 0; +} + + +/* Called only as a tasklet (software IRQ). Called for each RX frame after + * getting RX header and payload from hardware. */ +ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, + struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, + int wds) +{ + int ret; + struct sta_info *sta; + u16 fc, type, stype; + struct hostap_ieee80211_hdr *hdr; + + if (local->ap == NULL) + return AP_RX_CONTINUE; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (sta && !(sta->flags & WLAN_STA_AUTHORIZED)) + ret = AP_RX_CONTINUE_NOT_AUTHORIZED; + else + ret = AP_RX_CONTINUE; + + + if (fc & WLAN_FC_TODS) { + if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NON_ASSOC); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + printk(KERN_DEBUG "%s: dropped received packet" + " from non-associated STA " MACSTR + " (type=0x%02x, subtype=0x%02x)\n", + dev->name, MAC2STR(hdr->addr2), type, + stype); + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } + } else if (fc & WLAN_FC_FROMDS) { + if (!wds) { + /* FromDS frame - not for us; probably + * broadcast/multicast in another BSS - drop */ + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + printk(KERN_DEBUG "Odd.. FromDS packet " + "received with own BSSID\n"); + hostap_dump_rx_80211(dev->name, skb, rx_stats); + } + ret = AP_RX_DROP; + goto out; + } + } else if (stype == WLAN_FC_STYPE_NULLFUNC && sta == NULL && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NON_ASSOC); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + /* At least Lucent f/w seems to send data::nullfunc + * frames with no ToDS flag when the current AP returns + * after being unavailable for some time. Speed up + * re-association by informing the station about it not + * being associated. */ + printk(KERN_DEBUG "%s: rejected received nullfunc " + "frame without ToDS from not associated STA " + MACSTR "\n", + dev->name, MAC2STR(hdr->addr2)); + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } else if (stype == WLAN_FC_STYPE_NULLFUNC) { + /* At least Lucent cards seem to send periodic nullfunc + * frames with ToDS. Let these through to update SQ + * stats and PS state. Nullfunc frames do not contain + * any data and they will be dropped below. */ + } else { + /* If BSSID (Addr3) is foreign, this frame is a normal + * broadcast frame from an IBSS network. Drop it silently. + * If BSSID is own, report the dropping of this frame. */ + if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + printk(KERN_DEBUG "%s: dropped received packet from " + MACSTR " with no ToDS flag (type=0x%02x, " + "subtype=0x%02x)\n", dev->name, + MAC2STR(hdr->addr2), type, stype); + hostap_dump_rx_80211(dev->name, skb, rx_stats); + } + ret = AP_RX_DROP; + goto out; + } + + if (sta) { + hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT, + type, stype); + + sta->rx_packets++; + sta->rx_bytes += skb->len; + sta->last_rx = jiffies; + } + + if (local->ap->nullfunc_ack && stype == WLAN_FC_STYPE_NULLFUNC && + fc & WLAN_FC_TODS) { + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NULLFUNC_ACK); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + /* some STA f/w's seem to require control::ACK frame + * for data::nullfunc, but Prism2 f/w 0.8.0 (at least + * from Compaq) does not send this.. Try to generate + * ACK for these frames from the host driver to make + * power saving work with, e.g., Lucent WaveLAN f/w */ + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } + + out: + if (sta) + atomic_dec(&sta->users); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_handle_sta_crypto(local_info_t *local, + struct hostap_ieee80211_hdr *hdr, + struct prism2_crypt_data **crypt, void **sta_ptr) +{ + struct sta_info *sta; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (!sta) + return -1; + + if (sta->crypt) { + *crypt = sta->crypt; + *sta_ptr = sta; + /* hostap_handle_sta_release() will be called to release STA + * info */ + } else + atomic_dec(&sta->users); + + return 0; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 0; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap) + ret = 1; + spin_unlock(&ap->sta_table_lock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 0; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap && + ((sta->flags & WLAN_STA_AUTHORIZED) || + ap->local->ieee_802_1x == 0)) + ret = 1; + spin_unlock(&ap->sta_table_lock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_add_sta(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 1; + + if (!ap) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta) + ret = 0; + spin_unlock(&ap->sta_table_lock); + + if (ret == 1) { + sta = ap_add_sta(ap, sta_addr); + if (!sta) + ret = -1; + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->ap = 1; + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + /* No way of knowing which rates are supported since we did not + * get supported rates element from beacon/assoc req. Assume + * that remote end supports all 802.11b rates. */ + sta->supported_rates[0] = 0x82; + sta->supported_rates[1] = 0x84; + sta->supported_rates[2] = 0x0b; + sta->supported_rates[3] = 0x16; + sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M | + WLAN_RATE_5M5 | WLAN_RATE_11M; + sta->tx_rate = 110; + sta->tx_max_rate = sta->tx_rate_idx = 3; + } + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_update_rx_stats(struct ap_data *ap, + struct hostap_ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats) +{ + struct sta_info *sta; + + if (!ap) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr2); + if (sta) { + sta->last_rx_silence = rx_stats->noise; + sta->last_rx_signal = rx_stats->signal; + sta->last_rx_rate = rx_stats->rate; + sta->last_rx_updated = 7; + if (rx_stats->rate == 10) + sta->rx_count[0]++; + else if (rx_stats->rate == 20) + sta->rx_count[1]++; + else if (rx_stats->rate == 55) + sta->rx_count[2]++; + else if (rx_stats->rate == 110) + sta->rx_count[3]++; + } + spin_unlock(&ap->sta_table_lock); + + return sta ? 0 : -1; +} + + +void hostap_update_rates(local_info_t *local) +{ + struct list_head *ptr; + struct ap_data *ap = local->ap; + + if (!ap) + return; + + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + prism2_check_tx_rates(sta); + } + spin_unlock_bh(&ap->sta_table_lock); +} + + +static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, + struct prism2_crypt_data ***crypt) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta && permanent) + sta = ap_add_sta(ap, addr); + + if (!sta) + return NULL; + + if (permanent) + sta->flags |= WLAN_STA_PERM; + + *crypt = &sta->crypt; + + return sta; +} + + +void hostap_add_wds_links(local_info_t *local) +{ + struct ap_data *ap = local->ap; + struct list_head *ptr; + + spin_lock_bh(&ap->sta_table_lock); + list_for_each(ptr, &ap->sta_list) { + struct sta_info *sta = list_entry(ptr, struct sta_info, list); + if (sta->ap) + hostap_wds_link_oper(local, sta->addr, WDS_ADD); + } + spin_unlock_bh(&ap->sta_table_lock); + + PRISM2_SCHEDULE_TASK(&local->ap->wds_oper_queue); +} + + +void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type) +{ + struct wds_oper_data *entry; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, addr, ETH_ALEN); + entry->type = type; + spin_lock_bh(&local->lock); + entry->next = local->ap->wds_oper_entries; + local->ap->wds_oper_entries = entry; + spin_unlock_bh(&local->lock); + + PRISM2_SCHEDULE_TASK(&local->ap->wds_oper_queue); +} + + +EXPORT_SYMBOL(hostap_init_data); +EXPORT_SYMBOL(hostap_init_ap_proc); +EXPORT_SYMBOL(hostap_free_data); +EXPORT_SYMBOL(hostap_check_sta_fw_version); +EXPORT_SYMBOL(hostap_handle_sta_tx); +EXPORT_SYMBOL(hostap_handle_sta_release); +EXPORT_SYMBOL(hostap_handle_sta_tx_exc); +EXPORT_SYMBOL(hostap_update_sta_ps); +EXPORT_SYMBOL(hostap_handle_sta_rx); +EXPORT_SYMBOL(hostap_is_sta_assoc); +EXPORT_SYMBOL(hostap_is_sta_authorized); +EXPORT_SYMBOL(hostap_add_sta); +EXPORT_SYMBOL(hostap_update_rates); +EXPORT_SYMBOL(hostap_add_wds_links); +EXPORT_SYMBOL(hostap_wds_link_oper); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +EXPORT_SYMBOL(hostap_deauth_all_stas); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_ap.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_ap.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_ap.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_ap.h 2004-07-17 19:34:23.000000000 -0400 @@ -0,0 +1,272 @@ +#ifndef HOSTAP_AP_H +#define HOSTAP_AP_H + +/* AP data structures for STAs */ + +/* maximum number of frames to buffer per STA */ +#define STA_MAX_TX_BUFFER 32 + +/* Flags used in skb->cb[6] to control how the packet is handled in TX path. + * skb->cb[0..5] must contain magic value 'hostap' to indicate that cb[6] is + * used. */ +#define AP_SKB_CB_MAGIC "hostap" +#define AP_SKB_CB_MAGIC_LEN 6 +#define AP_SKB_CB_BUFFERED_FRAME BIT(0) +#define AP_SKB_CB_ADD_MOREDATA BIT(1) + + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ +#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ +#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is + * controlling whether STA is authorized to + * send and receive non-IEEE 802.1X frames + */ +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) +#define WLAN_RATE_COUNT 4 + +/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, + * but some pre-standard IEEE 802.11g products use longer elements. */ +#define WLAN_SUPP_RATES_MAX 32 + +/* Try to increase TX rate after # successfully sent consecutive packets */ +#define WLAN_RATE_UPDATE_COUNT 50 + +/* Decrease TX rate after # consecutive dropped packets */ +#define WLAN_RATE_DECREASE_THRESHOLD 2 + +struct sta_info { + struct list_head list; + struct sta_info *hnext; /* next entry in hash table list */ + atomic_t users; /* number of users (do not remove if > 0) */ + struct proc_dir_entry *proc; + + u8 addr[6]; + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + + unsigned long last_auth; + unsigned long last_assoc; + unsigned long last_rx; + unsigned long last_tx; + unsigned long rx_packets, tx_packets; + unsigned long rx_bytes, tx_bytes; + struct sk_buff_head tx_buf; + /* FIX: timeout buffers with an expiry time somehow derived from + * listen_interval */ + + s8 last_rx_silence; /* Noise in dBm */ + s8 last_rx_signal; /* Signal strength in dBm */ + u8 last_rx_rate; /* TX rate in 0.1 Mbps */ + u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */ + + u8 tx_supp_rates; /* bit field of supported TX rates */ + u8 tx_rate; /* current TX rate (in 0.1 Mbps) */ + u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */ + u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */ + u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */ + u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate) + */ + u32 tx_since_last_failure; + u32 tx_consecutive_exc; + + struct prism2_crypt_data *crypt; + + int ap; /* whether this station is an AP */ + + local_info_t *local; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + union { + struct { + char *challenge; /* shared key authentication + * challenge */ + } sta; + struct { + int ssid_len; + unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */ + int channel; + unsigned long last_beacon; /* last RX beacon time */ + } ap; + } u; + + struct timer_list timer; + enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +#define MAX_STA_COUNT 1024 + +/* Maximum number of AIDs to use for STAs; must be 2007 or lower + * (8802.11 limitation) */ +#define MAX_AID_TABLE_SIZE 128 + +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC + * has passed since last received frame from the station, a nullfunc data + * frame is sent to the station. If this frame is not acknowledged and no other + * frames have been received, the station will be disassociated after + * AP_DISASSOC_DELAY. Similarily, a the station will be deauthenticated after + * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with + * max inactivity timer. */ +#define AP_MAX_INACTIVITY_SEC (5 * 60) +#define AP_DISASSOC_DELAY (HZ) +#define AP_DEAUTH_DELAY (HZ) + +/* ap_policy: whether to accept frames to/from other APs/IBSS */ +typedef enum { + AP_OTHER_AP_SKIP_ALL = 0, + AP_OTHER_AP_SAME_SSID = 1, + AP_OTHER_AP_ALL = 2, + AP_OTHER_AP_EVEN_IBSS = 3 +} ap_policy_enum; + +#define PRISM2_AUTH_OPEN BIT(0) +#define PRISM2_AUTH_SHARED_KEY BIT(1) + + +/* MAC address-based restrictions */ +struct mac_entry { + struct list_head list; + u8 addr[6]; +}; + +struct mac_restrictions { + enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy; + unsigned int entries; + struct list_head mac_list; + spinlock_t lock; +}; + + +struct add_sta_proc_data { + u8 addr[ETH_ALEN]; + struct add_sta_proc_data *next; +}; + + +typedef enum { WDS_ADD, WDS_DEL } wds_oper_type; +struct wds_oper_data { + wds_oper_type type; + u8 addr[ETH_ALEN]; + struct wds_oper_data *next; +}; + + +struct ap_data { + int initialized; /* whether ap_data has been initialized */ + local_info_t *local; + int bridge_packets; /* send packet to associated STAs directly to the + * wireless media instead of higher layers in the + * kernel */ + unsigned int bridged_unicast; /* number of unicast frames bridged on + * wireless media */ + unsigned int bridged_multicast; /* number of non-unicast frames + * bridged on wireless media */ + unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped + * because they were to an address that + * was not associated */ + int nullfunc_ack; /* use workaround for nullfunc frame ACKs */ + + spinlock_t sta_table_lock; + int num_sta; /* number of entries in sta_list */ + struct list_head sta_list; /* STA info list head */ + struct sta_info *sta_hash[STA_HASH_SIZE]; + + struct proc_dir_entry *proc; + + ap_policy_enum ap_policy; + unsigned int max_inactivity; + int autom_ap_wds; + + struct mac_restrictions mac_restrictions; /* MAC-based auth */ + int last_tx_rate; + + HOSTAP_QUEUE add_sta_proc_queue; + struct add_sta_proc_data *add_sta_proc_entries; + + HOSTAP_QUEUE wds_oper_queue; + struct wds_oper_data *wds_oper_entries; + + u16 tx_callback_idx; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + /* pointers to STA info; based on allocated AID or NULL if AID free + * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 + * and so on + */ + struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; + + u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll; + + /* WEP operations for generating challenges to be used with shared key + * authentication */ + struct hostap_crypto_ops *crypt; + void *crypt_priv; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +void hostap_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); +void hostap_init_data(local_info_t *local); +void hostap_init_ap_proc(local_info_t *local); +void hostap_free_data(struct ap_data *ap); +void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver); + +typedef enum { + AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED, + AP_TX_CONTINUE_NOT_AUTHORIZED +} ap_tx_ret; +struct hostap_tx_data { + struct sk_buff *skb; + int host_encrypt; + struct prism2_crypt_data *crypt; + void *sta_ptr; +}; +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx); +void hostap_handle_sta_release(void *ptr); +void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb); +int hostap_update_sta_ps(local_info_t *local, + struct hostap_ieee80211_hdr *hdr); +typedef enum { + AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED +} ap_rx_ret; +ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, + struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, + int wds); +int hostap_handle_sta_crypto(local_info_t *local, + struct hostap_ieee80211_hdr *hdr, + struct prism2_crypt_data **crypt, void **sta_ptr); +int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr); +int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr); +int hostap_add_sta(struct ap_data *ap, u8 *sta_addr); +int hostap_update_rx_stats(struct ap_data *ap, + struct hostap_ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats); +void hostap_update_rates(local_info_t *local); +void hostap_add_wds_links(local_info_t *local); +void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, + int resend); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +#endif /* HOSTAP_AP_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap.c 2004-08-27 23:26:46.000000000 -0400 @@ -0,0 +1,1220 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include +#include +#include "hostap_wext.h" +#include + +#include "hostap_wlan.h" +#include "hostap_80211.h" +#include "hostap_ap.h" +#include "hostap.h" +#include "hostap_crypt.h" + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP common routines"); +MODULE_LICENSE("GPL"); + +#ifndef HOSTAP_CRYPT_MODULE +/* Old hostap_crypt module is now part of hostap module. */ +#include "hostap_crypt.c" +#else /* HOSTAP_CRYPT_MODULE */ +#define hostap_crypto_init() +#define hostap_crypto_deinit() +#endif /* HOSTAP_CRYPT_MODULE */ + +#define TX_TIMEOUT (2 * HZ) + +#define PRISM2_MAX_FRAME_SIZE 2304 +#define PRISM2_MIN_MTU 256 +/* FIX: */ +#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */)) + + +/* hostap.c */ +static int prism2_wds_add(local_info_t *local, u8 *remote_addr, + int rtnl_locked); +static int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove); + +/* hostap_ap.c */ +static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist); +static int prism2_ap_translate_scan(struct net_device *dev, char *buffer); +static int prism2_hostapd(struct ap_data *ap, + struct prism2_hostapd_param *param); +static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, + struct prism2_crypt_data ***crypt); +static void ap_control_kickall(struct ap_data *ap); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static int ap_control_add_mac(struct mac_restrictions *mac_restrictions, + u8 *mac); +static int ap_control_del_mac(struct mac_restrictions *mac_restrictions, + u8 *mac); +static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions); +static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, + u8 *mac); +#endif /* !PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; +#define FREQ_COUNT (sizeof(freq_list) / sizeof(freq_list[0])) + + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char rfc1042_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char bridge_tunnel_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + + +/* FIX: these could be compiled separately and linked together to hostap.o */ +#include "hostap_ap.c" +#include "hostap_info.c" +#include "hostap_ioctl.c" +#include "hostap_proc.c" +#include "hostap_80211_rx.c" +#include "hostap_80211_tx.c" + + +struct net_device * hostap_add_interface(struct local_info *local, + int type, int rtnl_locked, + const char *prefix, + const char *name) +{ + struct net_device *dev, *mdev; + struct hostap_interface *iface; + int ret; + + dev = alloc_etherdev(sizeof(struct hostap_interface)); + if (dev == NULL) + return NULL; + + iface = dev->priv; + iface->dev = dev; + iface->local = local; + iface->type = type; + list_add(&iface->list, &local->hostap_interfaces); + + mdev = local->dev; + memcpy(dev->dev_addr, mdev->dev_addr, ETH_ALEN); + dev->base_addr = mdev->base_addr; + dev->irq = mdev->irq; + dev->mem_start = mdev->mem_start; + dev->mem_end = mdev->mem_end; + + hostap_setup_dev(dev, local, 0); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) + dev->destructor = free_netdev; +#else + /* Linux 2.4.x uses dev entry (reads dev->features) after + * dev->destructor has returned, so must not free struct net_device + * here. Set NETIF_F_DYNALLOC flag so that netdev_finish_unregister() + * frees the entry. */ + dev->features |= NETIF_F_DYNALLOC; +#endif + + sprintf(dev->name, "%s%s", prefix, name); + if (!rtnl_locked) + rtnl_lock(); + + ret = 0; + if (strchr(dev->name, '%')) + ret = dev_alloc_name(dev, dev->name); + + if (ret >= 0) + ret = register_netdevice(dev); + + if (!rtnl_locked) + rtnl_unlock(); + + if (ret < 0) { + printk(KERN_WARNING "%s: failed to add new netdevice!\n", + dev->name); + free_netdev(dev); + return NULL; + } + + printk(KERN_DEBUG "%s: registered netdevice %s\n", + mdev->name, dev->name); + + return dev; +} + + +void hostap_remove_interface(struct net_device *dev, int rtnl_locked, + int remove_from_list) +{ + struct hostap_interface *iface; + + if (!dev) + return; + + iface = dev->priv; + + if (remove_from_list) { + list_del(&iface->list); + } + + if (dev == iface->local->ddev) + iface->local->ddev = NULL; + else if (dev == iface->local->apdev) + iface->local->apdev = NULL; + else if (dev == iface->local->stadev) + iface->local->stadev = NULL; + + if (rtnl_locked) + unregister_netdevice(dev); + else + unregister_netdev(dev); + + /* dev->destructor = free_netdev() will free the device data, including + * private data, when removing the device */ +} + + +static inline int prism2_wds_special_addr(u8 *addr) +{ + if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5]) + return 0; + + return 1; +} + + +static int prism2_wds_add(local_info_t *local, u8 *remote_addr, + int rtnl_locked) +{ + struct net_device *dev; + struct list_head *ptr; + struct hostap_interface *iface, *empty, *match; + + empty = match = NULL; + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + + if (prism2_wds_special_addr(iface->u.wds.remote_addr)) + empty = iface; + else if (memcmp(iface->u.wds.remote_addr, remote_addr, + ETH_ALEN) == 0) { + match = iface; + break; + } + } + if (!match && empty && !prism2_wds_special_addr(remote_addr)) { + /* take pre-allocated entry into use */ + memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN); + read_unlock_bh(&local->iface_lock); + printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n", + local->dev->name, empty->dev->name); + return 0; + } + read_unlock_bh(&local->iface_lock); + + if (!prism2_wds_special_addr(remote_addr)) { + if (match) + return -EEXIST; + hostap_add_sta(local->ap, remote_addr); + } + + if (local->wds_connections >= local->wds_max_connections) + return -ENOBUFS; + + /* verify that there is room for wds# postfix in the interface name */ + if (strlen(local->dev->name) > IFNAMSIZ - 5) { + printk(KERN_DEBUG "'%s' too long base device name\n", + local->dev->name); + return -EINVAL; + } + + dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked, + local->ddev->name, "wds%d"); + if (dev == NULL) + return -ENOMEM; + + iface = dev->priv; + memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN); + + local->wds_connections++; + + return 0; +} + + +static int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_interface *iface, *selected = NULL; + + write_lock_irqsave(&local->iface_lock, flags); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + + if (memcmp(iface->u.wds.remote_addr, remote_addr, + ETH_ALEN) == 0) { + selected = iface; + break; + } + } + if (selected && !do_not_remove) + list_del(&selected->list); + write_unlock_irqrestore(&local->iface_lock, flags); + + if (selected) { + if (do_not_remove) + memset(selected->u.wds.remote_addr, 0, ETH_ALEN); + else { + hostap_remove_interface(selected->dev, rtnl_locked, 0); + local->wds_connections--; + } + } + + return selected ? 0 : -ENODEV; +} + + +u16 hostap_tx_callback_register(local_info_t *local, + void (*func)(struct sk_buff *, int ok, void *), + void *data) +{ + unsigned long flags; + struct hostap_tx_callback_info *entry; + + entry = (struct hostap_tx_callback_info *) kmalloc(sizeof(*entry), + GFP_ATOMIC); + if (entry == NULL) + return 0; + + entry->func = func; + entry->data = data; + + spin_lock_irqsave(&local->lock, flags); + entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1; + entry->next = local->tx_callback; + local->tx_callback = entry; + spin_unlock_irqrestore(&local->lock, flags); + + return entry->idx; +} + + +int hostap_tx_callback_unregister(local_info_t *local, u16 idx) +{ + unsigned long flags; + struct hostap_tx_callback_info *cb, *prev = NULL; + + spin_lock_irqsave(&local->lock, flags); + cb = local->tx_callback; + while (cb != NULL && cb->idx != idx) { + prev = cb; + cb = cb->next; + } + if (cb) { + if (prev == NULL) + local->tx_callback = cb->next; + else + prev->next = cb->next; + kfree(cb); + } + spin_unlock_irqrestore(&local->lock, flags); + + return cb ? 0 : -1; +} + + +/* val is in host byte order */ +int hostap_set_word(struct net_device *dev, int rid, u16 val) +{ + struct hostap_interface *iface = dev->priv; + u16 tmp = cpu_to_le16(val); + return iface->local->func->set_rid(dev, rid, &tmp, 2); +} + + +int hostap_set_string(struct net_device *dev, int rid, const char *val) +{ + struct hostap_interface *iface = dev->priv; + char buf[MAX_SSID_LEN + 2]; + int len; + + len = strlen(val); + if (len > MAX_SSID_LEN) + return -1; + memset(buf, 0, sizeof(buf)); + buf[0] = len; /* little endian 16 bit word */ + memcpy(buf + 2, val, len); + + return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2); +} + + +u16 hostap_get_porttype(local_info_t *local) +{ + if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc) + return HFA384X_PORTTYPE_PSEUDO_IBSS; + if (local->iw_mode == IW_MODE_ADHOC) + return HFA384X_PORTTYPE_IBSS; + if (local->iw_mode == IW_MODE_INFRA) + return HFA384X_PORTTYPE_BSS; + if (local->iw_mode == IW_MODE_REPEAT) + return HFA384X_PORTTYPE_WDS; + if (local->iw_mode == IW_MODE_MONITOR) + return HFA384X_PORTTYPE_PSEUDO_IBSS; + return HFA384X_PORTTYPE_HOSTAP; +} + + +int hostap_set_encryption(local_info_t *local) +{ + u16 val, old_val; + int i, keylen, len, idx; + char keybuf[WEP_KEY_LEN + 1]; + enum { NONE, WEP, OTHER } encrypt_type; + + idx = local->tx_keyidx; + if (local->crypt[idx] == NULL || local->crypt[idx]->ops == NULL) + encrypt_type = NONE; + else if (strcmp(local->crypt[idx]->ops->name, "WEP") == 0) + encrypt_type = WEP; + else + encrypt_type = OTHER; + + if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, + 1) < 0) { + printk(KERN_DEBUG "Could not read current WEP flags.\n"); + goto fail; + } + le16_to_cpus(&val); + old_val = val; + + if (encrypt_type != NONE || local->privacy_invoked) + val |= HFA384X_WEPFLAGS_PRIVACYINVOKED; + else + val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED; + + if (local->open_wep || encrypt_type == NONE || + ((local->ieee_802_1x || local->wpa) && local->host_decrypt)) + val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + else + val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + + if ((encrypt_type != NONE || local->privacy_invoked) && + (encrypt_type == OTHER || local->host_encrypt)) + val |= HFA384X_WEPFLAGS_HOSTENCRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT; + if ((encrypt_type != NONE || local->privacy_invoked) && + (encrypt_type == OTHER || local->host_decrypt)) + val |= HFA384X_WEPFLAGS_HOSTDECRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT; + + if (val != old_val && + hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) { + printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n", + val); + goto fail; + } + + if (encrypt_type != WEP) + return 0; + + /* 104-bit support seems to require that all the keys are set to the + * same keylen */ + keylen = 6; /* first 5 octets */ + len = local->crypt[idx]->ops->get_key(keybuf, sizeof(keybuf), + NULL, local->crypt[idx]->priv); + if (idx >= 0 && idx < WEP_KEYS && len > 5) + keylen = WEP_KEY_LEN + 1; /* first 13 octets */ + + for (i = 0; i < WEP_KEYS; i++) { + memset(keybuf, 0, sizeof(keybuf)); + if (local->crypt[i]) { + (void) local->crypt[i]->ops->get_key( + keybuf, sizeof(keybuf), + NULL, local->crypt[i]->priv); + } + if (local->func->set_rid(local->dev, + HFA384X_RID_CNFDEFAULTKEY0 + i, + keybuf, keylen)) { + printk(KERN_DEBUG "Could not set key %d (len=%d)\n", + i, keylen); + goto fail; + } + } + if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) { + printk(KERN_DEBUG "Could not set default keyid %d\n", idx); + goto fail; + } + + return 0; + + fail: + printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name); + return -1; +} + + +int hostap_set_antsel(local_info_t *local) +{ + u16 val; + int ret = 0; + + if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH && + local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_TX_CONFIGURE, + NULL, &val) == 0) { + val &= ~(BIT(2) | BIT(1)); + switch (local->antsel_tx) { + case HOSTAP_ANTSEL_DIVERSITY: + val |= BIT(1); + break; + case HOSTAP_ANTSEL_LOW: + break; + case HOSTAP_ANTSEL_HIGH: + val |= BIT(2); + break; + } + + if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_TX_CONFIGURE, &val, NULL)) { + printk(KERN_INFO "%s: setting TX AntSel failed\n", + local->dev->name); + ret = -1; + } + } + + if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH && + local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_RX_CONFIGURE, + NULL, &val) == 0) { + val &= ~(BIT(1) | BIT(0)); + switch (local->antsel_rx) { + case HOSTAP_ANTSEL_DIVERSITY: + break; + case HOSTAP_ANTSEL_LOW: + val |= BIT(0); + break; + case HOSTAP_ANTSEL_HIGH: + val |= BIT(0) | BIT(1); + break; + } + + if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_RX_CONFIGURE, &val, NULL)) { + printk(KERN_INFO "%s: setting RX AntSel failed\n", + local->dev->name); + ret = -1; + } + } + + return ret; +} + + +int hostap_set_roaming(local_info_t *local) +{ + u16 val; + + switch (local->host_roaming) { + case 1: + val = HFA384X_ROAMING_HOST; + break; + case 2: + val = HFA384X_ROAMING_DISABLED; + break; + case 0: + default: + val = HFA384X_ROAMING_FIRMWARE; + break; + } + + return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val); +} + + +int hostap_set_auth_algs(local_info_t *local) +{ + int val = local->auth_algs; + /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication + * set to include both Open and Shared Key flags. It tries to use + * Shared Key authentication in that case even if WEP keys are not + * configured.. STA f/w v0.7.6 is able to handle such configuration, + * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */ + if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) && + val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY) + val = PRISM2_AUTH_OPEN; + + if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) { + printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x " + "failed\n", local->dev->name, local->auth_algs); + return -EINVAL; + } + + return 0; +} + + +void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx) +{ + u16 status, fc; + + status = __le16_to_cpu(rx->status); + + printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, " + "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; " + "jiffies=%ld\n", + name, status, (status >> 8) & 0x07, status >> 13, status & 1, + rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies); + + fc = __le16_to_cpu(rx->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " + "data_len=%d%s%s\n", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl), + __le16_to_cpu(rx->data_len), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4=" + MACSTR "\n", + MAC2STR(rx->addr1), MAC2STR(rx->addr2), MAC2STR(rx->addr3), + MAC2STR(rx->addr4)); + + printk(KERN_DEBUG " dst=" MACSTR " src=" MACSTR " len=%d\n", + MAC2STR(rx->dst_addr), MAC2STR(rx->src_addr), + __be16_to_cpu(rx->len)); +} + + +void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) +{ + u16 fc; + + printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d " + "tx_control=0x%04x; jiffies=%ld\n", + name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate, + __le16_to_cpu(tx->tx_control), jiffies); + + fc = __le16_to_cpu(tx->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " + "data_len=%d%s%s\n", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl), + __le16_to_cpu(tx->data_len), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4=" + MACSTR "\n", + MAC2STR(tx->addr1), MAC2STR(tx->addr2), MAC2STR(tx->addr3), + MAC2STR(tx->addr4)); + + printk(KERN_DEBUG " dst=" MACSTR " src=" MACSTR " len=%d\n", + MAC2STR(tx->dst_addr), MAC2STR(tx->src_addr), + __be16_to_cpu(tx->len)); +} + + +int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */ + return ETH_ALEN; +} + + +int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr) +{ + if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) { + memcpy(haddr, skb->mac.raw + + sizeof(struct linux_wlan_ng_prism_hdr) + 10, + ETH_ALEN); /* addr2 */ + } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */ + memcpy(haddr, skb->mac.raw + + sizeof(struct linux_wlan_ng_cap_hdr) + 10, + ETH_ALEN); /* addr2 */ + } + return ETH_ALEN; +} + + +int hostap_80211_get_hdrlen(u16 fc) +{ + int hdrlen = 24; + + switch (WLAN_FC_GET_TYPE(fc)) { + case WLAN_FC_TYPE_DATA: + if ((fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS)) + hdrlen = 30; /* Addr4 */ + break; + case WLAN_FC_TYPE_CTRL: + switch (WLAN_FC_GET_STYPE(fc)) { + case WLAN_FC_STYPE_CTS: + case WLAN_FC_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + break; + } + + return hdrlen; +} + + +struct net_device_stats *hostap_get_stats(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + return &iface->stats; +} + + +static int prism2_close(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name); + + if (dev == local->ddev) { + prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); + } +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && dev == local->dev && + (!local->func->card_present || local->func->card_present(local)) && + local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER) + hostap_deauth_all_stas(dev, local->ap, 1); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (local->func->dev_close && local->func->dev_close(local)) + return 0; + + if (dev == local->dev) { + local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL); + } + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + flush_scheduled_work(); + +#ifdef NEW_MODULE_CODE + module_put(local->hw_module); +#elif MODULE + __MOD_DEC_USE_COUNT(local->hw_module); +#endif + + local->num_dev_open--; + + if (dev != local->dev && local->dev->flags & IFF_UP && + local->master_dev_auto_open && local->num_dev_open == 1) { + /* Close master radio interface automatically if it was also + * opened automatically and we are now closing the last + * remaining non-master device. */ + dev_close(local->dev); + } + + return 0; +} + + +static int prism2_open(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name); + + if (local->no_pri) { + printk(KERN_DEBUG "%s: could not set interface UP - no PRI " + "f/w\n", dev->name); + return 1; + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + if (local->func->dev_open && local->func->dev_open(local)) + return 1; + +#ifdef NEW_MODULE_CODE + if (!try_module_get(local->hw_module)) + return -ENODEV; +#elif MODULE + __MOD_INC_USE_COUNT(local->hw_module); +#endif + local->num_dev_open++; + + if (!local->dev_enabled && local->func->hw_enable(dev, 1)) { + printk(KERN_WARNING "%s: could not enable MAC port\n", + dev->name); + prism2_close(dev); + return 1; + } + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + + if (dev != local->dev && !(local->dev->flags & IFF_UP)) { + /* Master radio interface is needed for all operation, so open + * it automatically when any virtual net_device is opened. */ + local->master_dev_auto_open = 1; + dev_open(local->dev); + } + + netif_device_attach(dev); + netif_start_queue(dev); + + return 0; +} + + +#ifdef HAVE_SET_MAC_ADDR +static int prism2_set_mac_address(struct net_device *dev, void *p) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct list_head *ptr; + struct sockaddr *addr = p; + + if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data, + ETH_ALEN) < 0 || local->func->reset_port(dev)) + return -EINVAL; + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN); + } + memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN); + read_unlock_bh(&local->iface_lock); + + return 0; +} +#endif /* HAVE_SET_MAC_ADDR */ + + +/* TODO: to be further implemented as soon as Prism2 fully supports + * GroupAddresses and correct documentation is available */ +void hostap_set_multicast_list_queue(void *data) +{ + struct net_device *dev = (struct net_device *) data; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc)) { + printk(KERN_INFO "%s: %sabling promiscuous mode failed\n", + dev->name, local->is_promisc ? "en" : "dis"); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +static void hostap_set_multicast_list(struct net_device *dev) +{ +#if 0 + /* FIX: promiscuous mode seems to be causing a lot of problems with + * some station firmware versions (FCSErr frames, invalid MACPort, etc. + * corrupted incoming frames). This code is now commented out while the + * problems are investigated. */ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) { + local->is_promisc = 1; + } else { + local->is_promisc = 0; + } + + PRISM2_SCHEDULE_TASK(&local->set_multicast_list_queue); +#endif +} + + +static int prism2_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + + +#ifdef HAVE_TX_TIMEOUT +static void prism2_tx_timeout(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hfa384x_regs regs; + + printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name); + netif_stop_queue(local->dev); + + local->func->read_regs(dev, ®s); + printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x " + "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n", + dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1, + regs.swsupport0); + + local->func->schedule_reset(local); +} +#endif /* HAVE_TX_TIMEOUT */ + + +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int main_dev) +{ + struct hostap_interface *iface = dev->priv; + + ether_setup(dev); + + /* kernel callbacks */ + dev->get_stats = hostap_get_stats; +#if WIRELESS_EXT > 16 + if (iface) { + /* Currently, we point to the proper spy_data only on + * the main_dev. This could be fixed. Jean II */ + iface->wireless_data.spy_data = &iface->spy_data; + dev->wireless_data = &iface->wireless_data; + } +#else /* WIRELESS_EXT > 16 */ + if (main_dev || (iface && iface->type == HOSTAP_INTERFACE_MAIN)) + dev->get_wireless_stats = hostap_get_wireless_stats; +#endif /* WIRELESS_EXT > 16 */ + dev->wireless_handlers = + (struct iw_handler_def *) &hostap_iw_handler_def; + dev->do_ioctl = hostap_ioctl; + dev->open = prism2_open; + dev->stop = prism2_close; + dev->hard_start_xmit = hostap_data_start_xmit; + dev->set_mac_address = prism2_set_mac_address; + dev->set_multicast_list = hostap_set_multicast_list; + dev->change_mtu = prism2_change_mtu; + dev->tx_timeout = prism2_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->mtu = local->mtu; + if (!main_dev) { + /* use main radio device queue */ + dev->tx_queue_len = 0; + } + + netif_stop_queue(dev); +} + + +static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + if (local->apdev) + return -EEXIST; + + printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name); + + local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP, + rtnl_locked, local->ddev->name, + "ap"); + if (local->apdev == NULL) + return -ENOMEM; + + local->apdev->hard_start_xmit = hostap_mgmt_start_xmit; + local->apdev->type = ARPHRD_IEEE80211; + local->apdev->hard_header_parse = hostap_80211_header_parse; + + return 0; +} + + +static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); + + hostap_remove_interface(local->apdev, rtnl_locked, 1); + local->apdev = NULL; + + return 0; +} + + +static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + if (local->stadev) + return -EEXIST; + + printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name); + + local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA, + rtnl_locked, local->ddev->name, + "sta"); + if (local->stadev == NULL) + return -ENOMEM; + + return 0; +} + + +static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); + + hostap_remove_interface(local->stadev, rtnl_locked, 1); + local->stadev = NULL; + + return 0; +} + + +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked) +{ + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd == val) + return 0; + + if (val) { + ret = hostap_enable_hostapd(local, rtnl_locked); + if (ret == 0) + local->hostapd = 1; + } else { + local->hostapd = 0; + ret = hostap_disable_hostapd(local, rtnl_locked); + if (ret != 0) + local->hostapd = 1; + } + + return ret; +} + + +int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked) +{ + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd_sta == val) + return 0; + + if (val) { + ret = hostap_enable_hostapd_sta(local, rtnl_locked); + if (ret == 0) + local->hostapd_sta = 1; + } else { + local->hostapd_sta = 0; + ret = hostap_disable_hostapd_sta(local, rtnl_locked); + if (ret != 0) + local->hostapd_sta = 1; + } + + + return ret; +} + + +int prism2_update_comms_qual(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret = 0; + struct hfa384x_comms_quality sq; + + if (!local->sta_fw_ver) + ret = -1; + else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + if (local->func->get_rid(local->dev, + HFA384X_RID_DBMCOMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->comms_qual = (s16) le16_to_cpu(sq.comm_qual); + local->avg_signal = (s16) le16_to_cpu(sq.signal_level); + local->avg_noise = (s16) le16_to_cpu(sq.noise_level); + local->last_comms_qual_update = jiffies; + } else + ret = -1; + } else { + if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->comms_qual = le16_to_cpu(sq.comm_qual); + local->avg_signal = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.signal_level)); + local->avg_noise = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.noise_level)); + local->last_comms_qual_update = jiffies; + } else + ret = -1; + } + + return ret; +} + + +int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u8 stype, + u8 *body, size_t bodylen) +{ + struct sk_buff *skb; + struct hostap_ieee80211_mgmt *mgmt; + struct hostap_skb_tx_data *meta; + struct net_device *dev = local->dev; + + skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen); + if (skb == NULL) + return -ENOMEM; + + mgmt = (struct hostap_ieee80211_mgmt *) + skb_put(skb, IEEE80211_MGMT_HDR_LEN); + memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN); + mgmt->frame_control = + cpu_to_le16((WLAN_FC_TYPE_MGMT << 2) | (stype << 4)); + memcpy(mgmt->da, dst, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, dst, ETH_ALEN); + if (body) + memcpy(skb_put(skb, bodylen), body, bodylen); + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = dev->priv; + + skb->dev = dev; + skb->mac.raw = skb->nh.raw = skb->data; + dev_queue_xmit(skb); + + return 0; +} + + +int prism2_sta_deauth(local_info_t *local, u16 reason) +{ + union iwreq_data wrqu; + int ret; + + if (local->iw_mode != IW_MODE_INFRA || + memcmp(local->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0 || + memcmp(local->bssid, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0) + return 0; + + reason = cpu_to_le16(reason); + ret = prism2_sta_send_mgmt(local, local->bssid, WLAN_FC_STYPE_DEAUTH, + (u8 *) &reason, 2); + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + return ret; +} + + +struct proc_dir_entry *hostap_proc; + +static int __init hostap_init(void) +{ + hostap_crypto_init(); + + if (proc_net != NULL) { + hostap_proc = proc_mkdir("hostap", proc_net); + if (!hostap_proc) + printk(KERN_WARNING "Failed to mkdir " + "/proc/net/hostap\n"); + } else + hostap_proc = NULL; + + return 0; +} + + +static void __exit hostap_exit(void) +{ + if (hostap_proc != NULL) { + hostap_proc = NULL; + remove_proc_entry("hostap", proc_net); + } + + hostap_crypto_deinit(); +} + + +EXPORT_SYMBOL(hostap_set_word); +EXPORT_SYMBOL(hostap_set_string); +EXPORT_SYMBOL(hostap_get_porttype); +EXPORT_SYMBOL(hostap_set_encryption); +EXPORT_SYMBOL(hostap_set_antsel); +EXPORT_SYMBOL(hostap_set_roaming); +EXPORT_SYMBOL(hostap_set_auth_algs); +EXPORT_SYMBOL(hostap_dump_rx_header); +EXPORT_SYMBOL(hostap_dump_tx_header); +EXPORT_SYMBOL(hostap_80211_header_parse); +EXPORT_SYMBOL(hostap_80211_prism_header_parse); +EXPORT_SYMBOL(hostap_80211_get_hdrlen); +EXPORT_SYMBOL(hostap_get_stats); +EXPORT_SYMBOL(hostap_setup_dev); +EXPORT_SYMBOL(hostap_proc); +EXPORT_SYMBOL(hostap_set_multicast_list_queue); +EXPORT_SYMBOL(hostap_set_hostapd); +EXPORT_SYMBOL(hostap_set_hostapd_sta); +EXPORT_SYMBOL(hostap_add_interface); +EXPORT_SYMBOL(hostap_remove_interface); +EXPORT_SYMBOL(prism2_update_comms_qual); + +module_init(hostap_init); +module_exit(hostap_exit); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_common.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_common.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_common.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_common.h 2005-01-23 21:52:00.000000000 -0500 @@ -0,0 +1,557 @@ +#ifndef HOSTAP_COMMON_H +#define HOSTAP_COMMON_H + +#define BIT(x) (1 << (x)) + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + + + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER (BIT(1) | BIT(0)) +#define WLAN_FC_TODS BIT(8) +#define WLAN_FC_FROMDS BIT(9) +#define WLAN_FC_MOREFRAG BIT(10) +#define WLAN_FC_RETRY BIT(11) +#define WLAN_FC_PWRMGT BIT(12) +#define WLAN_FC_MOREDATA BIT(13) +#define WLAN_FC_ISWEP BIT(14) +#define WLAN_FC_ORDER BIT(15) + +#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) +#define WLAN_FC_GET_STYPE(fc) \ + (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_CHALLENGE 16 +#define WLAN_EID_RSN 48 +#define WLAN_EID_GENERIC 221 + + +/* HFA384X Configuration RIDs */ +#define HFA384X_RID_CNFPORTTYPE 0xFC00 +#define HFA384X_RID_CNFOWNMACADDR 0xFC01 +#define HFA384X_RID_CNFDESIREDSSID 0xFC02 +#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 +#define HFA384X_RID_CNFOWNSSID 0xFC04 +#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 +#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 +#define HFA384X_RID_CNFMAXDATALEN 0xFC07 +#define HFA384X_RID_CNFWDSADDRESS 0xFC08 +#define HFA384X_RID_CNFPMENABLED 0xFC09 +#define HFA384X_RID_CNFPMEPS 0xFC0A +#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HFA384X_RID_CNFOWNNAME 0xFC0E +#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ +#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ +#define HFA384X_RID_UNKNOWN1 0xFC20 +#define HFA384X_RID_UNKNOWN2 0xFC21 +#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 +#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 +#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 +#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 +#define HFA384X_RID_CNFWEPFLAGS 0xFC28 +#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A +#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ +#define HFA384X_RID_CNFTXCONTROL 0xFC2C +#define HFA384X_RID_CNFROAMINGMODE 0xFC2D +#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ +#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 +#define HFA384X_RID_CNFMMLIFE 0xFC31 +#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 +#define HFA384X_RID_CNFBEACONINT 0xFC33 +#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ +#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 +#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HFA384X_RID_CNFTIMCTRL 0xFC40 +#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ +#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ +#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ +#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; + * write only */ +#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_GROUPADDRESSES 0xFC80 +#define HFA384X_RID_CREATEIBSS 0xFC81 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 +#define HFA384X_RID_RTSTHRESHOLD 0xFC83 +#define HFA384X_RID_TXRATECONTROL 0xFC84 +#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ +#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HFA384X_RID_CNFBASICRATES 0xFCB3 +#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ +#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ +#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ +#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ +#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ +#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_TICKTIME 0xFCE0 +#define HFA384X_RID_SCANREQUEST 0xFCE1 +#define HFA384X_RID_JOINREQUEST 0xFCE2 +#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ +#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ +#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ + +/* HFA384X Information RIDs */ +#define HFA384X_RID_MAXLOADTIME 0xFD00 +#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 +#define HFA384X_RID_PRIID 0xFD02 +#define HFA384X_RID_PRISUPRANGE 0xFD03 +#define HFA384X_RID_CFIACTRANGES 0xFD04 +#define HFA384X_RID_NICSERNUM 0xFD0A +#define HFA384X_RID_NICID 0xFD0B +#define HFA384X_RID_MFISUPRANGE 0xFD0C +#define HFA384X_RID_CFISUPRANGE 0xFD0D +#define HFA384X_RID_CHANNELLIST 0xFD10 +#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 +#define HFA384X_RID_TEMPTYPE 0xFD12 +#define HFA384X_RID_CIS 0xFD13 +#define HFA384X_RID_STAID 0xFD20 +#define HFA384X_RID_STASUPRANGE 0xFD21 +#define HFA384X_RID_MFIACTRANGES 0xFD22 +#define HFA384X_RID_CFIACTRANGES2 0xFD23 +#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; + * only Prism2.5(?) */ +#define HFA384X_RID_PORTSTATUS 0xFD40 +#define HFA384X_RID_CURRENTSSID 0xFD41 +#define HFA384X_RID_CURRENTBSSID 0xFD42 +#define HFA384X_RID_COMMSQUALITY 0xFD43 +#define HFA384X_RID_CURRENTTXRATE 0xFD44 +#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 +#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 +#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 +#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B +#define HFA384X_RID_CFPOLLABLE 0xFD4C +#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ +#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ +#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ +#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ +#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_PHYTYPE 0xFDC0 +#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 +#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 +#define HFA384X_RID_CCAMODE 0xFDC3 +#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 +#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ +#define HFA384X_RID_BUILDSEQ 0xFFFE +#define HFA384X_RID_FWID 0xFFFF + + +struct hfa384x_comp_ident +{ + u16 id; + u16 variant; + u16 major; + u16 minor; +} __attribute__ ((packed)); + +#define HFA384X_COMP_ID_PRI 0x15 +#define HFA384X_COMP_ID_STA 0x1f +#define HFA384X_COMP_ID_FW_AP 0x14b + +struct hfa384x_sup_range +{ + u16 role; + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __attribute__ ((packed)); + + +struct hfa384x_build_id +{ + u16 pri_seq; + u16 sec_seq; +} __attribute__ ((packed)); + +/* FD01 - Download Buffer */ +struct hfa384x_rid_download_buffer +{ + u16 page; + u16 offset; + u16 length; +} __attribute__ ((packed)); + +/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ +struct hfa384x_comms_quality { + u16 comm_qual; /* 0 .. 92 */ + u16 signal_level; /* 27 .. 154 */ + u16 noise_level; /* 27 .. 154 */ +} __attribute__ ((packed)); + + +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +/* New wireless extensions API - SET/GET convention (even ioctl numbers are + * root only) + */ +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) +#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) +#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) +#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) +#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) +#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) +#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) +#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) +#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) + +/* following are not in SIOCGIWPRIV list; check permission in the driver code + */ +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, +}; + +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + caddr_t ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + + +#endif /* HOSTAP_COMMON_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_compat.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_compat.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_compat.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_compat.h 2004-10-13 23:53:28.000000000 -0400 @@ -0,0 +1,148 @@ +#ifndef HOSTAP_COMPAT_H +#define HOSTAP_COMPAT_H + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)) +#define NEW_MODULE_CODE +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) + +#define HOSTAP_QUEUE struct tq_struct + +#define PRISM2_SCHEDULE_TASK(q) \ +MOD_INC_USE_COUNT; \ +if (schedule_task((q)) == 0) \ + MOD_DEC_USE_COUNT; + +static inline void flush_scheduled_work(void) +{ + flush_scheduled_tasks(); +} + +static inline void INIT_WORK(struct tq_struct *tq, + void (*routine)(void *), void *data) +{ + INIT_LIST_HEAD(&tq->list); + tq->sync = 0; + tq->routine = routine; + tq->data = data; +} + +#else /* kernel < 2.5.44 */ + +#define HOSTAP_QUEUE struct work_struct + +#ifdef NEW_MODULE_CODE +#define PRISM2_SCHEDULE_TASK(q) schedule_work(q); +#else /* NEW_MODULE_CODE */ +#define PRISM2_SCHEDULE_TASK(q) \ +MOD_INC_USE_COUNT; \ +if (schedule_work((q)) == 0) \ + MOD_DEC_USE_COUNT; +#endif /* NEW_MODULE_CODE */ + +#endif /* kernel < 2.5.44 */ + +#define HOSTAP_TASKLET struct tasklet_struct + +#define HOSTAP_TASKLET_INIT(q, f, d) \ +do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \ +while (0) + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)) +#define yield() schedule() +#endif + + +/* Interrupt handler backwards compatibility for Linux < 2.5.69 */ +#ifndef IRQ_NONE +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +typedef void irqreturn_t; +#endif + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(var) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)) +#define free_netdev(dev) kfree(dev) +#endif + +#ifndef offset_in_page +#define offset_in_page(p) ((unsigned long) (p) & ~PAGE_MASK) +#endif + +#ifdef __IN_PCMCIA_PACKAGE__ +#undef pcmcia_access_configuration_register +#define pcmcia_access_configuration_register(handle, reg) \ + CardServices(AccessConfigurationRegister, handle, reg) + +#undef pcmcia_register_client +#define pcmcia_register_client(handle, reg) \ + CardServices(RegisterClient, handle, reg) + +#undef pcmcia_deregister_client +#define pcmcia_deregister_client(handle) \ + CardServices(DeregisterClient, handle) + +#undef pcmcia_get_first_tuple +#define pcmcia_get_first_tuple(handle, tuple) \ + CardServices(GetFirstTuple, handle, tuple) + +#undef pcmcia_get_next_tuple +#define pcmcia_get_next_tuple(handle, tuple) \ + CardServices(GetNextTuple, handle, tuple) + +#undef pcmcia_get_tuple_data +#define pcmcia_get_tuple_data(handle, tuple) \ + CardServices(GetTupleData, handle, tuple) + +#undef pcmcia_parse_tuple +#define pcmcia_parse_tuple(handle, tuple, parse) \ + CardServices(ParseTuple, handle, tuple, parse) + +#undef pcmcia_get_configuration_info +#define pcmcia_get_configuration_info(handle, config) \ + CardServices(GetConfigurationInfo, handle, config) + +#undef pcmcia_request_io +#define pcmcia_request_io(handle, req) \ + CardServices(RequestIO, handle, req) + +#undef pcmcia_request_irq +#define pcmcia_request_irq(handle, req) \ + CardServices(RequestIRQ, handle, req) + +#undef pcmcia_request_configuration +#define pcmcia_request_configuration(handle, req) \ + CardServices(RequestConfiguration, handle, req) + +#undef pcmcia_release_configuration +#define pcmcia_release_configuration(handle) \ + CardServices(ReleaseConfiguration, handle) + +#undef pcmcia_release_io +#define pcmcia_release_io(handle, req) \ + CardServices(ReleaseIO, handle, req) + +#undef pcmcia_release_irq +#define pcmcia_release_irq(handle, req) \ + CardServices(ReleaseIRQ, handle, req) + +#undef pcmcia_release_window +#define pcmcia_release_window(win) \ + CardServices(ReleaseWindow, win) + +#undef pcmcia_get_card_services_info +#define pcmcia_get_card_services_info(info) \ + CardServices(GetCardServicesInfo, info) + +#undef pcmcia_report_error +#define pcmcia_report_error(handle, err) \ + CardServices(ReportError, handle, err) +#endif /* __IN_PCMCIA_PACKAGE__ */ + +#endif /* HOSTAP_COMPAT_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_config.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_config.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_config.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_config.h 2005-02-12 11:12:56.000000000 -0500 @@ -0,0 +1,101 @@ +#ifndef HOSTAP_CONFIG_H +#define HOSTAP_CONFIG_H + +#define PRISM2_VERSION "0.3.7 - 2005-02-12" + +/* In the previous versions of Host AP driver, support for user space version + * of IEEE 802.11 management (hostapd) used to be disabled in the default + * configuration. From now on, support for hostapd is always included and it is + * possible to disable kernel driver version of IEEE 802.11 management with a + * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */ +/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +/* Maximum number of events handler per one interrupt */ +#define PRISM2_MAX_INTERRUPT_EVENTS 20 + +/* Use PCI bus master to copy data to/from BAP (only available for + * hostap_pci.o). + * + * Note! This is extremely experimental. PCI bus master is not supported by + * Intersil and it seems to have some problems at least on TX path (see below). + * The driver code for implementing bus master support is based on guessing + * and experimenting suitable control bits and these might not be correct. + * This code is included because using bus master makes a huge difference in + * host CPU load (something like 40% host CPU usage to 5-10% when sending or + * receiving at maximum throughput). + * + * Note2! Station firmware version 1.3.5 and primary firmware version 1.0.7 + * have some fixes for PCI corruption and these (or newer) versions are + * recommended especially when using bus mastering. + * + * NOTE: PCI bus mastering code has not been updated for long time and it is + * not likely to compile and it will _not_ work as is. Only enable this if you + * are prepared to first fix the implementation.. + */ +/* #define PRISM2_BUS_MASTER */ + +#ifdef PRISM2_BUS_MASTER + +/* PCI bus master implementation seems to be broken in current + * hardware/firmware versions. Enable this to use enable command to fix + * something before starting bus master operation on TX path. This will add + * some latency and an extra interrupt to each TX packet. */ +#define PRISM2_ENABLE_BEFORE_TX_BUS_MASTER + +#endif /* PRISM2_BUS_MASTER */ + +/* Include code for downloading firmware images into volatile RAM. */ +#define PRISM2_DOWNLOAD_SUPPORT + +/* Allow kernel configuration to enable download support. */ +#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE) +#define PRISM2_DOWNLOAD_SUPPORT +#endif + +#ifdef PRISM2_DOWNLOAD_SUPPORT +/* Allow writing firmware images into flash, i.e., to non-volatile storage. + * Before you enable this option, you should make absolutely sure that you are + * using prism2_srec utility that comes with THIS version of the driver! + * In addition, please note that it is possible to kill your card with + * non-volatile download if you are using incorrect image. This feature has not + * been fully tested, so please be careful with it. */ +/* #define PRISM2_NON_VOLATILE_DOWNLOAD */ +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +/* Include wireless extensions sub-ioctl support even if wireless extensions + * version is less than 15 (actually, only if it is 12 .. 14). If ver >= 15, + * these will be included. Please note, that this requires iwpriv version 25 + * or higher (older versions will segfault due to long ioctl list). */ +/* #define PRISM2_USE_WE_SUB_IOCTLS */ + +/* Use IW_PRIV_TYPE_ADDR with private WE ioctls taking MAC address argument + * (instead of old 18*char). This requires iwpriv ver >= 25. This will be + * automatically included for WIRELESS_EXT >= 15. */ +/* #define PRISM2_USE_WE_TYPE_ADDR */ + +/* Save low-level I/O for debugging. This should not be enabled in normal use. + */ +/* #define PRISM2_IO_DEBUG */ + +/* Following defines can be used to remove unneeded parts of the driver, e.g., + * to limit the size of the kernel module. Definitions can be added here in + * hostap_config.h or they can be added to make command with EXTRA_CFLAGS, + * e.g., + * 'make pccard EXTRA_CFLAGS="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"' + */ + +/* Do not include debug messages into the driver */ +/* #define PRISM2_NO_DEBUG */ + +/* Do not include /proc/net/prism2/wlan#/{registers,debug} */ +/* #define PRISM2_NO_PROCFS_DEBUG */ + +/* Do not include station functionality (i.e., allow only Master (Host AP) mode + */ +/* #define PRISM2_NO_STATION_MODES */ + +/* Use Linux crypto API instead of own encryption implementation whenever + * possible. */ +/* #define HOSTAP_USE_CRYPTO_API */ + +#endif /* HOSTAP_CONFIG_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt.c 2003-11-21 00:09:05.000000000 -0500 @@ -0,0 +1,194 @@ +/* + * Host AP crypto routines + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#ifdef HOSTAP_CRYPT_MODULE +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_crypt.h" +#include "hostap_compat.h" + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypto"); +MODULE_LICENSE("GPL"); +#endif /* HOSTAP_CRYPT_MODULE */ + + +struct hostap_crypto_alg { + struct list_head list; + struct hostap_crypto_ops *ops; +}; + + +struct hostap_crypto { + struct list_head algs; + spinlock_t lock; +}; + +static struct hostap_crypto *hcrypt; + + +int hostap_register_crypto_ops(struct hostap_crypto_ops *ops) +{ + unsigned long flags; + struct hostap_crypto_alg *alg; + + if (hcrypt == NULL) + return -1; + + alg = (struct hostap_crypto_alg *) kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + spin_lock_irqsave(&hcrypt->lock, flags); + list_add(&alg->list, &hcrypt->algs); + spin_unlock_irqrestore(&hcrypt->lock, flags); + + printk(KERN_DEBUG "hostap_crypt: registered algorithm '%s'\n", + ops->name); + + return 0; +} + + +int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_crypto_alg *del_alg = NULL; + + if (hcrypt == NULL) + return -1; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + if (alg->ops == ops) { + list_del(&alg->list); + del_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (del_alg) { + printk(KERN_DEBUG "hostap_crypt: unregistered algorithm " + "'%s'\n", ops->name); + kfree(del_alg); + } + + return del_alg ? 0 : -1; +} + + +struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_crypto_alg *found_alg = NULL; + + if (hcrypt == NULL) + return NULL; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + if (strcmp(alg->ops->name, name) == 0) { + found_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (found_alg) + return found_alg->ops; + else + return NULL; +} + + +static void * hostap_crypt_null_init(int keyidx) { return (void *) 1; } +static void hostap_crypt_null_deinit(void *priv) {} + +static struct hostap_crypto_ops hostap_crypt_null = { + .name = "NULL", + .init = hostap_crypt_null_init, + .deinit = hostap_crypt_null_deinit, + .encrypt_mpdu = NULL, + .decrypt_mpdu = NULL, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = NULL, + .get_key = NULL, + .extra_prefix_len = 0, + .extra_postfix_len = 0 +}; + + +static int __init hostap_crypto_init(void) +{ + hcrypt = (struct hostap_crypto *) kmalloc(sizeof(*hcrypt), GFP_KERNEL); + if (hcrypt == NULL) + return -ENOMEM; + + memset(hcrypt, 0, sizeof(*hcrypt)); + INIT_LIST_HEAD(&hcrypt->algs); + spin_lock_init(&hcrypt->lock); + + (void) hostap_register_crypto_ops(&hostap_crypt_null); + + return 0; +} + + +static void __exit hostap_crypto_deinit(void) +{ + struct list_head *ptr, *n; + + if (hcrypt == NULL) + return; + + for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs; + ptr = n, n = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + list_del(ptr); + printk(KERN_DEBUG "hostap_crypt: unregistered algorithm " + "'%s' (deinit)\n", alg->ops->name); + kfree(alg); + } + + kfree(hcrypt); +} + + +EXPORT_SYMBOL(hostap_register_crypto_ops); +EXPORT_SYMBOL(hostap_unregister_crypto_ops); +EXPORT_SYMBOL(hostap_get_crypto_ops); + +#ifdef HOSTAP_CRYPT_MODULE +module_init(hostap_crypto_init); +module_exit(hostap_crypto_deinit); +#endif /* HOSTAP_CRYPT_MODULE */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt_ccmp.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt_ccmp.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt_ccmp.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt_ccmp.c 2004-06-20 16:49:01.000000000 -0400 @@ -0,0 +1,1119 @@ +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_crypt.h" +#include "hostap_compat.h" +#include "hostap_wext.h" +#include "hostap_wlan.h" +#include "hostap_80211.h" + +#ifdef CONFIG_CRYPTO +#ifdef HOSTAP_USE_CRYPTO_API +#include +#include +#endif /* HOSTAP_USE_CRYPTO_API */ +#else /* CONFIG_CRYPTO */ +#undef HOSTAP_USE_CRYPTO_API +#endif /* CONFIG_CRYPTO */ + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: CCMP"); +MODULE_LICENSE("GPL"); + + +#define AES_BLOCK_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 + + +struct hostap_ccmp_data { + u8 key[CCMP_TK_LEN]; + int key_set; + + u8 tx_pn[CCMP_PN_LEN]; + u8 rx_pn[CCMP_PN_LEN]; + + u32 dot11RSNAStatsCCMPFormatErrors; + u32 dot11RSNAStatsCCMPReplays; + u32 dot11RSNAStatsCCMPDecryptErrors; + + int key_idx; + +#ifdef HOSTAP_USE_CRYPTO_API + struct crypto_tfm *tfm; +#else /* HOSTAP_USE_CRYPTO_API */ + u32 tfm[44]; /* AES key data; does not change for each encrypt/decrypt + * operation */ +#endif /* HOSTAP_USE_CRYPTO_API */ + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], + tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; + u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; +}; + + +#ifdef HOSTAP_USE_CRYPTO_API + +void hostap_ccmp_aes_encrypt(struct crypto_tfm *tfm, + const u8 pt[16], u8 ct[16]) +{ + struct scatterlist src, dst; + + src.page = virt_to_page(pt); + src.offset = offset_in_page(pt); + src.length = AES_BLOCK_LEN; + + dst.page = virt_to_page(ct); + dst.offset = offset_in_page(ct); + dst.length = AES_BLOCK_LEN; + + crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN); +} + +#else /* HOSTAP_USE_CRYPTO_API */ + +/* ---- begin public domain AES implementation ----------------------------- */ +/* + * Modifications to public domain implementation: + * - support only encryption with 128-bit keys (the only operation needed for + * CCMP) + * - cleanup + * Copyright (c) 2003, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS OR CONTRIBUTORS 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. + */ + +#define FULL_UNROLL + + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, +}; + +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ +((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { \ +(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ +(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) +{ + int i; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + for (i = 0; i < 10; i++) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } +} + + +void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} +/* ---- end public domain AES implementation ------------------------------- */ + +#define hostap_ccmp_aes_encrypt(t, a, b) rijndaelEncrypt(t, a, b) +#define crypto_cipher_setkey(t, k, l) rijndaelKeySetupEnc(t, k) + +#endif /* HOSTAP_USE_CRYPTO_API */ + + +static void * hostap_ccmp_init(int key_idx) +{ + struct hostap_ccmp_data *priv; + +#ifdef NEW_MODULE_CODE + if (!try_module_get(THIS_MODULE)) + return NULL; +#else + MOD_INC_USE_COUNT; +#endif + + priv = (struct hostap_ccmp_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) { + goto fail; + } + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + +#ifdef HOSTAP_USE_CRYPTO_API + priv->tfm = crypto_alloc_tfm("aes", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "hostap_crypt_ccmp: could not allocate " + "crypto API aes\n"); + goto fail; + } +#endif /* HOSTAP_USE_CRYPTO_API */ + + return priv; + +fail: + if (priv) { +#ifdef HOSTAP_USE_CRYPTO_API + if (priv->tfm) + crypto_free_tfm(priv->tfm); +#endif /* HOSTAP_USE_CRYPTO_API */ + kfree(priv); + } +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + return NULL; +} + + +static void hostap_ccmp_deinit(void *priv) +{ +#ifdef HOSTAP_USE_CRYPTO_API + struct hostap_ccmp_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); +#endif /* HOSTAP_USE_CRYPTO_API */ + kfree(priv); +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif +} + + +static inline void xor_block(u8 *b, u8 *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + + +#ifdef HOSTAP_USE_CRYPTO_API +static void ccmp_init_blocks(struct crypto_tfm *tfm, +#else /* HOSTAP_USE_CRYPTO_API */ +static void ccmp_init_blocks(u32 tfm[], +#endif /* HOSTAP_USE_CRYPTO_API */ + struct hostap_ieee80211_hdr *hdr, + u8 *pn, size_t dlen, u8 *b0, u8 *auth, + u8 *s0) +{ + u8 *pos, qc = 0; + size_t aad_len; + u16 fc; + int a4_included, qc_included; + u8 aad[2 * AES_BLOCK_LEN]; + + fc = le16_to_cpu(hdr->frame_control); + a4_included = ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == + (WLAN_FC_TODS | WLAN_FC_FROMDS)); + qc_included = ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) && + (WLAN_FC_GET_STYPE(fc) & 0x08)); + aad_len = 22; + if (a4_included) + aad_len += 6; + if (qc_included) { + pos = (u8 *) &hdr->addr4; + if (a4_included) + pos += 6; + qc = *pos & 0x0f; + aad_len += 2; + } + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + b0[1] = qc; + memcpy(b0 + 2, hdr->addr2, ETH_ALEN); + memcpy(b0 + 8, pn, CCMP_PN_LEN); + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + pos = (u8 *) hdr; + aad[0] = 0; /* aad_len >> 8 */ + aad[1] = aad_len & 0xff; + aad[2] = pos[0] & 0x8f; + aad[3] = pos[1] & 0xc7; + memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); + pos = (u8 *) &hdr->seq_ctrl; + aad[22] = pos[0] & 0x0f; + aad[23] = 0; /* all bits masked */ + memset(aad + 24, 0, 8); + if (a4_included) + memcpy(aad + 24, hdr->addr4, ETH_ALEN); + if (qc_included) { + aad[a4_included ? 30 : 24] = qc; + /* rest of QC masked */ + } + + /* Start with the first block and AAD */ + hostap_ccmp_aes_encrypt(tfm, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + hostap_ccmp_aes_encrypt(tfm, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + hostap_ccmp_aes_encrypt(tfm, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + hostap_ccmp_aes_encrypt(tfm, b0, s0); +} + + +static int hostap_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_ccmp_data *key = priv; + int data_len, i, blocks, last, len; + u8 *pos, *mic; + struct hostap_ieee80211_hdr *hdr; + u8 *b0 = key->tx_b0; + u8 *b = key->tx_b; + u8 *e = key->tx_e; + u8 *s0 = key->tx_s0; + + if (skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < CCMP_MIC_LEN || + skb->len < hdr_len) + return -1; + + data_len = skb->len - hdr_len; + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; + mic = skb_put(skb, CCMP_MIC_LEN); + + i = CCMP_PN_LEN - 1; + while (i >= 0) { + key->tx_pn[i]++; + if (key->tx_pn[i] != 0) + break; + i--; + } + + *pos++ = key->tx_pn[5]; + *pos++ = key->tx_pn[4]; + *pos++ = 0; + *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = key->tx_pn[3]; + *pos++ = key->tx_pn[2]; + *pos++ = key->tx_pn[1]; + *pos++ = key->tx_pn[0]; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); + + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Authentication */ + xor_block(b, pos, len); + hostap_ccmp_aes_encrypt(key->tfm, b, b); + /* Encryption, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + hostap_ccmp_aes_encrypt(key->tfm, b0, e); + xor_block(pos, e, len); + pos += len; + } + + for (i = 0; i < CCMP_MIC_LEN; i++) + mic[i] = b[i] ^ s0[i]; + + return 0; +} + + +static int hostap_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_ccmp_data *key = priv; + u8 keyidx, *pos; + struct hostap_ieee80211_hdr *hdr; + u8 *b0 = key->rx_b0; + u8 *b = key->rx_b; + u8 *a = key->rx_a; + u8 pn[6]; + int i, blocks, last, len; + size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; + u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; + + if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { + key->dot11RSNAStatsCCMPFormatErrors++; + return -1; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet without ExtIV" + " flag from " MACSTR "\n", MAC2STR(hdr->addr2)); + } + key->dot11RSNAStatsCCMPFormatErrors++; + return -2; + } + keyidx >>= 6; + if (key->key_idx != keyidx) { + printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); + return -6; + } + if (!key->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet from " MACSTR + " with keyid=%d that does not have a configured" + " key\n", MAC2STR(hdr->addr2), keyidx); + } + return -3; + } + + pn[0] = pos[7]; + pn[1] = pos[6]; + pn[2] = pos[5]; + pn[3] = pos[4]; + pn[4] = pos[1]; + pn[5] = pos[0]; + pos += 8; + + if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: replay detected: STA=" MACSTR + " previous PN %02x%02x%02x%02x%02x%02x " + "received PN %02x%02x%02x%02x%02x%02x\n", + MAC2STR(hdr->addr2), MAC2STR(key->rx_pn), + MAC2STR(pn)); + } + key->dot11RSNAStatsCCMPReplays++; + return -4; + } + + ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); + xor_block(mic, b, CCMP_MIC_LEN); + + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Decrypt, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + hostap_ccmp_aes_encrypt(key->tfm, b0, b); + xor_block(pos, b, len); + /* Authentication */ + xor_block(a, pos, len); + hostap_ccmp_aes_encrypt(key->tfm, a, a); + pos += len; + } + + if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: decrypt failed: STA=" + MACSTR "\n", MAC2STR(hdr->addr2)); + } + key->dot11RSNAStatsCCMPDecryptErrors++; + return -5; + } + + memcpy(key->rx_pn, pn, CCMP_PN_LEN); + + /* Remove hdr and MIC */ + memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); + skb_pull(skb, CCMP_HDR_LEN); + skb_trim(skb, skb->len - CCMP_MIC_LEN); + + return keyidx; +} + + +static int hostap_ccmp_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_ccmp_data *data = priv; + int keyidx; +#ifdef HOSTAP_USE_CRYPTO_API + struct crypto_tfm *tfm = data->tfm; +#endif /* HOSTAP_USE_CRYPTO_API */ + + keyidx = data->key_idx; + memset(data, 0, sizeof(*data)); + data->key_idx = keyidx; +#ifdef HOSTAP_USE_CRYPTO_API + data->tfm = tfm; +#endif /* HOSTAP_USE_CRYPTO_API */ + if (len == CCMP_TK_LEN) { + memcpy(data->key, key, CCMP_TK_LEN); + data->key_set = 1; + if (seq) { + data->rx_pn[0] = seq[5]; + data->rx_pn[1] = seq[4]; + data->rx_pn[2] = seq[3]; + data->rx_pn[3] = seq[2]; + data->rx_pn[4] = seq[1]; + data->rx_pn[5] = seq[0]; + } + crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); + } else if (len == 0) { + data->key_set = 0; + } else + return -1; + + return 0; +} + + +static int hostap_ccmp_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_ccmp_data *data = priv; + + if (len < CCMP_TK_LEN) + return -1; + + if (!data->key_set) + return 0; + memcpy(key, data->key, CCMP_TK_LEN); + + if (seq) { + seq[0] = data->tx_pn[5]; + seq[1] = data->tx_pn[4]; + seq[2] = data->tx_pn[3]; + seq[3] = data->tx_pn[2]; + seq[4] = data->tx_pn[1]; + seq[5] = data->tx_pn[0]; + } + + return CCMP_TK_LEN; +} + + +static char * hostap_ccmp_print_stats(char *p, void *priv) +{ + struct hostap_ccmp_data *ccmp = priv; + p += sprintf(p, "key[%d] alg=CCMP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "format_errors=%d replays=%d decrypt_errors=%d\n", + ccmp->key_idx, ccmp->key_set, + MAC2STR(ccmp->tx_pn), MAC2STR(ccmp->rx_pn), + ccmp->dot11RSNAStatsCCMPFormatErrors, + ccmp->dot11RSNAStatsCCMPReplays, + ccmp->dot11RSNAStatsCCMPDecryptErrors); + + return p; +} + + +static struct hostap_crypto_ops hostap_crypt_ccmp = { + .name = "CCMP", + .init = hostap_ccmp_init, + .deinit = hostap_ccmp_deinit, + .encrypt_mpdu = hostap_ccmp_encrypt, + .decrypt_mpdu = hostap_ccmp_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = hostap_ccmp_set_key, + .get_key = hostap_ccmp_get_key, + .print_stats = hostap_ccmp_print_stats, + .extra_prefix_len = CCMP_HDR_LEN, + .extra_postfix_len = CCMP_MIC_LEN +}; + + +static int __init hostap_crypto_ccmp_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_ccmp) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_ccmp_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_ccmp); +} + + +module_init(hostap_crypto_ccmp_init); +module_exit(hostap_crypto_ccmp_exit); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt.h 2003-11-23 22:06:07.000000000 -0500 @@ -0,0 +1,50 @@ +#ifndef PRISM2_CRYPT_H +#define PRISM2_CRYPT_H + +struct hostap_crypto_ops { + char *name; + + /* init new crypto context (e.g., allocate private data space, + * select IV, etc.); returns NULL on failure or pointer to allocated + * private data on success */ + void * (*init)(int keyidx); + + /* deinitialize crypto context and free allocated private data */ + void (*deinit)(void *priv); + + /* encrypt/decrypt return < 0 on error or >= 0 on success. The return + * value from decrypt_mpdu is passed as the keyidx value for + * decrypt_msdu. skb must have enough head and tail room for the + * encryption; if not, error will be returned; these functions are + * called for all MPDUs (i.e., fragments). + */ + int (*encrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv); + int (*decrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv); + + /* These functions are called for full MSDUs, i.e. full frames. + * These can be NULL if full MSDU operations are not needed. */ + int (*encrypt_msdu)(struct sk_buff *skb, int hdr_len, void *priv); + int (*decrypt_msdu)(struct sk_buff *skb, int keyidx, int hdr_len, + void *priv); + + int (*set_key)(void *key, int len, u8 *seq, void *priv); + int (*get_key)(void *key, int len, u8 *seq, void *priv); + + /* procfs handler for printing out key information and possible + * statistics */ + char * (*print_stats)(char *p, void *priv); + + /* maximum number of bytes added by encryption; encrypt buf is + * allocated with extra_prefix_len bytes, copy of in_buf, and + * extra_postfix_len; encrypt need not use all this space, but + * the result must start at the beginning of the buffer and correct + * length must be returned */ + int extra_prefix_len, extra_postfix_len; +}; + + +int hostap_register_crypto_ops(struct hostap_crypto_ops *ops); +int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops); +struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name); + +#endif /* PRISM2_CRYPT_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt_tkip.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt_tkip.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt_tkip.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt_tkip.c 2004-08-30 00:11:48.000000000 -0400 @@ -0,0 +1,1021 @@ +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_crypt.h" +#include "hostap_compat.h" +#include "hostap_wext.h" +#include "hostap_wlan.h" +#include "hostap_80211.h" +#include "hostap_config.h" + +#ifdef CONFIG_CRYPTO +#ifdef HOSTAP_USE_CRYPTO_API +#include +#include +#include +#endif /* HOSTAP_USE_CRYPTO_API */ +#else /* CONFIG_CRYPTO */ +#undef HOSTAP_USE_CRYPTO_API +#endif /* CONFIG_CRYPTO */ + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: TKIP"); +MODULE_LICENSE("GPL"); + + +struct hostap_tkip_data { +#define TKIP_KEY_LEN 32 + u8 key[TKIP_KEY_LEN]; + int key_set; + + u32 tx_iv32; + u16 tx_iv16; + u16 tx_ttak[5]; + int tx_phase1_done; + + u32 rx_iv32; + u16 rx_iv16; + u16 rx_ttak[5]; + int rx_phase1_done; + u32 rx_iv32_new; + u16 rx_iv16_new; + + u32 dot11RSNAStatsTKIPReplays; + u32 dot11RSNAStatsTKIPICVErrors; + u32 dot11RSNAStatsTKIPLocalMICFailures; + + int key_idx; + +#ifdef HOSTAP_USE_CRYPTO_API + struct crypto_tfm *tfm_arc4; + struct crypto_tfm *tfm_michael; +#endif /* HOSTAP_USE_CRYPTO_API */ + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16], tx_hdr[16]; +}; + +#ifndef HOSTAP_USE_CRYPTO_API +static const __u32 crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#endif /* HOSTAP_USE_CRYPTO_API */ + + +static void * hostap_tkip_init(int key_idx) +{ + struct hostap_tkip_data *priv; + +#ifdef NEW_MODULE_CODE + if (!try_module_get(THIS_MODULE)) + return NULL; +#else + MOD_INC_USE_COUNT; +#endif + + priv = (struct hostap_tkip_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + +#ifdef HOSTAP_USE_CRYPTO_API + priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->tfm_arc4 == NULL) { + printk(KERN_DEBUG "hostap_crypt_tkip: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tfm_michael == NULL) { + printk(KERN_DEBUG "hostap_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + goto fail; + } +#endif /* HOSTAP_USE_CRYPTO_API */ + + return priv; + +fail: + if (priv) { +#ifdef HOSTAP_USE_CRYPTO_API + if (priv->tfm_michael) + crypto_free_tfm(priv->tfm_michael); + if (priv->tfm_arc4) + crypto_free_tfm(priv->tfm_arc4); +#endif /* HOSTAP_USE_CRYPTO_API */ + kfree(priv); + } +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + return NULL; +} + + +static void hostap_tkip_deinit(void *priv) +{ +#ifdef HOSTAP_USE_CRYPTO_API + struct hostap_tkip_data *_priv = priv; + if (_priv && _priv->tfm_michael) + crypto_free_tfm(_priv->tfm_michael); + if (_priv && _priv->tfm_arc4) + crypto_free_tfm(_priv->tfm_arc4); +#endif /* HOSTAP_USE_CRYPTO_API */ + kfree(priv); +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif +} + + +static inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + + +static inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + + +static inline u8 Hi8(u16 val) +{ + return val >> 8; +} + + +static inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + + +static inline u16 Hi16(u32 val) +{ + return val >> 16; +} + + +static inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (((u16) hi) << 8); +} + + +static inline u16 Mk16_le(u16 *v) +{ + return le16_to_cpu(*v); +} + + +static const u16 Sbox[256] = +{ + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + + +static inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + + +static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + u16 *PPK = (u16 *) &WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1); + +#ifdef __BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + + +#ifndef HOSTAP_USE_CRYPTO_API +static void hostap_wep_encrypt(u8 *key, u8 *buf, size_t buflen, u8 *icv) +{ + u32 i, j, k, crc; + u8 S[256]; + u8 *pos; +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + pos = buf; + for (k = 0; k < buflen; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + pos = icv; + pos[0] = crc; + pos[1] = crc >> 8; + pos[2] = crc >> 16; + pos[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } +} +#endif /* HOSTAP_USE_CRYPTO_API */ + + +static int hostap_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + int len; + u8 rc4key[16], *pos, *icv; + struct hostap_ieee80211_hdr *hdr; +#ifdef HOSTAP_USE_CRYPTO_API + u32 crc; + struct scatterlist sg; +#endif /* HOSTAP_USE_CRYPTO_API */ + + if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (!tkey->tx_phase1_done) { + tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, + tkey->tx_iv32); + tkey->tx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); + + len = skb->len - hdr_len; + pos = skb_push(skb, 8); + memmove(pos, pos + 8, hdr_len); + pos += hdr_len; + icv = skb_put(skb, 4); + + *pos++ = rc4key[0]; + *pos++ = rc4key[1]; + *pos++ = rc4key[2]; + *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = tkey->tx_iv32 & 0xff; + *pos++ = (tkey->tx_iv32 >> 8) & 0xff; + *pos++ = (tkey->tx_iv32 >> 16) & 0xff; + *pos++ = (tkey->tx_iv32 >> 24) & 0xff; + +#ifdef HOSTAP_USE_CRYPTO_API + crc = ~crc32_le(~0, pos, len); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); +#else /* HOSTAP_USE_CRYPTO_API */ + hostap_wep_encrypt(rc4key, pos, len, icv); +#endif /* HOSTAP_USE_CRYPTO_API */ + + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } + + return 0; +} + + +#ifndef HOSTAP_USE_CRYPTO_API +static int hostap_wep_decrypt(u8 *key, u8 *buf, size_t plen) +{ + u32 i, j, k, crc; + u8 S[256]; + u8 *pos, icv[4]; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + pos = buf; + crc = ~0; + i = j = 0; + for (k = 0; k < plen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with the + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return -1; + } + } + + return 0; +} +#endif /* HOSTAP_USE_CRYPTO_API */ + + +static int hostap_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + u8 rc4key[16]; + u8 keyidx, *pos; + u32 iv32; + u16 iv16; + struct hostap_ieee80211_hdr *hdr; +#ifdef HOSTAP_USE_CRYPTO_API + u8 icv[4]; + u32 crc; + struct scatterlist sg; + int plen; +#endif /* HOSTAP_USE_CRYPTO_API */ + + if (skb->len < hdr_len + 8 + 4) + return -1; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet without ExtIV" + " flag from " MACSTR "\n", MAC2STR(hdr->addr2)); + } + return -2; + } + keyidx >>= 6; + if (tkey->key_idx != keyidx) { + printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); + return -6; + } + if (!tkey->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet from " MACSTR + " with keyid=%d that does not have a configured" + " key\n", MAC2STR(hdr->addr2), keyidx); + } + return -3; + } + iv16 = (pos[0] << 8) | pos[2]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += 8; + + if (iv32 < tkey->rx_iv32 || + (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: replay detected: STA=" MACSTR + " previous TSC %08x%04x received TSC " + "%08x%04x\n", MAC2STR(hdr->addr2), + tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); + } + tkey->dot11RSNAStatsTKIPReplays++; + return -4; + } + + if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { + tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); + tkey->rx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); + +#ifdef HOSTAP_USE_CRYPTO_API + plen = skb->len - hdr_len - 12; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { +#else /* HOSTAP_USE_CRYPTO_API */ + if (hostap_wep_decrypt(rc4key, pos, skb->len - hdr_len - 12)) { +#endif /* HOSTAP_USE_CRYPTO_API */ + if (iv32 != tkey->rx_iv32) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + tkey->rx_phase1_done = 0; + } + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: ICV error detected: STA=" + MACSTR "\n", MAC2STR(hdr->addr2)); + } + tkey->dot11RSNAStatsTKIPICVErrors++; + return -5; + } + + /* Update real counters only after Michael MIC verification has + * completed */ + tkey->rx_iv32_new = iv32; + tkey->rx_iv16_new = iv16; + + /* Remove IV and ICV */ + memmove(skb->data + 8, skb->data, hdr_len); + skb_pull(skb, 8); + skb_trim(skb, skb->len - 4); + + return keyidx; +} + + +#ifdef HOSTAP_USE_CRYPTO_API +static int michael_mic(struct hostap_tkip_data *tkey, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + struct scatterlist sg[2]; + + if (tkey->tfm_michael == NULL) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; + + crypto_digest_init(tkey->tfm_michael); + crypto_digest_setkey(tkey->tfm_michael, key, 8); + crypto_digest_update(tkey->tfm_michael, sg, 2); + crypto_digest_final(tkey->tfm_michael, mic); + + return 0; +} + +#else /* HOSTAP_USE_CRYPTO_API */ + +static inline u32 rotl(u32 val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + + +static inline u32 xswap(u32 val) +{ + return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); +} + + +#define michael_block(l, r) \ +do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ +} while (0) + + +static inline u32 get_le32(u8 *p) +{ + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + + +static inline void put_le32(u8 *p, u32 v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + + +static int michael_mic(struct hostap_tkip_data *tkey, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + u32 l, r; + int i, blocks, last; + + l = get_le32(key); + r = get_le32(key + 4); + + /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */ + l ^= get_le32(hdr); + michael_block(l, r); + l ^= get_le32(&hdr[4]); + michael_block(l, r); + l ^= get_le32(&hdr[8]); + michael_block(l, r); + l ^= get_le32(&hdr[12]); + michael_block(l, r); + + /* 32-bit blocks of data */ + blocks = data_len / 4; + last = data_len % 4; + for (i = 0; i < blocks; i++) { + l ^= get_le32(&data[4 * i]); + michael_block(l, r); + } + + /* Last block and padding (0x5a, 4..7 x 0) */ + switch (last) { + case 0: + l ^= 0x5a; + break; + case 1: + l ^= data[4 * i] | 0x5a00; + break; + case 2: + l ^= data[4 * i] | (data[4 * i + 1] << 8) | 0x5a0000; + break; + case 3: + l ^= data[4 * i] | (data[4 * i + 1] << 8) | + (data[4 * i + 2] << 16) | 0x5a000000; + break; + } + michael_block(l, r); + /* l ^= 0; */ + michael_block(l, r); + + put_le32(mic, l); + put_le32(mic + 4, r); + return 0; +} +#endif /* HOSTAP_USE_CRYPTO_API */ + + +static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr) +{ + struct hostap_ieee80211_hdr *hdr11; + + hdr11 = (struct hostap_ieee80211_hdr *) skb->data; + switch (le16_to_cpu(hdr11->frame_control) & + (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + case WLAN_FC_FROMDS: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ + break; + case WLAN_FC_FROMDS | WLAN_FC_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ + break; + case 0: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + } + + hdr[12] = 0; /* priority */ + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + + +static int hostap_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + u8 *pos; + + if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { + printk(KERN_DEBUG "Invalid packet for Michael MIC add " + "(tailroom=%d hdr_len=%d skb->len=%d)\n", + skb_tailroom(skb), hdr_len, skb->len); + return -1; + } + + michael_mic_hdr(skb, tkey->tx_hdr); + pos = skb_put(skb, 8); + if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + return -1; + + return 0; +} + + +#if WIRELESS_EXT >= 18 +static void hostap_michael_mic_failure(struct net_device *dev, + struct hostap_ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + struct iw_michaelmicfailure ev; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + memset(&ev, 0, sizeof(ev)); + ev.flags = keyidx & IW_MICFAILURE_KEY_ID; + if (hdr->addr1[0] & 0x01) + ev.flags |= IW_MICFAILURE_GROUP; + else + ev.flags |= IW_MICFAILURE_PAIRWISE; + ev.src_addr.sa_family = ARPHRD_ETHER; + memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(ev); + wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *) &ev); +} +#elif WIRELESS_EXT >= 15 +static void hostap_michael_mic_failure(struct net_device *dev, + struct hostap_ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + char buf[128]; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=" + MACSTR ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC2STR(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); +} +#else /* WIRELESS_EXT >= 15 */ +static inline void hostap_michael_mic_failure(struct net_device *dev, + struct hostap_ieee80211_hdr *hdr, + int keyidx) +{ +} +#endif /* WIRELESS_EXT >= 15 */ + + +static int hostap_michael_mic_verify(struct sk_buff *skb, int keyidx, + int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + u8 mic[8]; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + return -1; + if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { + struct hostap_ieee80211_hdr *hdr; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + printk(KERN_DEBUG "%s: Michael MIC verification failed for " + "MSDU from " MACSTR " keyidx=%d\n", + skb->dev ? skb->dev->name : "N/A", MAC2STR(hdr->addr2), + keyidx); + if (skb->dev) + hostap_michael_mic_failure(skb->dev, hdr, keyidx); + tkey->dot11RSNAStatsTKIPLocalMICFailures++; + return -1; + } + + /* Update TSC counters for RX now that the packet verification has + * completed. */ + tkey->rx_iv32 = tkey->rx_iv32_new; + tkey->rx_iv16 = tkey->rx_iv16_new; + + skb_trim(skb, skb->len - 8); + + return 0; +} + + +static int hostap_tkip_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + int keyidx; +#ifdef HOSTAP_USE_CRYPTO_API + struct crypto_tfm *tfm = tkey->tfm_michael; + struct crypto_tfm *tfm2 = tkey->tfm_arc4; +#endif /* HOSTAP_USE_CRYPTO_API */ + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; +#ifdef HOSTAP_USE_CRYPTO_API + tkey->tfm_michael = tfm; + tkey->tfm_arc4 = tfm2; +#endif /* HOSTAP_USE_CRYPTO_API */ + if (len == TKIP_KEY_LEN) { + memcpy(tkey->key, key, TKIP_KEY_LEN); + tkey->key_set = 1; + tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ + if (seq) { + tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | + (seq[3] << 8) | seq[2]; + tkey->rx_iv16 = (seq[1] << 8) | seq[0]; + } + } else if (len == 0) { + tkey->key_set = 0; + } else + return -1; + + return 0; +} + + +static int hostap_tkip_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + + if (len < TKIP_KEY_LEN) + return -1; + + if (!tkey->key_set) + return 0; + memcpy(key, tkey->key, TKIP_KEY_LEN); + + if (seq) { + /* Return the sequence number of the last transmitted frame. */ + u16 iv16 = tkey->tx_iv16; + u32 iv32 = tkey->tx_iv32; + if (iv16 == 0) + iv32--; + iv16--; + seq[0] = tkey->tx_iv16; + seq[1] = tkey->tx_iv16 >> 8; + seq[2] = tkey->tx_iv32; + seq[3] = tkey->tx_iv32 >> 8; + seq[4] = tkey->tx_iv32 >> 16; + seq[5] = tkey->tx_iv32 >> 24; + } + + return TKIP_KEY_LEN; +} + + +static char * hostap_tkip_print_stats(char *p, void *priv) +{ + struct hostap_tkip_data *tkip = priv; + p += sprintf(p, "key[%d] alg=TKIP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "replays=%d icv_errors=%d local_mic_failures=%d\n", + tkip->key_idx, tkip->key_set, + (tkip->tx_iv32 >> 24) & 0xff, + (tkip->tx_iv32 >> 16) & 0xff, + (tkip->tx_iv32 >> 8) & 0xff, + tkip->tx_iv32 & 0xff, + (tkip->tx_iv16 >> 8) & 0xff, + tkip->tx_iv16 & 0xff, + (tkip->rx_iv32 >> 24) & 0xff, + (tkip->rx_iv32 >> 16) & 0xff, + (tkip->rx_iv32 >> 8) & 0xff, + tkip->rx_iv32 & 0xff, + (tkip->rx_iv16 >> 8) & 0xff, + tkip->rx_iv16 & 0xff, + tkip->dot11RSNAStatsTKIPReplays, + tkip->dot11RSNAStatsTKIPICVErrors, + tkip->dot11RSNAStatsTKIPLocalMICFailures); + return p; +} + + +static struct hostap_crypto_ops hostap_crypt_tkip = { + .name = "TKIP", + .init = hostap_tkip_init, + .deinit = hostap_tkip_deinit, + .encrypt_mpdu = hostap_tkip_encrypt, + .decrypt_mpdu = hostap_tkip_decrypt, + .encrypt_msdu = hostap_michael_mic_add, + .decrypt_msdu = hostap_michael_mic_verify, + .set_key = hostap_tkip_set_key, + .get_key = hostap_tkip_get_key, + .print_stats = hostap_tkip_print_stats, + .extra_prefix_len = 4 + 4 /* IV + ExtIV */, + .extra_postfix_len = 8 + 4 /* MIC + ICV */ +}; + + +static int __init hostap_crypto_tkip_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_tkip) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_tkip_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_tkip); +} + + +module_init(hostap_crypto_tkip_init); +module_exit(hostap_crypto_tkip_exit); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt_wep.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt_wep.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_crypt_wep.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_crypt_wep.c 2004-06-20 16:49:01.000000000 -0400 @@ -0,0 +1,479 @@ +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include +#include + +#include "hostap_crypt.h" +#include "hostap_compat.h" +#include "hostap_config.h" + +#ifdef CONFIG_CRYPTO +#ifdef HOSTAP_USE_CRYPTO_API +#include +#include +#include +#endif /* HOSTAP_USE_CRYPTO_API */ +#else /* CONFIG_CRYPTO */ +#undef HOSTAP_USE_CRYPTO_API +#endif /* CONFIG_CRYPTO */ + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: WEP"); +MODULE_LICENSE("GPL"); + + +struct prism2_wep_data { + u32 iv; +#define WEP_KEY_LEN 13 + u8 key[WEP_KEY_LEN + 1]; + u8 key_len; + u8 key_idx; +#ifdef HOSTAP_USE_CRYPTO_API + struct crypto_tfm *tfm; +#endif /* HOSTAP_USE_CRYPTO_API */ +}; + +#ifndef HOSTAP_USE_CRYPTO_API +static const __u32 crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#endif /* HOSTAP_USE_CRYPTO_API */ + + +static void * prism2_wep_init(int keyidx) +{ + struct prism2_wep_data *priv; + +#ifdef NEW_MODULE_CODE + if (!try_module_get(THIS_MODULE)) + return NULL; +#else + MOD_INC_USE_COUNT; +#endif + + priv = (struct prism2_wep_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) { +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + goto fail; + } + memset(priv, 0, sizeof(*priv)); + priv->key_idx = keyidx; + +#ifdef HOSTAP_USE_CRYPTO_API + priv->tfm = crypto_alloc_tfm("arc4", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "hostap_crypt_wep: could not allocate " + "crypto API arc4\n"); + goto fail; + } +#endif /* HOSTAP_USE_CRYPTO_API */ + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; + +fail: + if (priv) { +#ifdef HOSTAP_USE_CRYPTO_API + if (priv->tfm) + crypto_free_tfm(priv->tfm); +#endif /* HOSTAP_USE_CRYPTO_API */ + kfree(priv); + } +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + return NULL; +} + + +static void prism2_wep_deinit(void *priv) +{ +#ifdef HOSTAP_USE_CRYPTO_API + struct prism2_wep_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); +#endif /* HOSTAP_USE_CRYPTO_API */ + kfree(priv); +#ifdef NEW_MODULE_CODE + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif +} + + +/* Perform WEP encryption on given skb that has at least 4 bytes of headroom + * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, + * so the payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 crc, klen, len; + u8 key[WEP_KEY_LEN + 3]; +#ifdef HOSTAP_USE_CRYPTO_API + u8 *pos, *icv; + struct scatterlist sg; +#else /* HOSTAP_USE_CRYPTO_API */ + u32 i, j, k; + u8 S[256]; + u8 kpos, *pos; +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) +#endif /* HOSTAP_USE_CRYPTO_API */ + + if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + len = skb->len - hdr_len; + pos = skb_push(skb, 4); + memmove(pos, pos + 4, hdr_len); + pos += hdr_len; + + klen = 3 + wep->key_len; + + wep->iv++; + + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key + * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) + * can be used to speedup attacks, so avoid using them. */ + if ((wep->iv & 0xff00) == 0xff00) { + u8 B = (wep->iv >> 16) & 0xff; + if (B >= 3 && B < klen) + wep->iv += 0x0100; + } + + /* Prepend 24-bit IV to RC4 key and TX frame */ + *pos++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + +#ifdef HOSTAP_USE_CRYPTO_API + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + crc = ~crc32_le(~0, pos, len); + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4); + +#else /* HOSTAP_USE_CRYPTO_API */ + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= klen) + kpos = 0; + S_SWAP(i, j); + } + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + for (k = 0; k < len; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + pos = skb_put(skb, 4); + pos[0] = crc; + pos[1] = crc >> 8; + pos[2] = crc >> 16; + pos[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + +#endif /* HOSTAP_USE_CRYPTO_API */ + + return 0; +} + + +/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of + * the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed. + */ +static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 crc, klen, plen; + u8 key[WEP_KEY_LEN + 3]; + u8 keyidx, *pos, icv[4]; +#ifdef HOSTAP_USE_CRYPTO_API + struct scatterlist sg; +#else /* HOSTAP_USE_CRYPTO_API */ + u32 i, j, k; + u8 S[256], kpos; +#endif /* HOSTAP_USE_CRYPTO_API */ + + if (skb->len < hdr_len + 8) + return -1; + + pos = skb->data + hdr_len; + key[0] = *pos++; + key[1] = *pos++; + key[2] = *pos++; + keyidx = *pos++ >> 6; + if (keyidx != wep->key_idx) + return -1; + + klen = 3 + wep->key_len; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + +#ifdef HOSTAP_USE_CRYPTO_API + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + plen = skb->len - hdr_len - 8; + + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + /* ICV mismatch - drop frame */ + return -2; + } + +#else /* HOSTAP_USE_CRYPTO_API */ + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= klen) + kpos = 0; + S_SWAP(i, j); + } + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + plen = skb->len - hdr_len - 8; + crc = ~0; + i = j = 0; + for (k = 0; k < plen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with the + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return -2; + } + } + +#endif /* HOSTAP_USE_CRYPTO_API */ + + /* Remove IV and ICV */ + memmove(skb->data + 4, skb->data, hdr_len); + skb_pull(skb, 4); + skb_trim(skb, skb->len - 4); + + return 0; +} + + +static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < 0 || len > WEP_KEY_LEN) + return -1; + + memcpy(wep->key, key, len); + wep->key_len = len; + + return 0; +} + + +static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < wep->key_len) + return -1; + + memcpy(key, wep->key, wep->key_len); + + return wep->key_len; +} + + +static char * prism2_wep_print_stats(char *p, void *priv) +{ + struct prism2_wep_data *wep = priv; + p += sprintf(p, "key[%d] alg=WEP len=%d\n", + wep->key_idx, wep->key_len); + return p; +} + + +static struct hostap_crypto_ops hostap_crypt_wep = { + .name = "WEP", + .init = prism2_wep_init, + .deinit = prism2_wep_deinit, + .encrypt_mpdu = prism2_wep_encrypt, + .decrypt_mpdu = prism2_wep_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = prism2_wep_set_key, + .get_key = prism2_wep_get_key, + .print_stats = prism2_wep_print_stats, + .extra_prefix_len = 4 /* IV */, + .extra_postfix_len = 4 /* ICV */ +}; + + +static int __init hostap_crypto_wep_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_wep) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_wep_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_wep); +} + + +module_init(hostap_crypto_wep_init); +module_exit(hostap_crypto_wep_exit); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_cs.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_cs.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_cs.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_cs.c 2005-02-05 02:20:09.000000000 -0500 @@ -0,0 +1,966 @@ +#define PRISM2_PCCARD + +#include +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif /* __IN_PCMCIA_PACKAGE__ */ +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include "hostap_wext.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "hostap_wlan.h" + + +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif /* __IN_PCMCIA_PACKAGE__ */ + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static dev_info_t dev_info = "hostap_cs"; +static dev_link_t *dev_list = NULL; + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PC Card)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)"); +MODULE_LICENSE("GPL"); + + +static unsigned int irq_mask = 0xdeb8; +MODULE_PARM(irq_mask, "i"); + +static int irq_list[4] = { -1 }; +MODULE_PARM(irq_list, "1-4i"); + +static int ignore_cis_vcc = 0; +MODULE_PARM(ignore_cis_vcc, "i"); +MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + outb(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + u8 v; + + spin_lock_irqsave(&local->lock, flags); + v = inb(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + outw(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + u16 v; + + spin_lock_irqsave(&local->lock, flags); + v = inw(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outsw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); + outsw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline void hfa384x_insw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); + insw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) +#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) +#define HFA384X_INB(a) inb(dev->base_addr + (a)) +#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) +#define HFA384X_INW(a) inw(dev->base_addr + (a)) +#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) +#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_INSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_OUTSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + + + +static void prism2_detach(dev_link_t *link); +static void prism2_release(u_long arg); +static int prism2_event(event_t event, int priority, + event_callback_args_t *args); + + +static int prism2_pccard_card_present(local_info_t *local) +{ + if (local->link != NULL && + ((local->link->state & (DEV_PRESENT | DEV_CONFIG)) == + (DEV_PRESENT | DEV_CONFIG))) + return 1; + return 0; +} + + +/* SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0 + * Document No. 20-10-00058, January 2004 + * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf + */ +#define SANDISK_WLAN_ACTIVATION_OFF 0x40 +#define SANDISK_HCR_OFF 0x42 + + +static void sandisk_set_iobase(local_info_t *local) +{ + int res; + conf_reg_t reg; + + reg.Function = 0; + reg.Action = CS_WRITE; + reg.Offset = 0x10; /* 0x3f0 IO base 1 */ + reg.Value = local->link->io.BasePort1 & 0x00ff; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" + " res=%d\n", res); + } + udelay(10); + + reg.Function = 0; + reg.Action = CS_WRITE; + reg.Offset = 0x12; /* 0x3f2 IO base 2 */ + reg.Value = (local->link->io.BasePort1 & 0xff00) >> 8; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" + " res=%d\n", res); + } +} + + +static void sandisk_write_hcr(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + int i; + + HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF); + udelay(50); + for (i = 0; i < 10; i++) { + HFA384X_OUTB(hcr, SANDISK_HCR_OFF); + } + udelay(55); + HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF); +} + + +static void sandisk_enable_wireless(struct net_device *dev) +{ + int res; + conf_reg_t reg; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + tuple_t tuple; + cisparse_t parse; + u_char buf[64]; + + if (local->link->io.NumPorts1 < 0x42) { + /* Not enough ports to be SanDisk multi-function card */ + return; + } + + tuple.DesiredTuple = CISTPL_MANFID; + tuple.Attributes = TUPLE_RETURN_COMMON; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + if (pcmcia_get_first_tuple(local->link->handle, &tuple) || + pcmcia_get_tuple_data(local->link->handle, &tuple) || + pcmcia_parse_tuple(local->link->handle, &tuple, &parse) || + parse.manfid.manf != 0xd601 || parse.manfid.card != 0x0101) { + /* No SanDisk manfid found */ + return; + } + + tuple.DesiredTuple = CISTPL_LONGLINK_MFC; + if (pcmcia_get_first_tuple(local->link->handle, &tuple) || + pcmcia_get_tuple_data(local->link->handle, &tuple) || + pcmcia_parse_tuple(local->link->handle, &tuple, &parse) || + parse.longlink_mfc.nfn < 2) { + /* No multi-function links found */ + return; + } + + printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected" + " - using vendor-specific initialization\n", dev->name); + local->sandisk_connectplus = 1; + + reg.Function = 0; + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", + dev->name, res); + return; + } + mdelay(5); + + reg.Function = 0; + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + /* Do not enable interrupts here to avoid some bogus events. Interrupts + * will be enabled during the first cor_sreset call. */ + reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", + dev->name, res); + return; + } + mdelay(5); + + sandisk_set_iobase(local); + + HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF); + udelay(10); + HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF); + udelay(10); +} + + +static void prism2_pccard_cor_sreset(local_info_t *local) +{ + int res; + conf_reg_t reg; + + if (!prism2_pccard_card_present(local)) + return; + + reg.Function = 0; + reg.Action = CS_READ; + reg.Offset = CISREG_COR; + reg.Value = 0; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", + res); + return; + } + printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n", + reg.Value); + + reg.Action = CS_WRITE; + reg.Value |= COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", + res); + return; + } + + mdelay(local->sandisk_connectplus ? 5 : 2); + + reg.Value &= ~COR_SOFT_RESET; + if (local->sandisk_connectplus) + reg.Value |= COR_IREQ_ENA; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", + res); + return; + } + + mdelay(local->sandisk_connectplus ? 5 : 2); + + if (local->sandisk_connectplus) + sandisk_set_iobase(local); +} + + +static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) +{ + int res; + conf_reg_t reg; + int old_cor; + + if (!prism2_pccard_card_present(local)) + return; + + if (local->sandisk_connectplus) { + sandisk_write_hcr(local, hcr); + return; + } + + reg.Function = 0; + reg.Action = CS_READ; + reg.Offset = CISREG_COR; + reg.Value = 0; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 " + "(%d)\n", res); + return; + } + printk(KERN_DEBUG "prism2_pccard_genesis_sreset: original COR %02x\n", + reg.Value); + old_cor = reg.Value; + + reg.Action = CS_WRITE; + reg.Value |= COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 " + "(%d)\n", res); + return; + } + + mdelay(10); + + /* Setup Genesis mode */ + reg.Action = CS_WRITE; + reg.Value = hcr; + reg.Offset = CISREG_CCSR; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 " + "(%d)\n", res); + return; + } + mdelay(10); + + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = old_cor & ~COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 " + "(%d)\n", res); + return; + } + + mdelay(10); +} + + +static int prism2_pccard_dev_open(local_info_t *local) +{ + local->link->open++; + return 0; +} + + +static int prism2_pccard_dev_close(local_info_t *local) +{ + if (local == NULL || local->link == NULL) + return 1; + + if (!local->link->open) { + printk(KERN_WARNING "%s: prism2_pccard_dev_close(): " + "link not open?!\n", local->dev->name); + return 1; + } + + local->link->open--; + + return 0; +} + + +static struct prism2_helper_functions prism2_pccard_funcs = +{ + .card_present = prism2_pccard_card_present, + .cor_sreset = prism2_pccard_cor_sreset, + .dev_open = prism2_pccard_dev_open, + .dev_close = prism2_pccard_dev_close, + .genesis_reset = prism2_pccard_genesis_reset, + .hw_type = HOSTAP_HW_PCCARD, +}; + + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,68) +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + pcmcia_report_error(handle, &err); +} +#endif + + +/* allocate local data and register with CardServices + * initialize dev_link structure, but do not configure the card yet */ +static dev_link_t *prism2_attach(void) +{ + dev_link_t *link; + client_reg_t client_reg; + int ret; + + link = kmalloc(sizeof(dev_link_t), GFP_KERNEL); + if (link == NULL) + return NULL; + + memset(link, 0, sizeof(dev_link_t)); + + PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); + link->conf.Vcc = 33; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* register with CardServices */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT; + client_reg.EventMask = CS_EVENT_CARD_INSERTION | + CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &prism2_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + prism2_detach(link); + return NULL; + } + return link; +} + + +static void prism2_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + PDEBUG(DEBUG_FLOW, "prism2_detach\n"); + + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp == NULL) { + printk(KERN_WARNING "%s: Attempt to detach non-existing " + "PCMCIA client\n", dev_info); + return; + } + + if (link->state & DEV_CONFIG) { + prism2_release((u_long)link); + } + + if (link->handle) { + int res = pcmcia_deregister_client(link->handle); + if (res) { + printk("CardService(DeregisterClient) => %d\n", res); + cs_error(link->handle, DeregisterClient, res); + } + } + + *linkp = link->next; + /* release net devices */ + if (link->priv) { + prism2_free_local_data((struct net_device *) link->priv); + + } + kfree(link); +} + + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +#define CFG_CHECK2(fn, retf) \ +do { int ret = (retf); \ +if (ret != 0) { \ + PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \ + cs_error(link->handle, fn, ret); \ + goto next_entry; \ +} \ +} while (0) + + +/* run after a CARD_INSERTION event is received to configure the PCMCIA + * socket and make the device available to the system */ +static int prism2_config(dev_link_t *link) +{ + struct net_device *dev; + struct hostap_interface *iface; + local_info_t *local; + int ret; + tuple_t tuple; + cisparse_t parse; + int last_fn, last_ret; + u_char buf[64]; + config_info_t conf; + cistpl_cftable_entry_t dflt = { 0 }; + + PDEBUG(DEBUG_FLOW, "prism2_config()\n"); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, &parse)); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + CS_CHECK(GetConfigurationInfo, + pcmcia_get_configuration_info(link->handle, &conf)); + PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info, + ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc); + link->conf.Vcc = conf.Vcc; + + /* Look for an appropriate configuration table entry in the CIS */ + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple)); + for (;;) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + CFG_CHECK2(GetTupleData, + pcmcia_get_tuple_data(link->handle, &tuple)); + CFG_CHECK2(ParseTuple, + pcmcia_parse_tuple(link->handle, &tuple, &parse)); + + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + dflt = *cfg; + if (cfg->index == 0) + goto next_entry; + link->conf.ConfigIndex = cfg->index; + PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X " + "(default 0x%02X)\n", cfg->index, dflt.index); + + /* Does this card need audio output? */ + if (cfg->flags & CISTPL_CFTABLE_AUDIO) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + + /* Use power settings for Vcc and Vpp if present */ + /* Note that the CIS values need to be rescaled */ + if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / + 10000 && !ignore_cis_vcc) { + PDEBUG(DEBUG_EXTRA, " Vcc mismatch - skipping" + " this entry\n"); + goto next_entry; + } + } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / + 10000 && !ignore_cis_vcc) { + PDEBUG(DEBUG_EXTRA, " Vcc (default) mismatch " + "- skipping this entry\n"); + goto next_entry; + } + } + + if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; + else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; + + /* Do we need to allocate an interrupt? */ + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) + link->conf.Attributes |= CONF_ENABLE_IRQ; + else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) { + /* At least Compaq WL200 does not have IRQInfo1 set, + * but it does not work without interrupts.. */ + printk("Config has no IRQ info, but trying to enable " + "IRQ anyway..\n"); + link->conf.Attributes |= CONF_ENABLE_IRQ; + } + + /* IO window settings */ + PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d " + "dflt.io.nwin=%d\n", + cfg->io.nwin, dflt.io.nwin); + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, " + "io.base=0x%04x, len=%d\n", io->flags, + io->win[0].base, io->win[0].len); + if (!(io->flags & CISTPL_IO_8BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = io->flags & + CISTPL_IO_LINES_MASK; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + if (io->nwin > 1) { + link->io.Attributes2 = link->io.Attributes1; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + } + + /* This reserves IO space but doesn't actually enable it */ + CFG_CHECK2(RequestIO, + pcmcia_request_io(link->handle, &link->io)); + + /* This configuration table entry is OK */ + break; + + next_entry: + CS_CHECK(GetNextTuple, + pcmcia_get_next_tuple(link->handle, &tuple)); + } + + /* Need to allocate net_device before requesting IRQ handler */ + dev = prism2_init_local_data(&prism2_pccard_funcs, 0); + if (dev == NULL) + goto failed; + link->priv = dev; + + /* + * Allocate an interrupt line. Note that this does not assign a + * handler to the interrupt, unless the 'Handler' member of the + * irq structure is initialized. + */ + if (link->conf.Attributes & CONF_ENABLE_IRQ) { + int i; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = prism2_interrupt; + link->irq.Instance = dev; + CS_CHECK(RequestIRQ, + pcmcia_request_irq(link->handle, &link->irq)); + } + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping, and putting the + * card and host interface into "Memory and IO" mode. + */ + CS_CHECK(RequestConfiguration, + pcmcia_request_configuration(link->handle, &link->conf)); + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + + /* Finally, report what we've done */ + printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", + dev_info, link->conf.ConfigIndex, + link->conf.Vcc / 10, link->conf.Vcc % 10); + if (link->conf.Vpp1) + printk(", Vpp %d.%d", link->conf.Vpp1 / 10, + link->conf.Vpp1 % 10); + if (link->conf.Attributes & CONF_ENABLE_IRQ) + printk(", irq %d", link->irq.AssignedIRQ); + if (link->io.NumPorts1) + printk(", io 0x%04x-0x%04x", link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1-1); + if (link->io.NumPorts2) + printk(" & 0x%04x-0x%04x", link->io.BasePort2, + link->io.BasePort2+link->io.NumPorts2-1); + printk("\n"); + + link->state |= DEV_CONFIG; + link->state &= ~DEV_CONFIG_PENDING; + + iface = dev->priv; + local = iface->local; + local->link = link; + strcpy(local->node.dev_name, dev->name); + link->dev = &local->node; + + local->shutdown = 0; + + sandisk_enable_wireless(dev); + + ret = prism2_hw_config(dev, 1); + if (!ret) { + ret = hostap_hw_ready(dev); + if (ret == 0 && local->ddev) + strcpy(local->node.dev_name, local->ddev->name); + } + return ret; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + + failed: + prism2_release((u_long)link); + return 1; +} + + +static void prism2_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + PDEBUG(DEBUG_FLOW, "prism2_release\n"); + + if (link->priv) { + struct net_device *dev = link->priv; + struct hostap_interface *iface = dev->priv; + if (link->state & DEV_CONFIG) + prism2_hw_shutdown(dev, 0); + iface->local->shutdown = 1; + } + + if (link->win) + pcmcia_release_window(link->win); + pcmcia_release_configuration(link->handle); + if (link->io.NumPorts1) + pcmcia_release_io(link->handle, &link->io); + if (link->irq.AssignedIRQ) + pcmcia_release_irq(link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + + PDEBUG(DEBUG_FLOW, "release - done\n"); +} + + +static int prism2_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = (struct net_device *) link->priv; + + switch (event) { + case CS_EVENT_CARD_INSERTION: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_INSERTION\n", dev_info); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + if (prism2_config(link)) { + PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); + } + break; + + case CS_EVENT_CARD_REMOVAL: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_REMOVAL\n", dev_info); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + netif_stop_queue(dev); + netif_device_detach(dev); + prism2_release((u_long) link); + } + break; + + case CS_EVENT_PM_SUSPEND: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); + link->state |= DEV_SUSPEND; + /* fall through */ + + case CS_EVENT_RESET_PHYSICAL: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_RESET_PHYSICAL\n", dev_info); + if (link->state & DEV_CONFIG) { + if (link->open) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + prism2_suspend(dev); + pcmcia_release_configuration(link->handle); + } + break; + + case CS_EVENT_PM_RESUME: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); + link->state &= ~DEV_SUSPEND; + /* fall through */ + + case CS_EVENT_CARD_RESET: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_RESET\n", dev_info); + if (link->state & DEV_CONFIG) { + pcmcia_request_configuration(link->handle, + &link->conf); + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, link->open ? 0 : 1); + if (link->open) { + netif_device_attach(dev); + netif_start_queue(dev); + } + } + break; + + default: + PDEBUG(DEBUG_EXTRA, "%s: prism2_event() - unknown event %d\n", + dev_info, event); + break; + } + return 0; +} + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,67) +static struct pcmcia_driver hostap_driver = { + .drv = { + .name = "hostap_cs", + }, + .attach = prism2_attach, + .detach = prism2_detach, + .owner = THIS_MODULE, +}; + +static int __init init_prism2_pccard(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + return pcmcia_register_driver(&hostap_driver); +} + +static void __exit exit_prism2_pccard(void) +{ + pcmcia_unregister_driver(&hostap_driver); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + +#else + +static int __init init_prism2_pccard(void) +{ + servinfo_t serv; + + printk(KERN_INFO "%s: %s\n", dev_info, version); + pcmcia_get_card_services_info(&serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE + "%s: CardServices release does not match!\n", dev_info); + return -1; + } + register_pccard_driver(&dev_info, &prism2_attach, &prism2_detach); + + return 0; +} + + +static void __exit exit_prism2_pccard(void) +{ + unregister_pccard_driver(&dev_info); + while (dev_list) { + PDEBUG(DEBUG_FLOW, "exit_prism2 - detaching device\n"); + if (dev_list->state & DEV_CONFIG) + prism2_release((u_long)dev_list); + prism2_detach(dev_list); + } + + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} +#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,5,67) */ + + +module_init(init_prism2_pccard); +module_exit(exit_prism2_pccard); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_download.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_download.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_download.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_download.c 2004-12-17 23:27:23.000000000 -0500 @@ -0,0 +1,761 @@ +static int prism2_enable_aux_port(struct net_device *dev, int enable) +{ + u16 val, reg; + int i, tries; + unsigned long flags; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (local->no_pri) { + if (enable) { + PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " + "port is already enabled\n", dev->name); + } + return 0; + } + + spin_lock_irqsave(&local->cmdlock, flags); + + /* wait until busy bit is clear */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + spin_unlock_irqrestore(&local->cmdlock, flags); + printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", + dev->name, reg); + return -ETIMEDOUT; + } + + val = HFA384X_INW(HFA384X_CONTROL_OFF); + + if (enable) { + HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) + printk("prism2_enable_aux_port: was not disabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_ENABLE; + } else { + HFA384X_OUTW(0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) + printk("prism2_enable_aux_port: was not enabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_DISABLE; + } + HFA384X_OUTW(val, HFA384X_CONTROL_OFF); + + udelay(5); + + i = 10000; + while (i > 0) { + val = HFA384X_INW(HFA384X_CONTROL_OFF); + val &= HFA384X_AUX_PORT_MASK; + + if ((enable && val == HFA384X_AUX_PORT_ENABLED) || + (!enable && val == HFA384X_AUX_PORT_DISABLED)) + break; + + udelay(10); + i--; + } + + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (i == 0) { + printk("prism2_enable_aux_port(%d) timed out\n", + enable); + return -ETIMEDOUT; + } + + return 0; +} + + +static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + u16 *pos = (u16 *) buf; + while (len > 0) { + *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + u16 *pos = (u16 *) buf; + while (len > 0) { + HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int prism2_pda_ok(u8 *buf) +{ + u16 *pda = (u16 *) buf; + int pos; + u16 len, pdr; + + if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && + buf[3] == 0x00) + return 0; + + pos = 0; + while (pos + 1 < PRISM2_PDA_SIZE / 2) { + len = le16_to_cpu(pda[pos]); + pdr = le16_to_cpu(pda[pos + 1]); + if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) + return 0; + + if (pdr == 0x0000 && len == 2) { + /* PDA end found */ + return 1; + } + + pos += len + 1; + } + + return 0; +} + + +static int prism2_download_aux_dump(struct net_device *dev, + unsigned int addr, int len, u8 *buf) +{ + int res; + + prism2_enable_aux_port(dev, 1); + res = hfa384x_from_aux(dev, addr, len, buf); + prism2_enable_aux_port(dev, 0); + if (res) + return -1; + + return 0; +} + + +static u8 * prism2_read_pda(struct net_device *dev) +{ + u8 *buf; + int res, i, found = 0; +#define NUM_PDA_ADDRS 4 + unsigned int pda_addr[NUM_PDA_ADDRS] = { + 0x7f0000 /* others than HFA3841 */, + 0x3f0000 /* HFA3841 */, + 0x390000 /* apparently used in older cards */, + 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, + }; + + buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); + if (buf == NULL) + return NULL; + + /* Note: wlan card should be in initial state (just after init cmd) + * and no other operations should be performed concurrently. */ + + prism2_enable_aux_port(dev, 1); + + for (i = 0; i < NUM_PDA_ADDRS; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", + dev->name, pda_addr[i]); + res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); + if (res) + continue; + if (res == 0 && prism2_pda_ok(buf)) { + PDEBUG2(DEBUG_EXTRA2, ": OK\n"); + found = 1; + break; + } else { + PDEBUG2(DEBUG_EXTRA2, ": failed\n"); + } + } + + prism2_enable_aux_port(dev, 0); + + if (!found) { + printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); + kfree(buf); + buf = NULL; + } + + return buf; +} + + +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_data *param) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + u16 param0, param1; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + local->hw_downloading = 1; + if (local->pri_only) { + hfa384x_disable_interrupts(dev); + } else { + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for" + " download\n", dev->name); + ret = -1; + goto out; + } + } + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + param0 = param->start_addr & 0xffff; + param1 = param->start_addr >> 16; + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), + param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, param->data[i].data)) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -1; + goto out; + } + } + + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + /* ProgMode disable causes the hardware to restart itself from the + * given starting address. Give hw some time and ACK command just in + * case restart did not happen. */ + mdelay(5); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after RAM " + "download failed\n", dev->name); + ret = -1; + goto out; + } + + out: + local->hw_downloading = 0; + return ret; +} + + +static int prism2_enable_genesis(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; + u8 readbuf[4]; + + printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", + dev->name, hcr); + local->func->cor_sreset(local); + hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); + local->func->genesis_reset(local, hcr); + + /* Readback test */ + hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); + hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); + hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); + + if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { + printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", + hcr); + return 0; + } else { + printk(KERN_DEBUG "Readback test failed, HCR 0x%02x " + "write %02x %02x %02x %02x read %02x %02x %02x %02x\n", + hcr, initseq[0], initseq[1], initseq[2], initseq[3], + readbuf[0], readbuf[1], readbuf[2], readbuf[3]); + return 1; + } +} + + +static int prism2_get_ram_size(local_info_t *local) +{ + int ret; + + /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ + if (prism2_enable_genesis(local, 0x1f) == 0) + ret = 8; + else if (prism2_enable_genesis(local, 0x0f) == 0) + ret = 16; + else + ret = -1; + + /* Disable genesis mode */ + local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); + + return ret; +} + + +static int prism2_download_genesis(local_info_t *local, + struct prism2_download_data *param) +{ + struct net_device *dev = local->dev; + int ram16 = 0, i; + int ret = 0; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -EBUSY; + } + + if (!local->func->genesis_reset || !local->func->cor_sreset) { + printk(KERN_INFO "%s: Genesis mode downloading not supported " + "with this hwmodel\n", dev->name); + return -EOPNOTSUPP; + } + + local->hw_downloading = 1; + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_DEBUG "%s: failed to enable AUX port\n", + dev->name); + ret = -EIO; + goto out; + } + + if (local->sram_type == -1) { + /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ + if (prism2_enable_genesis(local, 0x1f) == 0) { + ram16 = 0; + PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " + "SRAM\n", dev->name); + } else if (prism2_enable_genesis(local, 0x0f) == 0) { + ram16 = 1; + PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " + "SRAM\n", dev->name); + } else { + printk(KERN_DEBUG "%s: Could not initiate genesis " + "mode\n", dev->name); + ret = -EIO; + goto out; + } + } else { + if (prism2_enable_genesis(local, local->sram_type == 8 ? + 0x1f : 0x0f)) { + printk(KERN_DEBUG "%s: Failed to set Genesis " + "mode (sram_type=%d)\n", dev->name, + local->sram_type); + ret = -EIO; + goto out; + } + ram16 = local->sram_type != 8; + } + + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, param->data[i].data)) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -EIO; + goto out; + } + } + + PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); + local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Failed to disable AUX port\n", + dev->name); + } + + mdelay(5); + local->hw_downloading = 0; + + PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); + /* Make sure the INIT command does not generate a command completion + * event by disabling interrupts. */ + hfa384x_disable_interrupts(dev); + if (prism2_hw_init(dev, 1)) { + printk(KERN_DEBUG "%s: Initialization after genesis mode " + "download failed\n", dev->name); + ret = -EIO; + goto out; + } + + PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); + if (prism2_hw_init2(dev, 1)) { + printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " + "download failed\n", dev->name); + ret = -EIO; + goto out; + } + + out: + local->hw_downloading = 0; + return ret; +} + + +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD +/* Note! Non-volatile downloading functionality has not yet been tested + * thoroughly and it may corrupt flash image and effectively kill the card that + * is being updated. You have been warned. */ + +static inline int prism2_download_block(struct net_device *dev, + u32 addr, u8 *data, + u32 bufaddr, int rest_len) +{ + u16 param0, param1; + int block_len; + + block_len = rest_len < 4096 ? rest_len : 4096; + + param0 = addr & 0xffff; + param1 = addr >> 16; + + HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), + param0)) { + printk(KERN_WARNING "%s: Flash download command execution " + "failed\n", dev->name); + return -1; + } + + if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { + printk(KERN_WARNING "%s: flash download at 0x%08x " + "(len=%d) failed\n", dev->name, addr, block_len); + return -1; + } + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), + 0)) { + printk(KERN_WARNING "%s: Flash write command execution " + "failed\n", dev->name); + return -1; + } + + return block_len; +} + + +static int prism2_download_nonvolatile(local_info_t *local, + struct prism2_download_data *dl) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + struct { + u16 page; + u16 offset; + u16 len; + } dlbuffer; + u32 bufaddr; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, + &dlbuffer, 6, 0); + + if (ret < 0) { + printk(KERN_WARNING "%s: Could not read download buffer " + "parameters\n", dev->name); + goto out; + } + + dlbuffer.page = le16_to_cpu(dlbuffer.page); + dlbuffer.offset = le16_to_cpu(dlbuffer.offset); + dlbuffer.len = le16_to_cpu(dlbuffer.len); + + printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", + dlbuffer.len, dlbuffer.page, dlbuffer.offset); + + bufaddr = (dlbuffer.page << 7) + dlbuffer.offset; + + local->hw_downloading = 1; + + if (!local->pri_only) { + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for" + " download\n", dev->name); + ret = -1; + goto out; + } + } + + hfa384x_disable_interrupts(dev); + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + printk(KERN_DEBUG "%s: starting flash download\n", dev->name); + for (i = 0; i < dl->num_areas; i++) { + int rest_len = dl->data[i].len; + int data_off = 0; + + while (rest_len > 0) { + int block_len; + + block_len = prism2_download_block( + dev, dl->data[i].addr + data_off, + dl->data[i].data + data_off, bufaddr, + rest_len); + + if (block_len < 0) { + ret = -1; + goto out; + } + + rest_len -= block_len; + data_off += block_len; + } + } + + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), 0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + + local->func->hw_reset(dev); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after flash " + "download failed\n", dev->name); + ret = -1; + } else { + printk(KERN_INFO "%s: Card initialized successfully after " + "flash download\n", dev->name); + } + + out: + local->hw_downloading = 0; + return ret; +} +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + + +static void prism2_download_free_data(struct prism2_download_data *dl) +{ + int i; + + if (dl == NULL) + return; + + for (i = 0; i < dl->num_areas; i++) + kfree(dl->data[i].data); + kfree(dl); +} + + +static int prism2_download(local_info_t *local, + struct prism2_download_param *param) +{ + int ret = 0; + int i; + u32 total_len = 0; + struct prism2_download_data *dl = NULL; + + printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " + "num_areas=%d\n", + param->dl_cmd, param->start_addr, param->num_areas); + + if (param->num_areas > 100) { + ret = -EINVAL; + goto out; + } + + dl = kmalloc(sizeof(*dl) + param->num_areas * + sizeof(struct prism2_download_data_area), GFP_KERNEL); + if (dl == NULL) { + ret = -ENOMEM; + goto out; + } + memset(dl, 0, sizeof(*dl) + param->num_areas * + sizeof(struct prism2_download_data_area)); + dl->dl_cmd = param->dl_cmd; + dl->start_addr = param->start_addr; + dl->num_areas = param->num_areas; + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, + " area %d: addr=0x%08x len=%d ptr=0x%p\n", + i, param->data[i].addr, param->data[i].len, + param->data[i].ptr); + + dl->data[i].addr = param->data[i].addr; + dl->data[i].len = param->data[i].len; + + total_len += param->data[i].len; + if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || + total_len > PRISM2_MAX_DOWNLOAD_LEN) { + ret = -E2BIG; + goto out; + } + + dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); + if (dl->data[i].data == NULL) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(dl->data[i].data, param->data[i].ptr, + param->data[i].len)) { + ret = -EFAULT; + goto out; + } + } + + switch (param->dl_cmd) { + case PRISM2_DOWNLOAD_VOLATILE: + case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: + ret = prism2_download_volatile(local, dl); + break; + case PRISM2_DOWNLOAD_VOLATILE_GENESIS: + case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: + ret = prism2_download_genesis(local, dl); + break; + case PRISM2_DOWNLOAD_NON_VOLATILE: +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD + ret = prism2_download_nonvolatile(local, dl); +#else /* PRISM2_NON_VOLATILE_DOWNLOAD */ + printk(KERN_INFO "%s: non-volatile downloading not enabled\n", + local->dev->name); + ret = -EOPNOTSUPP; +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + break; + default: + printk(KERN_DEBUG "%s: unsupported download command %d\n", + local->dev->name, param->dl_cmd); + ret = -EINVAL; + break; + }; + + out: + if (ret == 0 && dl && + param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { + prism2_download_free_data(local->dl_pri); + local->dl_pri = dl; + } else if (ret == 0 && dl && + param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { + prism2_download_free_data(local->dl_sec); + local->dl_sec = dl; + } else + prism2_download_free_data(dl); + + return ret; +} diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap.h 2003-11-29 21:14:26.000000000 -0500 @@ -0,0 +1,57 @@ +#ifndef HOSTAP_H +#define HOSTAP_H + +/* hostap.c */ + +extern struct proc_dir_entry *hostap_proc; + +u16 hostap_tx_callback_register(local_info_t *local, + void (*func)(struct sk_buff *, int ok, void *), + void *data); +int hostap_tx_callback_unregister(local_info_t *local, u16 idx); +int hostap_set_word(struct net_device *dev, int rid, u16 val); +int hostap_set_string(struct net_device *dev, int rid, const char *val); +u16 hostap_get_porttype(local_info_t *local); +int hostap_set_encryption(local_info_t *local); +int hostap_set_antsel(local_info_t *local); +int hostap_set_roaming(local_info_t *local); +int hostap_set_auth_algs(local_info_t *local); +void hostap_dump_rx_header(const char *name, + const struct hfa384x_rx_frame *rx); +void hostap_dump_tx_header(const char *name, + const struct hfa384x_tx_frame *tx); +int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr); +int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr); +int hostap_80211_get_hdrlen(u16 fc); +struct net_device_stats *hostap_get_stats(struct net_device *dev); +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int main_dev); +void hostap_set_multicast_list_queue(void *data); +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked); +int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked); +void hostap_cleanup(local_info_t *local); +void hostap_cleanup_handler(void *data); +struct net_device * hostap_add_interface(struct local_info *local, + int type, int rtnl_locked, + const char *prefix, const char *name); +void hostap_remove_interface(struct net_device *dev, int rtnl_locked, + int remove_from_list); +int prism2_update_comms_qual(struct net_device *dev); +int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u8 stype, + u8 *body, size_t bodylen); +int prism2_sta_deauth(local_info_t *local, u16 reason); + + +/* hostap_proc.c */ + +void hostap_init_proc(local_info_t *local); +void hostap_remove_proc(local_info_t *local); + + +/* hostap_info.c */ + +void hostap_info_init(local_info_t *local); +void hostap_info_process(local_info_t *local, struct sk_buff *skb); + + +#endif /* HOSTAP_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_hw.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_hw.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_hw.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_hw.c 2005-02-05 02:20:09.000000000 -0500 @@ -0,0 +1,3561 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * FIX: + * - there is currently no way of associating TX packets to correct wds device + * when TX Exc/OK event occurs, so all tx_packets and some + * tx_errors/tx_dropped are added to the main netdevice; using sw_support + * field in txdesc might be used to fix this (using Alloc event to increment + * tx_packets would need some further info in txfid table) + * + * Buffer Access Path (BAP) usage: + * Prism2 cards have two separate BAPs for accessing the card memory. These + * should allow concurrent access to two different frames and the driver + * previously used BAP0 for sending data and BAP1 for receiving data. + * However, there seems to be number of issues with concurrent access and at + * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI + * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between + * host and card memories. BAP0 accesses are protected with local->baplock + * (spin_lock_bh) to prevent concurrent use. + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostap_wext.h" +#include + + +#include "hostap_80211.h" +#include "hostap.h" +#include "hostap_ap.h" + + +/* #define final_version */ + +static int mtu = 1500; +MODULE_PARM(mtu, "i"); +MODULE_PARM_DESC(mtu, "Maximum transfer unit"); + +static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS }; +MODULE_PARM(channel, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(channel, "Initial channel"); + +static char *essid[MAX_PARM_DEVICES] = { "test" }; +MODULE_PARM(essid, PARM_MIN_MAX "s"); +MODULE_PARM_DESC(essid, "Host AP's ESSID"); + +static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS }; +MODULE_PARM(iw_mode, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(iw_mode, "Initial operation mode"); + +static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +MODULE_PARM(beacon_int, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)"); + +static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +MODULE_PARM(dtim_period, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(dtim_period, "DTIM period"); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +static int bus_master_threshold_rx[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +MODULE_PARM(bus_master_threshold_rx, "i"); +MODULE_PARM_DESC(bus_master_threshold_rx, "Packet length threshold for using " + "PCI bus master on RX"); + +static int bus_master_threshold_tx[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +MODULE_PARM(bus_master_threshold_tx, "i"); +MODULE_PARM_DESC(bus_master_threshold_tx, "Packet length threshold for using " + "PCI bus master on TX"); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + +static char *dev_template = "wlan%d"; +MODULE_PARM(dev_template, "s"); +MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: " + "wlan%d)"); + +#ifdef final_version +#define EXTRA_EVENTS_WTERR 0 +#else +/* check WTERR events (Wait Time-out) in development versions */ +#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR +#endif + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +#define EXTRA_EVENTS_BUS_MASTER (HFA384X_EV_PCI_M0 | HFA384X_EV_PCI_M1) +#else +#define EXTRA_EVENTS_BUS_MASTER 0 +#endif + +/* Events that will be using BAP0 */ +#define HFA384X_BAP0_EVENTS \ + (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX) + +/* event mask, i.e., events that will result in an interrupt */ +#define HFA384X_EVENT_MASK \ + (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ + HFA384X_EV_CMD | HFA384X_EV_TICK | \ + EXTRA_EVENTS_WTERR | EXTRA_EVENTS_BUS_MASTER) + +/* Default TX control flags: use 802.11 headers and request interrupt for + * failed transmits. Frames that request ACK callback, will add + * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. + */ +#define HFA384X_TX_CTRL_FLAGS \ + (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX) + + +/* ca. 1 usec */ +#define HFA384X_CMD_BUSY_TIMEOUT 5000 +#define HFA384X_BAP_BUSY_TIMEOUT 50000 + +/* ca. 10 usec */ +#define HFA384X_CMD_COMPL_TIMEOUT 20000 +#define HFA384X_DL_COMPL_TIMEOUT 1000000 + +/* Wait times for initialization; yield to other processes to avoid busy + * waiting for long time. */ +#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */ +#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */ + + +static void prism2_hw_reset(struct net_device *dev); +static void prism2_check_sta_fw_version(local_info_t *local); + +#ifdef PRISM2_DOWNLOAD_SUPPORT +/* hostap_download.c */ +static int prism2_download_aux_dump(struct net_device *dev, + unsigned int addr, int len, u8 *buf); +static u8 * prism2_read_pda(struct net_device *dev); +static int prism2_download(local_info_t *local, + struct prism2_download_param *param); +static void prism2_download_free_data(struct prism2_download_data *dl); +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_data *param); +static int prism2_download_genesis(local_info_t *local, + struct prism2_download_data *param); +static int prism2_get_ram_size(local_info_t *local); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + + + +#ifndef final_version +/* magic value written to SWSUPPORT0 reg. for detecting whether card is still + * present */ +#define HFA384X_MAGIC 0x8A32 +#endif + + +static u16 hfa384x_read_reg(struct net_device *dev, u16 reg) +{ + return HFA384X_INW(reg); +} + + +static void hfa384x_read_regs(struct net_device *dev, + struct hfa384x_regs *regs) +{ + regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); + regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); + regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); + regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF); +} + + +/** + * __hostap_cmd_queue_free - Free Prism2 command queue entry (private) + * @local: pointer to private Host AP driver data + * @entry: Prism2 command queue entry to be freed + * @del_req: request the entry to be removed + * + * Internal helper function for freeing Prism2 command queue entries. + * Caller must have acquired local->cmdlock before calling this function. + */ +static inline void __hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + if (del_req) { + entry->del_req = 1; + if (!list_empty(&entry->list)) { + list_del_init(&entry->list); + local->cmd_queue_len--; + } + } + + if (atomic_dec_and_test(&entry->usecnt) && entry->del_req) + kfree(entry); +} + + +/** + * hostap_cmd_queue_free - Free Prism2 command queue entry + * @local: pointer to private Host AP driver data + * @entry: Prism2 command queue entry to be freed + * @del_req: request the entry to be removed + * + * Free a Prism2 command queue entry. + */ +static inline void hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + unsigned long flags; + + spin_lock_irqsave(&local->cmdlock, flags); + __hostap_cmd_queue_free(local, entry, del_req); + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +/** + * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries + * @local: pointer to private Host AP driver data + */ +static void prism2_clear_cmd_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + unsigned long flags; + struct hostap_cmd_queue *entry; + + spin_lock_irqsave(&local->cmdlock, flags); + list_for_each_safe(ptr, n, &local->cmd_queue) { + entry = list_entry(ptr, struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + printk(KERN_DEBUG "%s: removed pending cmd_queue entry " + "(type=%d, cmd=0x%04x, param0=0x%04x)\n", + local->dev->name, entry->type, entry->cmd, + entry->param0); + __hostap_cmd_queue_free(local, entry, 1); + } + if (local->cmd_queue_len) { + /* This should not happen; print debug message and clear + * queue length. */ + printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " + "flush\n", local->dev->name, local->cmd_queue_len); + local->cmd_queue_len = 0; + } + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +/** + * hfa384x_cmd_issue - Issue a Prism2 command to the hardware + * @dev: pointer to net_device + * @entry: Prism2 command queue entry to be issued + */ +static inline int hfa384x_cmd_issue(struct net_device *dev, + struct hostap_cmd_queue *entry) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int tries; + u16 reg; + unsigned long flags; + + if (local->func->card_present && !local->func->card_present(local)) + return -ENODEV; + + if (entry->issued) { + printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", + dev->name, entry); + } + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } +#ifndef final_version + if (tries != HFA384X_CMD_BUSY_TIMEOUT) { + prism2_io_debug_error(dev, 1); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " + "for %d usec\n", dev->name, + HFA384X_CMD_BUSY_TIMEOUT - tries); + } +#endif + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, 2); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + /* write command */ + spin_lock_irqsave(&local->cmdlock, flags); + HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); + entry->issued = 1; + spin_unlock_irqrestore(&local->cmdlock, flags); + + return 0; +} + + +/** + * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @param1: value for Param1 register (pointer; %NULL if not used) + * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed + * + * Issue given command (possibly after waiting in command queue) and sleep + * until the command is completed (or timed out or interrupted). This can be + * called only from user process context. + */ +static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, + u16 *param1, u16 *resp0) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int err, res, issue, issued = 0; + unsigned long flags; + struct hostap_cmd_queue *entry; + DECLARE_WAITQUEUE(wait, current); + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt " + "context\n", dev->name); + return -1; + } + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + if (signal_pending(current)) + return -EINTR; + + entry = (struct hostap_cmd_queue *) + kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_DEBUG "%s: hfa384x_cmd - kmalloc failed\n", + dev->name); + return -ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + atomic_set(&entry->usecnt, 1); + entry->type = CMD_SLEEP; + entry->cmd = cmd; + entry->param0 = param0; + if (param1) + entry->param1 = *param1; + init_waitqueue_head(&entry->compl); + + /* prepare to wait for command completion event, but do not sleep yet + */ + add_wait_queue(&entry->compl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + err = 0; + if (!issue) + goto wait_completion; + + if (signal_pending(current)) + err = -EINTR; + + if (!err) { + if (hfa384x_cmd_issue(dev, entry)) + err = -ETIMEDOUT; + else + issued = 1; + } + + wait_completion: + if (!err && entry->type != CMD_COMPLETED) { + /* sleep until command is completed or timed out */ + res = schedule_timeout(2 * HZ); + } else + res = -1; + + if (!err && signal_pending(current)) + err = -EINTR; + + if (err && issued) { + /* the command was issued, so a CmdCompl event should occur + * soon; however, there's a pending signal and + * schedule_timeout() would be interrupted; wait a short period + * of time to avoid removing entry from the list before + * CmdCompl event */ + udelay(300); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&entry->compl, &wait); + + /* If entry->list is still in the list, it must be removed + * first and in this case prism2_cmd_ev() does not yet have + * local reference to it, and the data can be kfree()'d + * here. If the command completion event is still generated, + * it will be assigned to next (possibly) pending command, but + * the driver will reset the card anyway due to timeout + * + * If the entry is not in the list prism2_cmd_ev() has a local + * reference to it, but keeps cmdlock as long as the data is + * needed, so the data can be kfree()'d here. */ + + /* FIX: if the entry->list is in the list, it has not been completed + * yet, so removing it here is somewhat wrong.. this could cause + * references to freed memory and next list_del() causing NULL pointer + * dereference.. it would probably be better to leave the entry in the + * list and the list should be emptied during hw reset */ + + spin_lock_irqsave(&local->cmdlock, flags); + if (!list_empty(&entry->list)) { + printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " + "(entry=%p, type=%d, res=%d)\n", dev->name, entry, + entry->type, res); + list_del_init(&entry->list); + local->cmd_queue_len--; + } + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (err) { + printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", + dev->name, err); + res = err; + goto done; + } + + if (entry->type != CMD_COMPLETED) { + u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " + "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " + "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name, + res, entry, entry->type, entry->cmd, entry->param0, reg, + HFA384X_INW(HFA384X_INTEN_OFF)); + if (reg & HFA384X_EV_CMD) { + /* Command completion event is pending, but the + * interrupt was not delivered - probably an issue + * with pcmcia-cs configuration. */ + printk(KERN_WARNING "%s: interrupt delivery does not " + "seem to work\n", dev->name); + } + prism2_io_debug_error(dev, 3); + res = -ETIMEDOUT; + goto done; + } + + if (resp0 != NULL) + *resp0 = entry->resp0; +#ifndef final_version + if (entry->res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " + "resp0=0x%04x\n", + dev->name, cmd, entry->res, entry->resp0); + } +#endif /* final_version */ + + res = entry->res; + done: + hostap_cmd_queue_free(local, entry, 1); + return res; +} + + +/** + * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @callback: command completion callback function (%NULL = no callback) + * @context: data pointer to be given to callback function + * + * Issue given command (possibly after waiting in command queue) and use + * callback function to indicate command completion. This can be called both + * from user and interrupt context. The callback function will be called in + * hardware IRQ context. It can be %NULL, when no function is called when + * command is completed. + */ +static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0, + void (*callback)(struct net_device *dev, + void *context, u16 resp0, + u16 status), + void *context) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int issue, ret; + unsigned long flags; + struct hostap_cmd_queue *entry; + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + entry = (struct hostap_cmd_queue *) + kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_DEBUG "%s: hfa384x_cmd_callback - kmalloc " + "failed\n", dev->name); + return -ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + atomic_set(&entry->usecnt, 1); + entry->type = CMD_CALLBACK; + entry->cmd = cmd; + entry->param0 = param0; + entry->callback = callback; + entry->context = context; + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (issue && hfa384x_cmd_issue(dev, entry)) + ret = -ETIMEDOUT; + else + ret = 0; + + hostap_cmd_queue_free(local, entry, ret); + + return ret; +} + + +/** + * __hfa384x_cmd_no_wait - Issue a Prism2 command (private) + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @io_debug_num: I/O debug error number + * + * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait(). + */ +static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0, + int io_debug_num) +{ + int tries; + u16 reg; + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, io_debug_num); + printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - " + "reg=0x%04x\n", dev->name, io_debug_num, reg); + return -ETIMEDOUT; + } + + /* write command */ + HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(cmd, HFA384X_CMD_OFF); + + return 0; +} + + +/** + * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + */ +static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0) +{ + int res, tries; + u16 reg; + + res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4); + if (res) + return res; + + /* wait for command completion */ + if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD) + tries = HFA384X_DL_COMPL_TIMEOUT; + else + tries = HFA384X_CMD_COMPL_TIMEOUT; + + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + tries > 0) { + tries--; + udelay(10); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + prism2_io_debug_error(dev, 5); + printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) | + BIT(8))) >> 8; +#ifndef final_version + if (res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n", + dev->name, cmd, res); + } +#endif + + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + return res; +} + + +/** + * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + */ +static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, + u16 param0) +{ + return __hfa384x_cmd_no_wait(dev, cmd, param0, 6); +} + + +/** + * prism2_cmd_ev - Prism2 command completion event handler + * @dev: pointer to net_device + * + * Interrupt handler for command completion events. Called by the main + * interrupt handler in hardware IRQ context. Read Resp0 and status registers + * from the hardware and ACK the event. Depending on the issued command type + * either wake up the sleeping process that is waiting for command completion + * or call the callback function. Issue the next command, if one is pending. + */ +static void prism2_cmd_ev(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hostap_cmd_queue *entry = NULL; + + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + list_del_init(&entry->list); + local->cmd_queue_len--; + + if (!entry->issued) { + printk(KERN_DEBUG "%s: Command completion event, but " + "cmd not issued\n", dev->name); + __hostap_cmd_queue_free(local, entry, 1); + entry = NULL; + } + } + spin_unlock(&local->cmdlock); + + if (!entry) { + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: Command completion event, but no " + "pending commands\n", dev->name); + return; + } + + entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF); + entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | + BIT(9) | BIT(8))) >> 8; + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + /* TODO: rest of the CmdEv handling could be moved to tasklet */ + if (entry->type == CMD_SLEEP) { + entry->type = CMD_COMPLETED; + wake_up_interruptible(&entry->compl); + } else if (entry->type == CMD_CALLBACK) { + if (entry->callback) + entry->callback(dev, entry->context, entry->resp0, + entry->res); + } else { + printk(KERN_DEBUG "%s: Invalid command completion type %d\n", + dev->name, entry->type); + } + hostap_cmd_queue_free(local, entry, 1); + + /* issue next command, if pending */ + entry = NULL; + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + if (entry->issuing) { + /* hfa384x_cmd() has already started issuing this + * command, so do not start here */ + entry = NULL; + } + if (entry) + atomic_inc(&entry->usecnt); + } + spin_unlock(&local->cmdlock); + + if (entry) { + /* issue next command; if command issuing fails, remove the + * entry from cmd_queue */ + int res = hfa384x_cmd_issue(dev, entry); + spin_lock(&local->cmdlock); + __hostap_cmd_queue_free(local, entry, res); + spin_unlock(&local->cmdlock); + } +} + + +static inline int hfa384x_wait_offset(struct net_device *dev, u16 o_off) +{ + int tries = HFA384X_BAP_BUSY_TIMEOUT; + int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; + + while (res && tries > 0) { + tries--; + udelay(1); + res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; + } + return res; +} + + +/* Offset must be even */ +static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id, + int offset) +{ + u16 o_off, s_off; + int ret = 0; + + if (offset % 2 || bap > 1) + return -EINVAL; + + if (bap == BAP1) { + o_off = HFA384X_OFFSET1_OFF; + s_off = HFA384X_SELECT1_OFF; + } else { + o_off = HFA384X_OFFSET0_OFF; + s_off = HFA384X_SELECT0_OFF; + } + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 7); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } + + HFA384X_OUTW(id, s_off); + HFA384X_OUTW(offset, o_off); + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 8); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } +#ifndef final_version + if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) { + prism2_io_debug_error(dev, 9); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error " + "(%d,0x04%x,%d); reg=0x%04x\n", + dev->name, bap, id, offset, HFA384X_INW(o_off)); + ret = -EINVAL; + } +#endif + + out: + return ret; +} + + +static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int res, rlen = 0; + struct hfa384x_rid_hdr rec; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI " + "f/w\n", dev->name, rid, len); + return -ENOTTY; /* Well.. not really correct, but return + * something unique enough.. */ + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + res = down_interruptible(&local->rid_bap_sem); + if (res) + return res; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed " + "(res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + up(&local->rid_bap_sem); + return res; + } + + spin_lock_bh(&local->baplock); + + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + + if (le16_to_cpu(rec.len) == 0) { + /* RID not available */ + res = -ENODATA; + } + + rlen = (le16_to_cpu(rec.len) - 1) * 2; + if (!res && exact_len && rlen != len) { + printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " + "rid=0x%04x, len=%d (expected %d)\n", + dev->name, rid, rlen, len); + res = -ENODATA; + } + + if (!res) + res = hfa384x_from_bap(dev, BAP0, buf, len); + + spin_unlock_bh(&local->baplock); + up(&local->rid_bap_sem); + + if (res) { + if (res != -ENODATA) + printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, " + "len=%d) - failed - res=%d\n", dev->name, rid, + len, res); + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + return res; + } + + return rlen; +} + + +static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hfa384x_rid_hdr rec; + int res; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI " + "f/w\n", dev->name, rid, len); + return -ENOTTY; /* Well.. not really correct, but return + * something unique enough.. */ + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + rec.rid = cpu_to_le16(rid); + /* RID len in words and +1 for rec.rid */ + rec.len = cpu_to_le16(len / 2 + len % 2 + 1); + + res = down_interruptible(&local->rid_bap_sem); + if (res) + return res; + + spin_lock_bh(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, buf, len); + spin_unlock_bh(&local->baplock); + + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - " + "failed - res=%d\n", dev->name, rid, len, res); + up(&local->rid_bap_sem); + return res; + } + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL); + up(&local->rid_bap_sem); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE " + "failed (res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + return res; + } + + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + + return res; +} + + +static void hfa384x_disable_interrupts(struct net_device *dev) +{ + /* disable interrupts and clear event status */ + HFA384X_OUTW(0, HFA384X_INTEN_OFF); + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); +} + + +static void hfa384x_enable_interrupts(struct net_device *dev) +{ + /* ack pending events and enable interrupts from selected events */ + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_no_bap0(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS, + HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_all(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_only_cmd(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF); +} + + +static u16 hfa384x_allocate_fid(struct net_device *dev, int len) +{ + u16 fid; + unsigned long delay; + + /* FIX: this could be replace with hfa384x_cmd() if the Alloc event + * below would be handled like CmdCompl event (sleep here, wake up from + * interrupt handler */ + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) { + printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n", + dev->name, len); + return 0xffff; + } + + delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) && + time_before(jiffies, delay)) + yield(); + if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) { + printk("%s: fid allocate, len=%d - timeout\n", dev->name, len); + return 0xffff; + } + + fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + + return fid; +} + + +static int prism2_reset_port(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int res; + + if (!local->dev_enabled) + return 0; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to disable port\n", + dev->name); + else { + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to enable " + "port\n", dev->name); + } + + /* It looks like at least some STA firmware versions reset + * fragmentation threshold back to 2346 after enable command. Restore + * the configured value, if it differs from this default. */ + if (local->fragm_threshold != 2346 && + hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + local->fragm_threshold)) { + printk(KERN_DEBUG "%s: failed to restore fragmentation " + "threshold (%d) after Port0 enable\n", + dev->name, local->fragm_threshold); + } + + return res; +} + + +static int prism2_get_version_info(struct net_device *dev, u16 rid, + const char *txt) +{ + struct hfa384x_comp_ident comp; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (local->no_pri) { + /* PRI f/w not yet available - cannot read RIDs */ + return -1; + } + if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) { + printk(KERN_DEBUG "Could not get RID for component %s\n", txt); + return -1; + } + + printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt, + __le16_to_cpu(comp.id), __le16_to_cpu(comp.major), + __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant)); + return 0; +} + + +static int prism2_setup_rids(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 tmp; + int ret = 0; + + hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000); + + if (!local->fw_ap) { + tmp = hostap_get_porttype(local); + ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp); + if (ret) { + printk("%s: Port type setting to %d failed\n", + dev->name, tmp); + goto fail; + } + } + + /* Setting SSID to empty string seems to kill the card in Host AP mode + */ + if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') { + ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, + local->essid); + if (ret) { + printk("%s: AP own SSID setting failed\n", dev->name); + goto fail; + } + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN, + PRISM2_DATA_MAXLEN); + if (ret) { + printk("%s: MAC data length setting to %d failed\n", + dev->name, PRISM2_DATA_MAXLEN); + goto fail; + } + + if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) { + printk("%s: Channel list read failed\n", dev->name); + ret = -EINVAL; + goto fail; + } + local->channel_mask = __le16_to_cpu(tmp); + + if (local->channel < 1 || local->channel > 14 || + !(local->channel_mask & (1 << (local->channel - 1)))) { + printk(KERN_WARNING "%s: Channel setting out of range " + "(%d)!\n", dev->name, local->channel); + ret = -EBUSY; + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel); + if (ret) { + printk("%s: Channel setting to %d failed\n", + dev->name, local->channel); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, + local->beacon_int); + if (ret) { + printk("%s: Beacon interval setting to %d failed\n", + dev->name, local->beacon_int); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, + local->dtim_period); + if (ret) { + printk("%s: DTIM period setting to %d failed\n", + dev->name, local->dtim_period); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc); + if (ret) + printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n", + dev->name, local->is_promisc); + + if (!local->fw_ap) { + ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, + local->essid); + if (ret) { + printk("%s: Desired SSID setting failed\n", dev->name); + goto fail; + } + } + + /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and + * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic + * rates */ + if (local->tx_rate_control == 0) { + local->tx_rate_control = + HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | + HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + } + if (local->basic_rates == 0) + local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS; + + if (!local->fw_ap) { + ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control); + if (ret) { + printk("%s: TXRateControl setting to %d failed\n", + dev->name, local->tx_rate_control); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control); + if (ret) { + printk("%s: cnfSupportedRates setting to %d failed\n", + dev->name, local->tx_rate_control); + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates); + if (ret) { + printk("%s: cnfBasicRates setting to %d failed\n", + dev->name, local->basic_rates); + } + + ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1); + if (ret) { + printk("%s: Create IBSS setting to 1 failed\n", + dev->name); + } + } + + if (local->name_set) + (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, + local->name); + + if (hostap_set_encryption(local)) { + printk(KERN_INFO "%s: could not configure encryption\n", + dev->name); + } + + (void) hostap_set_antsel(local); + + if (hostap_set_roaming(local)) { + printk(KERN_INFO "%s: could not set host roaming\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) && + hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec)) + printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n", + dev->name, local->enh_sec); + + /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently + * not working correctly (last seven counters report bogus values). + * This has been fixed in 0.8.2, so enable 32-bit tallies only + * beginning with that firmware version. Another bug fix for 32-bit + * tallies in 1.4.0; should 16-bit tallies be used for some other + * versions, too? */ + if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) { + if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) { + printk(KERN_INFO "%s: cnfThirty2Tally setting " + "failed\n", dev->name); + local->tallies32 = 0; + } else + local->tallies32 = 1; + } else + local->tallies32 = 0; + + hostap_set_auth_algs(local); + + if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + local->fragm_threshold)) { + printk(KERN_INFO "%s: setting FragmentationThreshold to %d " + "failed\n", dev->name, local->fragm_threshold); + } + + if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD, + local->rts_threshold)) { + printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n", + dev->name, local->rts_threshold); + } + + if (local->manual_retry_count >= 0 && + hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + local->manual_retry_count)) { + printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n", + dev->name, local->manual_retry_count); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) && + hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) { + local->rssi_to_dBm = le16_to_cpu(tmp); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa && + hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) { + printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem && + hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT, + local->generic_elem, local->generic_elem_len)) { + printk(KERN_INFO "%s: setting genericElement failed\n", + dev->name); + } + + fail: + return ret; +} + + +static int prism2_hw_init(struct net_device *dev, int initial) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret, first = 1; + unsigned long start, delay; + + PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n"); + + clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits); + + init: + /* initialize HFA 384x */ + ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0); + if (ret) { + printk(KERN_INFO "%s: first command failed - assuming card " + "does not have primary firmware\n", dev_info); + } + + if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { + /* EvStat has Cmd bit set in some cases, so retry once if no + * wait was needed */ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: init command completed too quickly - " + "retrying\n", dev->name); + first = 0; + goto init; + } + + start = jiffies; + delay = jiffies + HFA384X_INIT_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + time_before(jiffies, delay)) + yield(); + if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { + printk(KERN_DEBUG "%s: assuming no Primary image in " + "flash - card initialization not completed\n", + dev_info); + local->no_pri = 1; +#ifdef PRISM2_DOWNLOAD_SUPPORT + if (local->sram_type == -1) + local->sram_type = prism2_get_ram_size(local); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + return 1; + } + local->no_pri = 0; + printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n", + (jiffies - start) * 1000 / HZ); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + return 0; +} + + +static int prism2_hw_init2(struct net_device *dev, int initial) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int i; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + kfree(local->pda); + if (local->no_pri) + local->pda = NULL; + else + local->pda = prism2_read_pda(dev); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + hfa384x_disable_interrupts(dev); + +#ifndef final_version + HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF); + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + printk("SWSUPPORT0 write/read failed: %04X != %04X\n", + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC); + goto failed; + } +#endif + + if (initial || local->pri_only) { + hfa384x_events_only_cmd(dev); + /* get card version information */ + if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") || + prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) { + hfa384x_disable_interrupts(dev); + goto failed; + } + + if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) { + printk(KERN_DEBUG "%s: Failed to read STA f/w version " + "- only Primary f/w present\n", dev->name); + local->pri_only = 1; + return 0; + } + local->pri_only = 0; + hfa384x_disable_interrupts(dev); + } + + /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and + * enable interrupts before this. This would also require some sort of + * sleeping AllocEv waiting */ + + /* allocate TX FIDs */ + local->txfid_len = PRISM2_TXFID_LEN; + for (i = 0; i < PRISM2_TXFID_COUNT; i++) { + local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len); + if (local->txfid[i] == 0xffff && local->txfid_len > 1600) { + local->txfid[i] = hfa384x_allocate_fid(dev, 1600); + if (local->txfid[i] != 0xffff) { + printk(KERN_DEBUG "%s: Using shorter TX FID " + "(1600 bytes)\n", dev->name); + local->txfid_len = 1600; + } + } + if (local->txfid[i] == 0xffff) + goto failed; + local->intransmitfid[i] = PRISM2_TXFID_EMPTY; + } + + hfa384x_events_only_cmd(dev); + + if (initial) { + struct list_head *ptr; + prism2_check_sta_fw_version(local); + + if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR, + &dev->dev_addr, 6, 1) < 0) { + printk("%s: could not get own MAC address\n", + dev->name); + } + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + memcpy(iface->dev->dev_addr, dev->dev_addr, ETH_ALEN); + } + } else if (local->fw_ap) + prism2_check_sta_fw_version(local); + + prism2_setup_rids(dev); + + /* MAC is now configured, but port 0 is not yet enabled */ + return 0; + + failed: + if (!local->no_pri) + printk(KERN_WARNING "%s: Initialization failed\n", dev_info); + return 1; +} + + +static int prism2_hw_enable(struct net_device *dev, int initial) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int was_resetting = local->hw_resetting; + + if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) { + printk("%s: MAC port 0 enabling failed\n", dev->name); + return 1; + } + + local->hw_ready = 1; + local->hw_reset_tries = 0; + local->hw_resetting = 0; + hfa384x_enable_interrupts(dev); + + /* at least D-Link DWL-650 seems to require additional port reset + * before it starts acting as an AP, so reset port automatically + * here just in case */ + if (initial && prism2_reset_port(dev)) { + printk("%s: MAC port 0 reseting failed\n", dev->name); + return 1; + } + + if (was_resetting && netif_queue_stopped(dev)) { + /* If hw_reset() was called during pending transmit, netif + * queue was stopped. Wake it up now since the wlan card has + * been resetted. */ + netif_wake_queue(dev); + } + + return 0; +} + + +static int prism2_hw_config(struct net_device *dev, int initial) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + if (local->hw_downloading) + return 1; + + if (prism2_hw_init(dev, initial)) { + return local->no_pri ? 0 : 1; + } + + if (prism2_hw_init2(dev, initial)) + return 1; + + /* Enable firmware if secondary image is loaded and at least one of the + * netdevices is up. */ + if (!local->pri_only && + (initial == 0 || (initial == 2 && local->num_dev_open > 0))) { + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + return prism2_hw_enable(dev, initial); + } + + return 0; +} + + +static void prism2_hw_shutdown(struct net_device *dev, int no_disable) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + /* Allow only command completion events during disable */ + hfa384x_events_only_cmd(dev); + + local->hw_ready = 0; + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + local->dev_enabled = 0; + + if (local->func->card_present && !local->func->card_present(local)) { + printk(KERN_DEBUG "%s: card already removed or not configured " + "during shutdown\n", dev->name); + return; + } + + if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 && + hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL)) + printk(KERN_WARNING "%s: Shutdown failed\n", dev_info); + + hfa384x_disable_interrupts(dev); + + if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL) + hfa384x_events_only_cmd(dev); + else + prism2_clear_cmd_queue(local); +} + + +static void prism2_hw_reset(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + +#if 0 + static long last_reset = 0; + + /* do not reset card more than once per second to avoid ending up in a + * busy loop reseting the card */ + if (time_before_eq(jiffies, last_reset + HZ)) + return; + last_reset = jiffies; +#endif + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called " + "in interrupt context\n", dev->name); + return; + } + + if (local->hw_downloading) + return; + + if (local->hw_resetting) { + printk(KERN_WARNING "%s: %s: already resetting card - " + "ignoring reset request\n", dev_info, dev->name); + return; + } + + local->hw_reset_tries++; + if (local->hw_reset_tries > 10) { + printk(KERN_WARNING "%s: too many reset tries, skipping\n", + dev->name); + return; + } + + printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name); + hfa384x_disable_interrupts(dev); + local->hw_resetting = 1; + if (local->func->cor_sreset) { + /* Host system seems to hang in some cases with high traffic + * load or shared interrupts during COR sreset. Disable shared + * interrupts during reset to avoid these crashes. COS sreset + * takes quite a long time, so it is unfortunate that this + * seems to be needed. Anyway, I do not know of any better way + * of avoiding the crash. */ + disable_irq(dev->irq); + local->func->cor_sreset(local); + enable_irq(dev->irq); + } + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, 0); + local->hw_resetting = 0; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + if (local->dl_pri) { + printk(KERN_DEBUG "%s: persistent download of primary " + "firmware\n", dev->name); + if (prism2_download_genesis(local, local->dl_pri) < 0) + printk(KERN_WARNING "%s: download (PRI) failed\n", + dev->name); + } + + if (local->dl_sec) { + printk(KERN_DEBUG "%s: persistent download of secondary " + "firmware\n", dev->name); + if (prism2_download_volatile(local, local->dl_sec) < 0) + printk(KERN_WARNING "%s: download (SEC) failed\n", + dev->name); + } +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + /* TODO: restore beacon TIM bits for STAs that have buffered frames */ +} + + +static void prism2_schedule_reset(local_info_t *local) +{ + PRISM2_SCHEDULE_TASK(&local->reset_queue); +} + + +/* Called only as scheduled task after noticing card timeout in interrupt + * context */ +static void handle_reset_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + + printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name); + prism2_hw_reset(local->dev); + + if (netif_queue_stopped(local->dev)) { + int i; + + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) { + PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: " + "wake up queue\n"); + netif_wake_queue(local->dev); + break; + } + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +static int prism2_get_txfid_idx(local_info_t *local) +{ + int idx, end; + unsigned long flags; + + spin_lock_irqsave(&local->txfidlock, flags); + end = idx = local->next_txfid; + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + local->intransmitfid[idx] = PRISM2_TXFID_RESERVED; + spin_unlock_irqrestore(&local->txfidlock, flags); + return idx; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != end); + spin_unlock_irqrestore(&local->txfidlock, flags); + + PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: " + "packet dropped\n"); + local->stats.tx_dropped++; + + return -1; +} + + +/* Called only from hardware IRQ */ +static void prism2_transmit_cb(struct net_device *dev, void *context, + u16 resp0, u16 res) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int idx = (int) context; + + if (res) { + printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n", + dev->name, res); + return; + } + + if (idx < 0 || idx >= PRISM2_TXFID_COUNT) { + printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid " + "idx=%d\n", dev->name, idx); + return; + } + + if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called " + "with no pending transmit\n", dev->name); + } + + if (netif_queue_stopped(dev)) { + /* ready for next TX, so wake up queue that was stopped in + * prism2_transmit() */ + netif_wake_queue(dev); + } + + spin_lock(&local->txfidlock); + + /* With reclaim, Resp0 contains new txfid for transmit; the old txfid + * will be automatically allocated for the next TX frame */ + local->intransmitfid[idx] = resp0; + + PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, " + "resp0=0x%04x, transmit_txfid=0x%04x\n", + dev->name, idx, local->txfid[idx], + resp0, local->intransmitfid[local->next_txfid]); + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + local->next_txfid = idx; + + /* check if all TX buffers are occupied */ + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + spin_unlock(&local->txfidlock); + return; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_txfid); + spin_unlock(&local->txfidlock); + + /* no empty TX buffers, stop queue */ + netif_stop_queue(dev); +} + + +/* Called only from software IRQ if PCI bus master is not used (with bus master + * this can be called both from software and hardware IRQ) */ +static int prism2_transmit(struct net_device *dev, int idx) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int res; + + /* The driver tries to stop netif queue so that there would not be + * more than one attempt to transmit frames going on; check that this + * is really the case */ + + if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called " + "when previous TX was pending\n", dev->name); + return -1; + } + + /* stop the queue for the time that transmit is pending */ + netif_stop_queue(dev); + + /* transmit packet */ + res = hfa384x_cmd_callback( + dev, + HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM, + local->txfid[idx], + prism2_transmit_cb, (void *) idx); + + if (res) { + struct net_device_stats *stats; + printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT " + "failed (res=%d)\n", dev->name, res); + stats = hostap_get_stats(dev); + stats->tx_dropped++; + netif_wake_queue(dev); + return -1; + } + dev->trans_start = jiffies; + + /* Since we did not wait for command completion, the card continues + * to process on the background and we will finish handling when + * command completion event is handled (prism2_cmd_ev() function) */ + + return 0; +} + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +/* Called only from hardware IRQ */ +static void prism2_tx_cb(struct net_device *dev, void *context, + u16 resp0, u16 res) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long addr; + int buf_len = (int) context; + + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_cb - res=0x%02x\n", + dev->name, res); + return; + } + + addr = virt_to_phys(local->bus_m0_buf); + HFA384X_OUTW((addr & 0xffff0000) >> 16, HFA384X_PCI_M0_ADDRH_OFF); + HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); + HFA384X_OUTW(buf_len / 2, HFA384X_PCI_M0_LEN_OFF); + HFA384X_OUTW(HFA384X_PCI_CTL_TO_BAP, HFA384X_PCI_M0_CTL_OFF); +} +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + +/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and + * send the payload with this descriptor) */ +/* Called only from software IRQ */ +static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hfa384x_tx_frame txdesc; + struct hostap_ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + int hdr_len, data_len, idx, res, ret = -1; + u16 tx_control, fc; + + meta = (struct hostap_skb_tx_data *) skb->cb; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + prism2_callback(local, PRISM2_CALLBACK_TX_START); + + if ((local->func->card_present && !local->func->card_present(local)) || + !local->hw_ready || local->hw_downloading || local->pri_only) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -" + " skipping\n", dev->name); + } + goto fail; + } + + memset(&txdesc, 0, sizeof(txdesc)); + + /* skb->data starts with txdesc->frame_control */ + hdr_len = 24; + memcpy(&txdesc.frame_control, skb->data, hdr_len); + fc = le16_to_cpu(txdesc.frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + (fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS) && skb->len >= 30) { + /* Addr4 */ + memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN); + hdr_len += ETH_ALEN; + } + + tx_control = local->tx_control; + if (meta->tx_cb_idx) { + tx_control |= HFA384X_TX_CTRL_TX_OK; + txdesc.sw_support = cpu_to_le16(meta->tx_cb_idx); + } + txdesc.tx_control = cpu_to_le16(tx_control); + txdesc.tx_rate = meta->rate; + + data_len = skb->len - hdr_len; + txdesc.data_len = cpu_to_le16(data_len); + txdesc.len = cpu_to_be16(data_len); + + idx = prism2_get_txfid_idx(local); + if (idx < 0) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) + hostap_dump_tx_header(dev->name, &txdesc); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (!res && skb->len >= local->bus_master_threshold_tx) { + u8 *pos; + int buf_len; + + local->bus_m0_tx_idx = idx; + + /* FIX: BAP0 should be locked during bus master transfer, but + * baplock with BH's disabled is not OK for this; netif queue + * stopping is not enough since BAP0 is used also for RID + * read/write */ + + /* stop the queue for the time that bus mastering on BAP0 is + * in use */ + netif_stop_queue(dev); + + spin_unlock(&local->baplock); + + /* Copy frame data to bus_m0_buf */ + pos = local->bus_m0_buf; + memcpy(pos, &txdesc, sizeof(txdesc)); + pos += sizeof(txdesc); + memcpy(pos, skb->data + hdr_len, skb->len - hdr_len); + pos += skb->len - hdr_len; + buf_len = pos - local->bus_m0_buf; + if (buf_len & 1) + buf_len++; + +#ifdef PRISM2_ENABLE_BEFORE_TX_BUS_MASTER + /* Any RX packet seems to break something with TX bus + * mastering; enable command is enough to fix this.. */ + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_ENABLE, 0, + prism2_tx_cb, (void *) buf_len)) { + printk(KERN_DEBUG "%s: TX: enable port0 failed\n", + dev->name); + } +#else /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ + prism2_tx_cb(dev, (void *) buf_len, 0, 0); +#endif /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ + + /* Bus master transfer will be started from command completion + * event handler and TX handling will be finished by calling + * prism2_transmit() from bus master event handler */ + goto tx_stats; + } +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + if (!res) + res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, + skb->len - hdr_len); + spin_unlock(&local->baplock); + + if (!res) + res = prism2_transmit(dev, idx); + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", + dev->name); + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + PRISM2_SCHEDULE_TASK(&local->reset_queue); + goto fail; + } + + ret = 0; + +fail: + prism2_callback(local, PRISM2_CALLBACK_TX_END); + return ret; +} + + +/* Some SMP systems have reported number of odd errors with hostap_pci. fid + * register has changed values between consecutive reads for an unknown reason. + * This should really not happen, so more debugging is needed. This test + * version is a big slower, but it will detect most of such register changes + * and will try to get the correct fid eventually. */ +#define EXTRA_FID_READ_TESTS + +static inline u16 prism2_read_fid_reg(struct net_device *dev, u16 reg) +{ +#ifdef EXTRA_FID_READ_TESTS + u16 val, val2, val3; + int i; + + for (i = 0; i < 10; i++) { + val = HFA384X_INW(reg); + val2 = HFA384X_INW(reg); + val3 = HFA384X_INW(reg); + + if (val == val2 && val == val3) + return val; + + printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):" + " %04x %04x %04x\n", + dev->name, i, reg, val, val2, val3); + if ((val == val2 || val == val3) && val != 0) + return val; + if (val2 == val3 && val2 != 0) + return val2; + } + printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg " + "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3); + return val; +#else /* EXTRA_FID_READ_TESTS */ + return HFA384X_INW(reg); +#endif /* EXTRA_FID_READ_TESTS */ +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_rx(local_info_t *local) +{ + struct net_device *dev = local->dev; + int res, rx_pending = 0; + u16 len, hdr_len, rxfid, status, macport; + struct net_device_stats *stats; + struct hfa384x_rx_frame rxdesc; + struct sk_buff *skb = NULL; + + prism2_callback(local, PRISM2_CALLBACK_RX_START); + stats = hostap_get_stats(dev); + + rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF); +#ifndef final_version + if (rxfid == 0) { + rxfid = HFA384X_INW(HFA384X_RXFID_OFF); + printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n", + rxfid); + if (rxfid == 0) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + goto rx_dropped; + } + /* try to continue with the new rxfid value */ + } +#endif + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rxfid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc)); + + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name, + res); + if (res == -ETIMEDOUT) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + goto rx_dropped; + } + + len = le16_to_cpu(rxdesc.data_len); + hdr_len = sizeof(rxdesc); + status = le16_to_cpu(rxdesc.status); + macport = (status >> 8) & 0x07; + + /* Drop frames with too large reported payload length. Monitor mode + * seems to sometimes pass frames (e.g., ctrl::ack) with signed and + * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for + * macport 7 */ + if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) { + if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) { + if (len >= (u16) -14) { + hdr_len -= 65535 - len; + hdr_len--; + } + len = 0; + } else { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received frame with invalid " + "length 0x%04x\n", dev->name, len); + hostap_dump_rx_header(dev->name, &rxdesc); + goto rx_dropped; + } + } + + skb = dev_alloc_skb(len + hdr_len); + if (!skb) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: RX failed to allocate skb\n", + dev->name); + goto rx_dropped; + } + skb->dev = dev; + memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (len >= local->bus_master_threshold_rx) { + unsigned long addr; + + hfa384x_events_no_bap1(dev); + + local->rx_skb = skb; + /* Internal BAP0 offset points to the byte following rxdesc; + * copy rest of the data using bus master */ + addr = virt_to_phys(skb_put(skb, len)); + HFA384X_OUTW((addr & 0xffff0000) >> 16, + HFA384X_PCI_M0_ADDRH_OFF); + HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); + if (len & 1) + len++; + HFA384X_OUTW(len / 2, HFA384X_PCI_M0_LEN_OFF); + HFA384X_OUTW(HFA384X_PCI_CTL_FROM_BAP, HFA384X_PCI_M0_CTL_OFF); + + /* pci_bus_m1 event will be generated when data transfer is + * complete and the frame will then be added to rx_list and + * rx_tasklet is scheduled */ + rx_pending = 1; + + /* Have to release baplock before returning, although BAP0 + * should really not be used before DMA transfer has been + * completed. */ + spin_unlock(&local->baplock); + } else +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + { + if (len > 0) + res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), + len); + spin_unlock(&local->baplock); + if (res) { + printk(KERN_DEBUG "%s: RX failed to read " + "frame data\n", dev->name); + goto rx_dropped; + } + + skb_queue_tail(&local->rx_list, skb); + tasklet_schedule(&local->rx_tasklet); + } + + rx_exit: + prism2_callback(local, PRISM2_CALLBACK_RX_END); + if (!rx_pending) { + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } + + return; + + rx_dropped: + stats->rx_dropped++; + if (skb) + dev_kfree_skb(skb); + goto rx_exit; +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_rx_frame *rxdesc; + struct net_device *dev = skb->dev; + struct hostap_80211_rx_status stats; + int hdrlen, rx_hdrlen; + + rx_hdrlen = sizeof(*rxdesc); + if (skb->len < sizeof(*rxdesc)) { + /* Allow monitor mode to receive shorter frames */ + if (local->iw_mode == IW_MODE_MONITOR && + skb->len >= sizeof(*rxdesc) - 30) { + rx_hdrlen = skb->len; + } else { + dev_kfree_skb(skb); + return; + } + } + + rxdesc = (struct hfa384x_rx_frame *) skb->data; + + if (local->frame_dump & PRISM2_DUMP_RX_HDR && + skb->len >= sizeof(*rxdesc)) + hostap_dump_rx_header(dev->name, rxdesc); + + if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR && + (!local->monitor_allow_fcserr || + local->iw_mode != IW_MODE_MONITOR)) + goto drop; + + if (skb->len > PRISM2_DATA_MAXLEN) { + printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n", + dev->name, skb->len, PRISM2_DATA_MAXLEN); + goto drop; + } + + stats.mac_time = le32_to_cpu(rxdesc->time); + stats.signal = rxdesc->signal - local->rssi_to_dBm; + stats.noise = rxdesc->silence - local->rssi_to_dBm; + stats.rate = rxdesc->rate; + + /* Convert Prism2 RX structure into IEEE 802.11 header */ + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(rxdesc->frame_control)); + if (hdrlen > rx_hdrlen) + hdrlen = rx_hdrlen; + + memmove(skb_pull(skb, rx_hdrlen - hdrlen), + &rxdesc->frame_control, hdrlen); + + hostap_80211_rx(dev, skb, &stats); + return; + + drop: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->rx_list)) != NULL) + hostap_rx_skb(local, skb); +} + + +/* Called only from hardware IRQ */ +static void prism2_alloc_ev(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int idx; + u16 fid; + + fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF); + + PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid); + + spin_lock(&local->txfidlock); + idx = local->next_alloc; + + do { + if (local->txfid[idx] == fid) { + PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n", + idx); + +#ifndef final_version + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) + printk("Already released txfid found at idx " + "%d\n", idx); + if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED) + printk("Already reserved txfid found at idx " + "%d\n", idx); +#endif + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + idx++; + local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 : + idx; + + if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) && + netif_queue_stopped(dev)) + netif_wake_queue(dev); + + spin_unlock(&local->txfidlock); + return; + } + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_alloc); + + printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new " + "read 0x%04x) for alloc event\n", dev->name, fid, + HFA384X_INW(HFA384X_ALLOCFID_OFF)); + printk(KERN_DEBUG "TXFIDs:"); + for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++) + printk(" %04x[%04x]", local->txfid[idx], + local->intransmitfid[idx]); + printk("\n"); + spin_unlock(&local->txfidlock); + + /* FIX: should probably schedule reset; reference to one txfid was lost + * completely.. Bad things will happen if we run out of txfids + * Actually, this will cause netdev watchdog to notice TX timeout and + * then card reset after all txfids have been leaked. */ +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_tx_callback(local_info_t *local, + struct hfa384x_tx_frame *txdesc, int ok, + char *payload) +{ + u16 sw_support, hdrlen, len; + struct sk_buff *skb; + struct hostap_tx_callback_info *cb; + + /* Make sure that frame was from us. */ + if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) { + printk(KERN_DEBUG "%s: TX callback - foreign frame\n", + local->dev->name); + return; + } + + sw_support = le16_to_cpu(txdesc->sw_support); + + spin_lock(&local->lock); + cb = local->tx_callback; + while (cb != NULL && cb->idx != sw_support) + cb = cb->next; + spin_unlock(&local->lock); + + if (cb == NULL) { + printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n", + local->dev->name, sw_support); + return; + } + + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(txdesc->frame_control)); + len = le16_to_cpu(txdesc->data_len); + skb = dev_alloc_skb(hdrlen + len); + if (skb == NULL) { + printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate " + "skb\n", local->dev->name); + return; + } + + memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen); + if (payload) + memcpy(skb_put(skb, len), payload, len); + + skb->dev = local->dev; + skb->mac.raw = skb->data; + + cb->func(skb, ok, cb->data); +} + + +/* Called only as a tasklet (software IRQ) */ +static int hostap_tx_compl_read(local_info_t *local, int error, + struct hfa384x_tx_frame *txdesc, + char **payload) +{ + u16 fid, len; + int res, ret = 0; + struct net_device *dev = local->dev; + + fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF); + + PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc)); + if (res) { + PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not " + "read txdesc\n", dev->name, error, fid); + if (res == -ETIMEDOUT) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + ret = -1; + goto fail; + } + if (txdesc->sw_support) { + len = le16_to_cpu(txdesc->data_len); + if (len < PRISM2_DATA_MAXLEN) { + *payload = (char *) kmalloc(len, GFP_ATOMIC); + if (*payload == NULL || + hfa384x_from_bap(dev, BAP0, *payload, len)) { + PDEBUG(DEBUG_EXTRA, "%s: could not read TX " + "frame payload\n", dev->name); + kfree(*payload); + *payload = NULL; + ret = -1; + goto fail; + } + } + } + + fail: + spin_unlock(&local->baplock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_tx_ev(local_info_t *local) +{ + struct net_device *dev = local->dev; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + if (hostap_tx_compl_read(local, 0, &txdesc, &payload)) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) { + PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x " + "retry_count=%d tx_rate=%d seq_ctrl=%d " + "duration_id=%d\n", + dev->name, le16_to_cpu(txdesc.status), + txdesc.retry_count, txdesc.tx_rate, + le16_to_cpu(txdesc.seq_ctrl), + le16_to_cpu(txdesc.duration_id)); + } + + if (txdesc.sw_support) + hostap_tx_callback(local, &txdesc, 1, payload); + kfree(payload); + + fail: + HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_sta_tx_exc_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) { + struct hfa384x_tx_frame *txdesc = + (struct hfa384x_tx_frame *) skb->data; + + if (skb->len >= sizeof(*txdesc)) { + /* Convert Prism2 RX structure into IEEE 802.11 header + */ + u16 fc = le16_to_cpu(txdesc->frame_control); + int hdrlen = hostap_80211_get_hdrlen(fc); + memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen), + &txdesc->frame_control, hdrlen); + + hostap_handle_sta_tx_exc(local, skb); + } + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_txexc(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 status, fc; + int show_dump, res; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR; + local->stats.tx_errors++; + + res = hostap_tx_compl_read(local, 1, &txdesc, &payload); + HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF); + if (res) + return; + + status = le16_to_cpu(txdesc.status); + + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR)) + { + union iwreq_data wrqu; + + /* Copy 802.11 dest address. */ + memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } else + show_dump = 1; + + if (local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->wds_type & HOSTAP_WDS_AP_CLIENT) { + struct sk_buff *skb; + skb = dev_alloc_skb(sizeof(txdesc)); + if (skb) { + memcpy(skb_put(skb, sizeof(txdesc)), &txdesc, + sizeof(txdesc)); + skb_queue_tail(&local->sta_tx_exc_list, skb); + tasklet_schedule(&local->sta_tx_exc_tasklet); + } + } + + if (txdesc.sw_support) + hostap_tx_callback(local, &txdesc, 0, payload); + kfree(payload); + + if (!show_dump) + return; + + PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)" + " tx_control=%04x\n", + dev->name, status, + status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "", + status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "", + status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "", + status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "", + le16_to_cpu(txdesc.tx_control)); + + fc = le16_to_cpu(txdesc.frame_control); + PDEBUG(DEBUG_EXTRA, " retry_count=%d tx_rate=%d fc=0x%04x " + "(%s%s%s::%d%s%s)\n", + txdesc.retry_count, txdesc.tx_rate, fc, + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT ? "Mgmt" : "", + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_CTRL ? "Ctrl" : "", + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ? "Data" : "", + WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " ToDS" : "", + fc & WLAN_FC_FROMDS ? " FromDS" : ""); + PDEBUG(DEBUG_EXTRA, " A1=" MACSTR " A2=" MACSTR " A3=" + MACSTR " A4=" MACSTR "\n", + MAC2STR(txdesc.addr1), MAC2STR(txdesc.addr2), + MAC2STR(txdesc.addr3), MAC2STR(txdesc.addr4)); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_info_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->info_list)) != NULL) { + hostap_info_process(local, skb); + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 fid; + int res, left; + struct hfa384x_info_frame info; + struct sk_buff *skb; + + fid = HFA384X_INW(HFA384X_INFOFID_OFF); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info)); + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n", + fid); + if (res == -ETIMEDOUT) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + goto out; + } + + le16_to_cpus(&info.len); + le16_to_cpus(&info.type); + left = (info.len - 1) * 2; + + if (info.len & 0x8000 || info.len == 0 || left > 2060) { + /* data register seems to give 0x8000 in some error cases even + * though busy bit is not set in offset register; + * in addition, length must be at least 1 due to type field */ + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received info frame with invalid " + "length 0x%04x (type 0x%04x)\n", dev->name, info.len, + info.type); + goto out; + } + + skb = dev_alloc_skb(sizeof(info) + left); + if (skb == NULL) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Could not allocate skb for info " + "frame\n", dev->name); + goto out; + } + + memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info)); + if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left)) + { + spin_unlock(&local->baplock); + printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, " + "len=0x%04x, type=0x%04x\n", + dev->name, fid, info.len, info.type); + dev_kfree_skb(skb); + goto out; + } + spin_unlock(&local->baplock); + + skb_queue_tail(&local->info_list, skb); + tasklet_schedule(&local->info_tasklet); + + out: + HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_bap_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 ev; + int frames = 30; + + if (local->func->card_present && !local->func->card_present(local)) + return; + + set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Process all pending BAP events without generating new interrupts + * for them */ + while (frames-- > 0) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS)) + break; + if (ev & HFA384X_EV_RX) + prism2_rx(local); + if (ev & HFA384X_EV_INFO) + prism2_info(local); + if (ev & HFA384X_EV_TX) + prism2_tx_ev(local); + if (ev & HFA384X_EV_TXEXC) + prism2_txexc(local); + } + + set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); + clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Enable interrupts for new BAP events */ + hfa384x_events_all(dev); + clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); +} + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +/* Called only from hardware IRQ */ +static void prism2_bus_master_ev(struct net_device *dev, int bap) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + if (bap == BAP1) { + /* FIX: frame payload was DMA'd to skb->data; might need to + * invalidate data cache for that memory area */ + skb_queue_tail(&local->rx_list, local->rx_skb); + tasklet_schedule(&local->rx_tasklet); + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } else { + if (prism2_transmit(dev, local->bus_m0_tx_idx)) { + printk(KERN_DEBUG "%s: prism2_transmit() failed " + "when called from bus master event\n", + dev->name); + local->intransmitfid[local->bus_m0_tx_idx] = + PRISM2_TXFID_EMPTY; + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + } +} +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + +/* Called only from hardware IRQ */ +static void prism2_infdrop(struct net_device *dev) +{ + static unsigned long last_inquire = 0; + + PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name); + + /* some firmware versions seem to get stuck with + * full CommTallies in high traffic load cases; every + * packet will then cause INFDROP event and CommTallies + * info frame will not be sent automatically. Try to + * get out of this state by inquiring CommTallies. */ + if (!last_inquire || time_after(jiffies, last_inquire + HZ)) { + hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, NULL); + last_inquire = jiffies; + } +} + + +/* Called only from hardware IRQ */ +static void prism2_ev_tick(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 evstat, inten; + static int prev_stuck = 0; + + if (time_after(jiffies, local->last_tick_timer + 5 * HZ) && + local->last_tick_timer) { + evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + inten = HFA384X_INW(HFA384X_INTEN_OFF); + if (!prev_stuck) { + printk(KERN_INFO "%s: SW TICK stuck? " + "bits=0x%lx EvStat=%04x IntEn=%04x\n", + dev->name, local->bits, evstat, inten); + } + local->sw_tick_stuck++; + if ((evstat & HFA384X_BAP0_EVENTS) && + (inten & HFA384X_BAP0_EVENTS)) { + printk(KERN_INFO "%s: trying to recover from IRQ " + "hang\n", dev->name); + hfa384x_events_no_bap0(dev); + } + prev_stuck = 1; + } else + prev_stuck = 0; +} + + +/* Called only from hardware IRQ */ +static inline void prism2_check_magic(local_info_t *local) +{ + /* at least PCI Prism2.5 with bus mastering seems to sometimes + * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the + * register once or twice seems to get the correct value.. PCI cards + * cannot anyway be removed during normal operation, so there is not + * really any need for this verification with them. */ + +#ifndef PRISM2_PCI +#ifndef final_version + static unsigned long last_magic_err = 0; + struct net_device *dev = local->dev; + + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + if (!local->hw_ready) + return; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + if (time_after(jiffies, last_magic_err + 10 * HZ)) { + printk("%s: Interrupt, but SWSUPPORT0 does not match: " + "%04X != %04X - card removed?\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + last_magic_err = jiffies; + } else if (net_ratelimit()) { + printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x " + "MAGIC=%04x\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + } + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff) + PRISM2_SCHEDULE_TASK(&local->reset_queue); + return; + } +#endif /* final_version */ +#endif /* !PRISM2_PCI */ +} + + +/* Called only from hardware IRQ */ +static irqreturn_t prism2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int events = 0; + u16 ev; + + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + + if (local->func->card_present && !local->func->card_present(local)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n", + dev->name); + } + return IRQ_HANDLED; + } + + prism2_check_magic(local); + + for (;;) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff) { + if (local->shutdown) + return IRQ_HANDLED; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n", + dev->name); + return IRQ_HANDLED; + } + + ev &= HFA384X_INW(HFA384X_INTEN_OFF); + if (ev == 0) + break; + + if (ev & HFA384X_EV_CMD) { + prism2_cmd_ev(dev); + } + + /* Above events are needed even before hw is ready, but other + * events should be skipped during initialization. This may + * change for AllocEv if allocate_fid is implemented without + * busy waiting. */ + if (!local->hw_ready || local->hw_resetting || + !local->dev_enabled) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev & HFA384X_EV_CMD) + goto next_event; + if ((ev & HFA384X_EVENT_MASK) == 0) + return IRQ_HANDLED; + if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) && + net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_interrupt: hw " + "not ready; skipping events 0x%04x " + "(IntEn=0x%04x)%s%s%s\n", + dev->name, ev, + HFA384X_INW(HFA384X_INTEN_OFF), + !local->hw_ready ? " (!hw_ready)" : "", + local->hw_resetting ? + " (hw_resetting)" : "", + !local->dev_enabled ? + " (!dev_enabled)" : ""); + } + HFA384X_OUTW(ev, HFA384X_EVACK_OFF); + return IRQ_HANDLED; + } + + if (ev & HFA384X_EV_TICK) { + prism2_ev_tick(dev); + HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF); + } + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (ev & HFA384X_EV_PCI_M0) { + prism2_bus_master_ev(dev, BAP0); + HFA384X_OUTW(HFA384X_EV_PCI_M0, HFA384X_EVACK_OFF); + } + + if (ev & HFA384X_EV_PCI_M1) { + /* previous RX has been copied can be ACKed now */ + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + + prism2_bus_master_ev(dev, BAP1); + HFA384X_OUTW(HFA384X_EV_PCI_M1, HFA384X_EVACK_OFF); + } +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + if (ev & HFA384X_EV_ALLOC) { + prism2_alloc_ev(dev); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + } + + /* Reading data from the card is quite time consuming, so do it + * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed + * and unmasked after needed data has been read completely. */ + if (ev & HFA384X_BAP0_EVENTS) { + hfa384x_events_no_bap0(dev); + tasklet_schedule(&local->bap_tasklet); + } + +#ifndef final_version + if (ev & HFA384X_EV_WTERR) { + PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name); + HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF); + } +#endif /* final_version */ + + if (ev & HFA384X_EV_INFDROP) { + prism2_infdrop(dev); + HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF); + } + + next_event: + events++; + if (events >= PRISM2_MAX_INTERRUPT_EVENTS) { + PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events " + "(EvStat=0x%04x)\n", + PRISM2_MAX_INTERRUPT_EVENTS, + HFA384X_INW(HFA384X_EVSTAT_OFF)); + break; + } + } + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1); + return IRQ_RETVAL(events); +} + + +static void prism2_check_sta_fw_version(local_info_t *local) +{ + struct hfa384x_comp_ident comp; + int id, variant, major, minor; + + if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID, + &comp, sizeof(comp), 1) < 0) + return; + + local->fw_ap = 0; + id = le16_to_cpu(comp.id); + if (id != HFA384X_COMP_ID_STA) { + if (id == HFA384X_COMP_ID_FW_AP) + local->fw_ap = 1; + return; + } + + major = __le16_to_cpu(comp.major); + minor = __le16_to_cpu(comp.minor); + variant = __le16_to_cpu(comp.variant); + local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant); + + /* Station firmware versions before 1.4.x seem to have a bug in + * firmware-based WEP encryption when using Host AP mode, so use + * host_encrypt as a default for them. Firmware version 1.4.9 is the + * first one that has been seen to produce correct encryption, but the + * bug might be fixed before that (although, at least 1.4.2 is broken). + */ + local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9); + + if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + local->dev->name); + local->host_encrypt = 1; + } + + /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken + * in station firmware versions before 1.5.x. With these versions, the + * driver uses a workaround with bogus frame format (4th address after + * the payload). This is not compatible with other AP devices. Since + * the firmware bug is fixed in the latest station firmware versions, + * automatically enable standard compliant mode for cards using station + * firmware version 1.5.0 or newer. */ + if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0)) + local->wds_type |= HOSTAP_WDS_STANDARD_FRAME; + else { + printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a " + "workaround for firmware bug in Host AP mode WDS\n", + local->dev->name); + } + + hostap_check_sta_fw_version(local->ap, local->sta_fw_ver); +} + + +static void prism2_crypt_deinit_entries(local_info_t *local, int force) +{ + struct list_head *ptr, *n; + struct prism2_crypt_data *entry; + + for (ptr = local->crypt_deinit_list.next, n = ptr->next; + ptr != &local->crypt_deinit_list; ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct prism2_crypt_data, list); + + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + + list_del(ptr); + + if (entry->ops) + entry->ops->deinit(entry->priv); + kfree(entry); + } +} + + +static void prism2_crypt_deinit_handler(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_crypt_deinit_entries(local, 0); + if (!list_empty(&local->crypt_deinit_list)) { + printk(KERN_DEBUG "%s: entries remaining in delayed crypt " + "deletion list\n", local->dev->name); + local->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&local->crypt_deinit_timer); + } + spin_unlock_irqrestore(&local->lock, flags); + +} + + +static void hostap_passive_scan(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 channel; + + if (local->passive_scan_interval <= 0) + return; + + if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) { + int max_tries = 16; + + /* Even though host system does not really know when the WLAN + * MAC is sending frames, try to avoid changing channels for + * passive scanning when a host-generated frame is being + * transmitted */ + if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: passive scan detected pending " + "TX - delaying\n", dev->name); + local->passive_scan_timer.expires = jiffies + HZ / 10; + add_timer(&local->passive_scan_timer); + return; + } + + do { + local->passive_scan_channel++; + if (local->passive_scan_channel > 14) + local->passive_scan_channel = 1; + max_tries--; + } while (!(local->channel_mask & + (1 << (local->passive_scan_channel - 1))) && + max_tries > 0); + + if (max_tries == 0) { + printk(KERN_INFO "%s: no allowed passive scan channels" + " found\n", dev->name); + return; + } + + printk(KERN_DEBUG "%s: passive scan channel %d\n", + dev->name, local->passive_scan_channel); + channel = local->passive_scan_channel; + local->passive_scan_state = PASSIVE_SCAN_WAIT; + local->passive_scan_timer.expires = jiffies + HZ / 10; + } else { + channel = local->channel; + local->passive_scan_state = PASSIVE_SCAN_LISTEN; + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + } + + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CHANGE_CHANNEL << 8), + channel, NULL, NULL)) + printk(KERN_ERR "%s: passive scan channel set %d " + "failed\n", dev->name, channel); + + add_timer(&local->passive_scan_timer); +} + + +/* Called only as a scheduled task when communications quality values should + * be updated. */ +static void handle_comms_qual_update(void *data) +{ + local_info_t *local = data; + prism2_update_comms_qual(local->dev); + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is + * used to monitor that local->last_tick_timer is being updated. If not, + * interrupt busy-loop is assumed and driver tries to recover by masking out + * some events. */ +static void hostap_tick_timer(unsigned long data) +{ + static unsigned long last_inquire = 0; + local_info_t *local = (local_info_t *) data; + local->last_tick_timer = jiffies; + + /* Inquire CommTallies every 10 seconds to keep the statistics updated + * more often during low load and when using 32-bit tallies. */ + if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) && + !local->hw_downloading && local->hw_ready && + !local->hw_resetting && local->dev_enabled) { + hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, NULL); + last_inquire = jiffies; + } + + if ((local->last_comms_qual_update == 0 || + time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) && + (local->iw_mode == IW_MODE_INFRA || + local->iw_mode == IW_MODE_ADHOC)) { + PRISM2_SCHEDULE_TASK(&local->comms_qual_update); + } + + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); +} + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_registers_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + + if (off != 0) { + *eof = 1; + return 0; + } + +#define SHOW_REG(n) \ +p += sprintf(p, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF)) + + SHOW_REG(CMD); + SHOW_REG(PARAM0); + SHOW_REG(PARAM1); + SHOW_REG(PARAM2); + SHOW_REG(STATUS); + SHOW_REG(RESP0); + SHOW_REG(RESP1); + SHOW_REG(RESP2); + SHOW_REG(INFOFID); + SHOW_REG(CONTROL); + SHOW_REG(SELECT0); + SHOW_REG(SELECT1); + SHOW_REG(OFFSET0); + SHOW_REG(OFFSET1); + SHOW_REG(RXFID); + SHOW_REG(ALLOCFID); + SHOW_REG(TXCOMPLFID); + SHOW_REG(SWSUPPORT0); + SHOW_REG(SWSUPPORT1); + SHOW_REG(SWSUPPORT2); + SHOW_REG(EVSTAT); + SHOW_REG(INTEN); + SHOW_REG(EVACK); + /* Do not read data registers, because they change the state of the + * MAC (offset += 2) */ + /* SHOW_REG(DATA0); */ + /* SHOW_REG(DATA1); */ + SHOW_REG(AUXPAGE); + SHOW_REG(AUXOFFSET); + /* SHOW_REG(AUXDATA); */ +#ifdef PRISM2_PCI + SHOW_REG(PCICOR); + SHOW_REG(PCIHCR); + SHOW_REG(PCI_M0_ADDRH); + SHOW_REG(PCI_M0_ADDRL); + SHOW_REG(PCI_M0_LEN); + SHOW_REG(PCI_M0_CTL); + SHOW_REG(PCI_STATUS); + SHOW_REG(PCI_M1_ADDRH); + SHOW_REG(PCI_M1_ADDRL); + SHOW_REG(PCI_M1_LEN); + SHOW_REG(PCI_M1_CTL); +#endif /* PRISM2_PCI */ + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +struct set_tim_data { + struct list_head list; + int aid; + int set; +}; + +static int prism2_set_tim(struct net_device *dev, int aid, int set) +{ + struct list_head *ptr; + struct set_tim_data *new_entry; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + new_entry = (struct set_tim_data *) + kmalloc(sizeof(*new_entry), GFP_ATOMIC); + if (new_entry == NULL) { + printk(KERN_DEBUG "%s: prism2_set_tim: kmalloc failed\n", + local->dev->name); + return -ENOMEM; + } + memset(new_entry, 0, sizeof(*new_entry)); + new_entry->aid = aid; + new_entry->set = set; + + spin_lock_bh(&local->set_tim_lock); + list_for_each(ptr, &local->set_tim_list) { + struct set_tim_data *entry = + list_entry(ptr, struct set_tim_data, list); + if (entry->aid == aid) { + PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d " + "set=%d ==> %d\n", + local->dev->name, aid, entry->set, set); + entry->set = set; + kfree(new_entry); + new_entry = NULL; + break; + } + } + if (new_entry) + list_add_tail(&new_entry->list, &local->set_tim_list); + spin_unlock_bh(&local->set_tim_lock); + + PRISM2_SCHEDULE_TASK(&local->set_tim_queue); + + return 0; +} + + +static void handle_set_tim_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + struct set_tim_data *entry; + u16 val; + + for (;;) { + entry = NULL; + spin_lock_bh(&local->set_tim_lock); + if (!list_empty(&local->set_tim_list)) { + entry = list_entry(local->set_tim_list.next, + struct set_tim_data, list); + list_del(&entry->list); + } + spin_unlock_bh(&local->set_tim_lock); + if (!entry) + break; + + PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n", + local->dev->name, entry->aid, entry->set); + + val = entry->aid; + if (entry->set) + val |= 0x8000; + if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) { + printk(KERN_DEBUG "%s: set_tim failed (aid=%d " + "set=%d)\n", + local->dev->name, entry->aid, entry->set); + } + + kfree(entry); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +static void prism2_clear_set_tim_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + + list_for_each_safe(ptr, n, &local->set_tim_list) { + struct set_tim_data *entry; + entry = list_entry(ptr, struct set_tim_data, list); + list_del(&entry->list); + kfree(entry); + } +} + + +static struct net_device * +prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx) +{ + struct net_device *dev; + struct hostap_interface *iface; + struct local_info *local; + int len, i, ret; + + if (funcs == NULL) + return NULL; + + len = strlen(dev_template); + if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) { + printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n", + dev_template); + return NULL; + } + + len = sizeof(struct hostap_interface) + + 3 + sizeof(struct local_info) + + 3 + sizeof(struct ap_data); + + dev = alloc_etherdev(len); + if (dev == NULL) + return NULL; + + iface = dev->priv; + local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3); + local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3); + local->dev = iface->dev = dev; + iface->local = local; + iface->type = HOSTAP_INTERFACE_MASTER; + INIT_LIST_HEAD(&local->hostap_interfaces); + + local->hw_module = THIS_MODULE; + +#ifdef PRISM2_IO_DEBUG + local->io_debug_enabled = 1; +#endif /* PRISM2_IO_DEBUG */ + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + local->bus_m0_buf = (u8 *) kmalloc(sizeof(struct hfa384x_tx_frame) + + PRISM2_DATA_MAXLEN, GFP_DMA); + if (local->bus_m0_buf == NULL) + goto fail; +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + local->func = funcs; + local->func->cmd = hfa384x_cmd; + local->func->read_regs = hfa384x_read_regs; + local->func->get_rid = hfa384x_get_rid; + local->func->set_rid = hfa384x_set_rid; + local->func->hw_enable = prism2_hw_enable; + local->func->hw_config = prism2_hw_config; + local->func->hw_reset = prism2_hw_reset; + local->func->hw_shutdown = prism2_hw_shutdown; + local->func->reset_port = prism2_reset_port; + local->func->schedule_reset = prism2_schedule_reset; +#ifdef PRISM2_DOWNLOAD_SUPPORT + local->func->read_aux = prism2_download_aux_dump; + local->func->download = prism2_download; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + local->func->tx = prism2_tx_80211; + local->func->set_tim = prism2_set_tim; + local->func->need_tx_headroom = 0; /* no need to add txdesc in + * skb->data (FIX: maybe for DMA bus + * mastering? */ + + local->mtu = mtu; + + rwlock_init(&local->iface_lock); + spin_lock_init(&local->txfidlock); + spin_lock_init(&local->cmdlock); + spin_lock_init(&local->baplock); + spin_lock_init(&local->lock); + init_MUTEX(&local->rid_bap_sem); + + if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES) + card_idx = 0; + local->card_idx = card_idx; + + i = essid[card_idx] == NULL ? 0 : card_idx; + len = strlen(essid[i]); + memcpy(local->essid, essid[i], + len > MAX_SSID_LEN ? MAX_SSID_LEN : len); + local->essid[MAX_SSID_LEN] = '\0'; + i = GET_INT_PARM(iw_mode, card_idx); + if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) || + i == IW_MODE_MONITOR) { + local->iw_mode = i; + } else { + printk(KERN_WARNING "prism2: Unknown iw_mode %d; using " + "IW_MODE_MASTER\n", i); + local->iw_mode = IW_MODE_MASTER; + } + local->channel = GET_INT_PARM(channel, card_idx); + local->beacon_int = GET_INT_PARM(beacon_int, card_idx); + local->dtim_period = GET_INT_PARM(dtim_period, card_idx); + local->wds_max_connections = 16; + local->tx_control = HFA384X_TX_CTRL_FLAGS; + local->manual_retry_count = -1; + local->rts_threshold = 2347; + local->fragm_threshold = 2346; + local->rssi_to_dBm = 100; /* default; to be overriden by + * cnfDbmAdjust, if available */ + local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY; + local->sram_type = -1; +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + local->bus_master_threshold_rx = GET_INT_PARM(bus_master_threshold_rx, + card_idx); + local->bus_master_threshold_tx = GET_INT_PARM(bus_master_threshold_tx, + card_idx); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + /* Initialize task queue structures */ + INIT_WORK(&local->reset_queue, handle_reset_queue, local); + INIT_WORK(&local->set_multicast_list_queue, + hostap_set_multicast_list_queue, local->dev); + + INIT_WORK(&local->set_tim_queue, handle_set_tim_queue, local); + INIT_LIST_HEAD(&local->set_tim_list); + spin_lock_init(&local->set_tim_lock); + + INIT_WORK(&local->comms_qual_update, handle_comms_qual_update, local); + + /* Initialize tasklets for handling hardware IRQ related operations + * outside hw IRQ handler */ + HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet, + (unsigned long) local); + + HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet, + (unsigned long) local); + hostap_info_init(local); + + HOSTAP_TASKLET_INIT(&local->rx_tasklet, + hostap_rx_tasklet, (unsigned long) local); + skb_queue_head_init(&local->rx_list); + + HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet, + hostap_sta_tx_exc_tasklet, (unsigned long) local); + skb_queue_head_init(&local->sta_tx_exc_list); + + INIT_LIST_HEAD(&local->cmd_queue); + init_waitqueue_head(&local->hostscan_wq); + INIT_LIST_HEAD(&local->crypt_deinit_list); + init_timer(&local->crypt_deinit_timer); + local->crypt_deinit_timer.data = (unsigned long) local; + local->crypt_deinit_timer.function = prism2_crypt_deinit_handler; + + init_timer(&local->passive_scan_timer); + local->passive_scan_timer.data = (unsigned long) local; + local->passive_scan_timer.function = hostap_passive_scan; + + init_timer(&local->tick_timer); + local->tick_timer.data = (unsigned long) local; + local->tick_timer.function = hostap_tick_timer; + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); + + INIT_LIST_HEAD(&local->bss_list); + + hostap_setup_dev(dev, local, 1); + local->saved_eth_header_parse = dev->hard_header_parse; + + dev->hard_start_xmit = hostap_master_start_xmit; + dev->type = ARPHRD_IEEE80211; + dev->hard_header_parse = hostap_80211_header_parse; + + rtnl_lock(); + ret = dev_alloc_name(dev, "wifi%d"); + if (ret >= 0) + ret = register_netdevice(dev); + rtnl_unlock(); + if (ret < 0) { + printk(KERN_WARNING "%s: register netdevice failed!\n", + dev_info); + goto fail; + } + printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name); + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("registers", 0, local->proc, + prism2_registers_proc_read, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + hostap_init_data(local); + return dev; + + fail: +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + kfree(local->bus_m0_buf); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + free_netdev(dev); + return NULL; +} + + +static int hostap_hw_ready(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + struct local_info *local = iface->local; + + local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0, + "", dev_template); + + if (local->ddev) { + if (local->iw_mode == IW_MODE_INFRA || + local->iw_mode == IW_MODE_ADHOC) { + netif_carrier_off(local->dev); + netif_carrier_off(local->ddev); + } + hostap_init_proc(local); + hostap_init_ap_proc(local); + return 0; + } + + return -1; +} + + +static void prism2_free_local_data(struct net_device *dev) +{ + struct hostap_tx_callback_info *tx_cb, *tx_cb_prev; + int i; + struct hostap_interface *iface; + struct local_info *local; + struct list_head *ptr, *n; + + if (dev == NULL) + return; + + iface = dev->priv; + local = iface->local; + + flush_scheduled_work(); + + if (timer_pending(&local->crypt_deinit_timer)) + del_timer(&local->crypt_deinit_timer); + prism2_crypt_deinit_entries(local, 1); + + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + + if (timer_pending(&local->tick_timer)) + del_timer(&local->tick_timer); + + prism2_clear_cmd_queue(local); + + skb_queue_purge(&local->info_list); + skb_queue_purge(&local->rx_list); + skb_queue_purge(&local->sta_tx_exc_list); + + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + + for (i = 0; i < WEP_KEYS; i++) { + struct prism2_crypt_data *crypt = local->crypt[i]; + if (crypt) { + if (crypt->ops) + crypt->ops->deinit(crypt->priv); + kfree(crypt); + local->crypt[i] = NULL; + } + } + + if (local->ap != NULL) + hostap_free_data(local->ap); + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (local->proc != NULL) + remove_proc_entry("registers", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + hostap_remove_proc(local); + + tx_cb = local->tx_callback; + while (tx_cb != NULL) { + tx_cb_prev = tx_cb; + tx_cb = tx_cb->next; + kfree(tx_cb_prev); + } + + hostap_set_hostapd(local, 0, 0); + hostap_set_hostapd_sta(local, 0, 0); + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + if (local->frag_cache[i].skb != NULL) + dev_kfree_skb(local->frag_cache[i].skb); + } + +#ifdef PRISM2_DOWNLOAD_SUPPORT + prism2_download_free_data(local->dl_pri); + prism2_download_free_data(local->dl_sec); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + list_for_each_safe(ptr, n, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_MASTER) { + /* special handling for this interface below */ + continue; + } + hostap_remove_interface(iface->dev, 0, 1); + } + + prism2_clear_set_tim_queue(local); + + list_for_each_safe(ptr, n, &local->bss_list) { + struct hostap_bss_info *bss = + list_entry(ptr, struct hostap_bss_info, list); + kfree(bss); + } + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + kfree(local->bus_m0_buf); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + kfree(local->pda); + kfree(local->last_scan_results); + kfree(local->generic_elem); + + unregister_netdev(local->dev); + free_netdev(local->dev); +} + + +void prism2_suspend(struct net_device *dev) +{ + struct hostap_interface *iface; + struct local_info *local; + union iwreq_data wrqu; + + iface = dev->priv; + local = iface->local; + + /* Send disconnect event, e.g., to trigger reassociation after resume + * if wpa_supplicant is used. */ + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + + /* Disable hardware and firmware */ + prism2_hw_shutdown(dev, 0); +} + + +/* These might at some point be compiled separately and used as separate + * kernel modules or linked into one */ +#ifdef PRISM2_DOWNLOAD_SUPPORT +#include "hostap_download.c" +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_CALLBACK +/* External hostap_callback.c file can be used to, e.g., blink activity led. + * This can use platform specific code and must define prism2_callback() + * function (if PRISM2_CALLBACK is not defined, these function calls are not + * used. */ +#include "hostap_callback.c" +#endif /* PRISM2_CALLBACK */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_info.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_info.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_info.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_info.c 2004-02-29 13:05:44.000000000 -0500 @@ -0,0 +1,473 @@ +/* Host AP driver Info Frame processing (part of hostap.o module) */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies *) buf; +#define ADD_COMM_TALLIES(name) \ +local->comm_tallies.name += le16_to_cpu(tallies->name) + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies32 *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies32)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies32 *) buf; +#define ADD_COMM_TALLIES(name) \ +local->comm_tallies.name += le32_to_cpu(tallies->name) + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies(local_info_t *local, unsigned char *buf, + int left) +{ + if (local->tallies32) + prism2_info_commtallies32(local, buf, left); + else + prism2_info_commtallies16(local, buf, left); +} + + +#ifndef PRISM2_NO_STATION_MODES +#ifndef PRISM2_NO_DEBUG +static const char* hfa384x_linkstatus_str(u16 linkstatus) +{ + switch (linkstatus) { + case HFA384X_LINKSTATUS_CONNECTED: + return "Connected"; + case HFA384X_LINKSTATUS_DISCONNECTED: + return "Disconnected"; + case HFA384X_LINKSTATUS_AP_CHANGE: + return "Access point change"; + case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE: + return "Access point out of range"; + case HFA384X_LINKSTATUS_AP_IN_RANGE: + return "Access point in range"; + case HFA384X_LINKSTATUS_ASSOC_FAILED: + return "Association failed"; + default: + return "Unknown"; + } +} +#endif /* PRISM2_NO_DEBUG */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf, + int left) +{ + u16 val; + int non_sta_mode; + + /* Alloc new JoinRequests to occur since LinkStatus for the previous + * has been received */ + local->last_join_time = 0; + + if (left != 2) { + printk(KERN_DEBUG "%s: invalid linkstatus info frame " + "length %d\n", local->dev->name, left); + return; + } + + non_sta_mode = local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->iw_mode == IW_MODE_MONITOR; + + val = buf[0] | (buf[1] << 8); + if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n", + local->dev->name, val, hfa384x_linkstatus_str(val)); + } + + if (non_sta_mode) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + return; + } + + /* Get current BSSID later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info); + local->prev_link_status = val; + PRISM2_SCHEDULE_TASK(&local->info_queue); +} + + +static void prism2_host_roaming(local_info_t *local) +{ + struct hfa384x_join_request req; + struct net_device *dev = local->dev; + struct hfa384x_scan_result *selected, *entry; + int i; + unsigned long flags; + + if (local->last_join_time && + time_before(jiffies, local->last_join_time + 10 * HZ)) { + PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been " + "completed - waiting for it before issuing new one\n", + dev->name); + return; + } + + /* ScanResults are sorted: first ESS results in decreasing signal + * quality then IBSS results in similar order. + * Trivial roaming policy: just select the first entry. + * This could probably be improved by adding hysteresis to limit + * number of handoffs, etc. + * + * Could do periodic RID_SCANREQUEST or Inquire F101 to get new + * ScanResults */ + spin_lock_irqsave(&local->lock, flags); + if (local->last_scan_results == NULL || + local->last_scan_results_count == 0) { + spin_unlock_irqrestore(&local->lock, flags); + PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n", + dev->name); + return; + } + + selected = &local->last_scan_results[0]; + + if (local->preferred_ap[0] || local->preferred_ap[1] || + local->preferred_ap[2] || local->preferred_ap[3] || + local->preferred_ap[4] || local->preferred_ap[5]) { + /* Try to find preferred AP */ + PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n", + dev->name, MAC2STR(local->preferred_ap)); + for (i = 0; i < local->last_scan_results_count; i++) { + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, 6) == 0) + { + PDEBUG(DEBUG_EXTRA, "%s: using preferred AP " + "selection\n", dev->name); + selected = entry; + break; + } + } + } + + memcpy(req.bssid, selected->bssid, 6); + req.channel = selected->chid; + spin_unlock_irqrestore(&local->lock, flags); + + PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n", + dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel)); + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name); + } + local->last_join_time = jiffies; +} + + +static void hostap_report_scan_complete(local_info_t *local) +{ + union iwreq_data wrqu; + + /* Inform user space about new scan results (just empty event, + * SIOCGIWSCAN can be used to fetch data */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL); + + /* Allow SIOCGIWSCAN handling to occur since we have received + * scanning result */ + local->scan_timestamp = 0; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_scanresults(local_info_t *local, unsigned char *buf, + int left) +{ + u16 *pos; + int new_count; + unsigned long flags; + struct hfa384x_scan_result *results, *prev; + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid scanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (u16 *) buf; + pos++; + pos++; + left -= 4; + + new_count = left / sizeof(struct hfa384x_scan_result); + results = kmalloc(new_count * sizeof(struct hfa384x_scan_result), + GFP_ATOMIC); + if (results == NULL) + return; + memcpy(results, pos, new_count * sizeof(struct hfa384x_scan_result)); + + spin_lock_irqsave(&local->lock, flags); + local->last_scan_type = PRISM2_SCAN; + prev = local->last_scan_results; + local->last_scan_results = results; + local->last_scan_results_count = new_count; + spin_unlock_irqrestore(&local->lock, flags); + kfree(prev); + + hostap_report_scan_complete(local); + + /* Perform rest of ScanResults handling later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info); + PRISM2_SCHEDULE_TASK(&local->info_queue); +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_hostscanresults(local_info_t *local, + unsigned char *buf, int left) +{ + int i, result_size, copy_len, new_count; + struct hfa384x_hostscan_result *results, *prev; + unsigned long flags; + u16 *pos; + u8 *ptr; + + wake_up_interruptible(&local->hostscan_wq); + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid hostscanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (u16 *) buf; + copy_len = result_size = le16_to_cpu(*pos); + if (result_size == 0) { + printk(KERN_DEBUG "%s: invalid result_size (0) in " + "hostscanresults\n", local->dev->name); + return; + } + if (copy_len > sizeof(struct hfa384x_hostscan_result)) + copy_len = sizeof(struct hfa384x_hostscan_result); + + pos++; + pos++; + left -= 4; + ptr = (u8 *) pos; + + new_count = left / result_size; + results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result), + GFP_ATOMIC); + if (results == NULL) + return; + memset(results, 0, new_count * sizeof(struct hfa384x_hostscan_result)); + + for (i = 0; i < new_count; i++) { + memcpy(&results[i], ptr, copy_len); + ptr += result_size; + left -= result_size; + } + + if (left) { + printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n", + local->dev->name, left, result_size); + } + + spin_lock_irqsave(&local->lock, flags); + local->last_scan_type = PRISM2_HOSTSCAN; + prev = local->last_hostscan_results; + local->last_hostscan_results = results; + local->last_hostscan_results_count = new_count; + spin_unlock_irqrestore(&local->lock, flags); + kfree(prev); + + hostap_report_scan_complete(local); +} +#endif /* PRISM2_NO_STATION_MODES */ + + +/* Called only as a tasklet (software IRQ) */ +void hostap_info_process(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_info_frame *info; + unsigned char *buf; + int left; +#ifndef PRISM2_NO_DEBUG + int i; +#endif /* PRISM2_NO_DEBUG */ + + info = (struct hfa384x_info_frame *) skb->data; + buf = skb->data + sizeof(*info); + left = skb->len - sizeof(*info); + + switch (info->type) { + case HFA384X_INFO_COMMTALLIES: + prism2_info_commtallies(local, buf, left); + break; + +#ifndef PRISM2_NO_STATION_MODES + case HFA384X_INFO_LINKSTATUS: + prism2_info_linkstatus(local, buf, left); + break; + + case HFA384X_INFO_SCANRESULTS: + prism2_info_scanresults(local, buf, left); + break; + + case HFA384X_INFO_HOSTSCANRESULTS: + prism2_info_hostscanresults(local, buf, left); + break; +#endif /* PRISM2_NO_STATION_MODES */ + +#ifndef PRISM2_NO_DEBUG + default: + PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n", + local->dev->name, info->len, info->type); + PDEBUG(DEBUG_EXTRA, "Unknown info frame:"); + for (i = 0; i < (left < 100 ? left : 100); i++) + PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]); + PDEBUG2(DEBUG_EXTRA, "\n"); + break; +#endif /* PRISM2_NO_DEBUG */ + } +} + + +#ifndef PRISM2_NO_STATION_MODES +static void handle_info_queue_linkstatus(local_info_t *local) +{ + int val = local->prev_link_status; + int connected; + union iwreq_data wrqu; + + connected = + val == HFA384X_LINKSTATUS_CONNECTED || + val == HFA384X_LINKSTATUS_AP_CHANGE || + val == HFA384X_LINKSTATUS_AP_IN_RANGE; + + if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID, + local->bssid, ETH_ALEN, 1) < 0) { + printk(KERN_DEBUG "%s: could not read CURRENTBSSID after " + "LinkStatus event\n", local->dev->name); + } else { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n", + local->dev->name, + MAC2STR((unsigned char *) local->bssid)); + if (local->wds_type & HOSTAP_WDS_AP_CLIENT) + hostap_add_sta(local->ap, local->bssid); + } + + /* Get BSSID if we have a valid AP address */ + if (connected) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN); + } else { + netif_carrier_off(local->dev); + netif_carrier_off(local->ddev); + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + } + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); +} + + +static void handle_info_queue_scanresults(local_info_t *local) +{ + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) + prism2_host_roaming(local); +} + + +/* Called only as scheduled task after receiving info frames (used to avoid + * pending too much time in HW IRQ handler). */ +static void handle_info_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS, + &local->pending_info)) + handle_info_queue_linkstatus(local); + + if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS, + &local->pending_info)) + handle_info_queue_scanresults(local); + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_info_init(local_info_t *local) +{ + skb_queue_head_init(&local->info_list); +#ifndef PRISM2_NO_STATION_MODES + INIT_WORK(&local->info_queue, handle_info_queue, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +EXPORT_SYMBOL(hostap_info_init); +EXPORT_SYMBOL(hostap_info_process); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_ioctl.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_ioctl.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_ioctl.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_ioctl.c 2004-11-22 01:03:05.000000000 -0500 @@ -0,0 +1,4273 @@ +/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ + +#ifdef in_atomic +/* Get kernel_locked() for in_atomic() */ +#include +#endif +#include + + +static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct iw_statistics *wstats; + +#if WIRELESS_EXT > 16 + /* Why are we doing that ? Jean II */ + if (iface->type != HOSTAP_INTERFACE_MAIN) + return NULL; +#endif /* WIRELESS_EXT > 16 */ + + wstats = &local->wstats; + + wstats->status = 0; + wstats->discard.code = + local->comm_tallies.rx_discards_wep_undecryptable; + wstats->discard.misc = + local->comm_tallies.rx_fcs_errors + + local->comm_tallies.rx_discards_no_buffer + + local->comm_tallies.tx_discards_wrong_sa; + + wstats->discard.retries = + local->comm_tallies.tx_retry_limit_exceeded; + wstats->discard.fragment = + local->comm_tallies.rx_message_in_bad_msg_fragments; + + if (local->iw_mode != IW_MODE_MASTER && + local->iw_mode != IW_MODE_REPEAT) { + int update = 1; +#ifdef in_atomic + /* RID reading might sleep and it must not be called in + * interrupt context or while atomic. However, this + * function seems to be called while atomic (at least in Linux + * 2.5.59). Update signal quality values only if in suitable + * context. Otherwise, previous values read from tick timer + * will be used. */ + if (in_atomic()) + update = 0; +#endif /* in_atomic */ + + if (update && prism2_update_comms_qual(dev) == 0) + wstats->qual.updated = 7; + + wstats->qual.qual = local->comms_qual; + wstats->qual.level = local->avg_signal; + wstats->qual.noise = local->avg_noise; + } else { + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = 0; + } + + return wstats; +} + + +static int prism2_get_datarates(struct net_device *dev, u8 *rates) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u8 buf[12]; + int len; + u16 val; + + len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf, + sizeof(buf), 0); + if (len < 2) + return 0; + + val = le16_to_cpu(*(u16 *) buf); /* string length */ + + if (len - 2 < val || val > 10) + return 0; + + memcpy(rates, buf + 2, val); + return val; +} + + +static int prism2_get_name(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra) +{ + u8 rates[10]; + int len, i, over2 = 0; + + len = prism2_get_datarates(dev, rates); + + for (i = 0; i < len; i++) { + if (rates[i] == 0x0b || rates[i] == 0x16) { + over2 = 1; + break; + } + } + + strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); + + return 0; +} + + +static void prism2_crypt_delayed_deinit(local_info_t *local, + struct prism2_crypt_data **crypt) +{ + struct prism2_crypt_data *tmp; + unsigned long flags; + + tmp = *crypt; + *crypt = NULL; + + if (tmp == NULL) + return; + + /* must not run ops->deinit() while there may be pending encrypt or + * decrypt operations. Use a list of delayed deinits to avoid needing + * locking. */ + + spin_lock_irqsave(&local->lock, flags); + list_add(&tmp->list, &local->crypt_deinit_list); + if (!timer_pending(&local->crypt_deinit_timer)) { + local->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&local->crypt_deinit_timer); + } + spin_unlock_irqrestore(&local->lock, flags); +} + + +static int prism2_ioctl_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int i; + struct prism2_crypt_data **crypt; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + crypt = &local->crypt[i]; + + if (erq->flags & IW_ENCODE_DISABLED) { + if (*crypt) + prism2_crypt_delayed_deinit(local, crypt); + goto done; + } + + if (*crypt != NULL && (*crypt)->ops != NULL && + strcmp((*crypt)->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm */ + prism2_crypt_delayed_deinit(local, crypt); + } + + if (*crypt == NULL) { + struct prism2_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = hostap_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("hostap_crypt_wep"); + new_crypt->ops = hostap_get_crypto_ops("WEP"); + } + if (new_crypt->ops) + new_crypt->priv = new_crypt->ops->init(i); + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module hostap_crypt_wep.o\n", + dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + if (erq->length > 0) { + int len = erq->length <= 5 ? 5 : 13; + int first = 1, j; + if (len > erq->length) + memset(keybuf + erq->length, 0, len - erq->length); + (*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv); + for (j = 0; j < WEP_KEYS; j++) { + if (j != i && local->crypt[j]) { + first = 0; + break; + } + } + if (first) + local->tx_keyidx = i; + } else { + /* No key data - just set the default TX key index */ + local->tx_keyidx = i; + } + + done: + local->open_wep = erq->flags & IW_ENCODE_OPEN; + + if (hostap_set_encryption(local)) { + printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name); + return -EINVAL; + } + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + + return 0; +} + + +static int prism2_ioctl_giwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *key) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int i, len; + u16 val; + struct prism2_crypt_data *crypt; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + crypt = local->crypt[i]; + erq->flags = i + 1; + + if (crypt == NULL || crypt->ops == NULL) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + return 0; + } + + /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show + * the keys from driver buffer */ + len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv); + erq->length = (len >= 0 ? len : 0); + + if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0) + { + printk("CNFWEPFLAGS reading failed\n"); + return -EOPNOTSUPP; + } + le16_to_cpus(&val); + if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED) + erq->flags |= IW_ENCODE_ENABLED; + else + erq->flags |= IW_ENCODE_DISABLED; + if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + + return 0; +} + + +#if WIRELESS_EXT <= 15 +static int prism2_ioctl_giwspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct sockaddr addr[IW_MAX_SPY]; + struct iw_quality qual[IW_MAX_SPY]; + + srq->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_SPY, 0); + + memcpy(extra, &addr, sizeof(addr[0]) * srq->length); + memcpy(extra + sizeof(addr[0]) * srq->length, &qual, + sizeof(qual[0]) * srq->length); + + return 0; +} + + +static int prism2_ioctl_siwspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct sockaddr *addr; + int i; + + if (srq->length > IW_MAX_SPY) + return -E2BIG; + + addr = (struct sockaddr *) extra; + for (i = 0; i < srq->length; i++, addr++) { + hostap_add_sta(local->ap, addr->sa_data); + /* TODO: how are these entries timed out? is the default + * timeout suitable? probably not.. */ + } + + return 0; +} +#endif /* WIRELESS_EXT <= 15 */ + + +static int hostap_set_rate(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret, basic_rates; + + basic_rates = local->basic_rates & local->tx_rate_control; + if (!basic_rates || basic_rates != local->basic_rates) { + printk(KERN_INFO "%s: updating basic rate set automatically " + "to match with the new supported rate set\n", + dev->name); + if (!basic_rates) + basic_rates = local->tx_rate_control; + + local->basic_rates = basic_rates; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + basic_rates)) + printk(KERN_WARNING "%s: failed to set " + "cnfBasicRates\n", dev->name); + } + + ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control) || + hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control) || + local->func->reset_port(dev)); + + if (ret) { + printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates " + "setting to 0x%x failed\n", + dev->name, local->tx_rate_control); + } + + /* Update TX rate configuration for all STAs based on new operational + * rate set. */ + hostap_update_rates(local); + + return ret; +} + + +static int prism2_ioctl_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (rrq->fixed) { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } else { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } + + return hostap_set_rate(dev); +} + + +static int prism2_ioctl_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + u16 val; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret = 0; + + if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) < + 0) + return -EINVAL; + + if ((val & 0x1) && (val > 1)) + rrq->fixed = 0; + else + rrq->fixed = 1; + + if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL && + !local->fw_tx_rate_control) { + /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in + * Host AP mode, so use the recorded TX rate of the last sent + * frame */ + rrq->value = local->ap->last_tx_rate > 0 ? + local->ap->last_tx_rate * 100000 : 11000000; + return 0; + } + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) < + 0) + return -EINVAL; + + switch (val) { + case HFA384X_RATES_1MBPS: + rrq->value = 1000000; + break; + case HFA384X_RATES_2MBPS: + rrq->value = 2000000; + break; + case HFA384X_RATES_5MBPS: + rrq->value = 5500000; + break; + case HFA384X_RATES_11MBPS: + rrq->value = 11000000; + break; + default: + /* should not happen */ + rrq->value = 11000000; + ret = -EINVAL; + break; + } + + return ret; +} + + +static int prism2_ioctl_siwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + /* Set the desired AP density */ + if (sens->value < 1 || sens->value > 3) + return -EINVAL; + + if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + /* Get the current AP density */ + if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) < + 0) + return -EINVAL; + + sens->value = __le16_to_cpu(val); + sens->fixed = 1; + + return 0; +} + + +/* Deprecated in new wireless extension API */ +static int prism2_ioctl_giwaplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct sockaddr addr[IW_MAX_AP]; + struct iw_quality qual[IW_MAX_AP]; + + if (local->iw_mode != IW_MODE_MASTER) { + printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported " + "in Host AP mode\n"); + data->length = 0; + return -EOPNOTSUPP; + } + + data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); + + memcpy(extra, &addr, sizeof(addr[0]) * data->length); + data->flags = 1; /* has quality information */ + memcpy(extra + sizeof(addr[0]) * data->length, &qual, + sizeof(qual[0]) * data->length); + + return 0; +} + + +static int prism2_ioctl_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + if (rts->disabled) + val = __constant_cpu_to_le16(2347); + else if (rts->value < 0 || rts->value > 2347) + return -EINVAL; + else + val = __cpu_to_le16(rts->value); + + if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) || + local->func->reset_port(dev)) + return -EINVAL; + + local->rts_threshold = rts->value; + + return 0; +} + +static int prism2_ioctl_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) < + 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2347); + rts->fixed = 1; + + return 0; +} + + +static int prism2_ioctl_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + if (rts->disabled) + val = __constant_cpu_to_le16(2346); + else if (rts->value < 256 || rts->value > 2346) + return -EINVAL; + else + val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */ + + local->fragm_threshold = rts->value & ~0x1; + if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val, + 2) + || local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + &val, 2, 1) < 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2346); + rts->fixed = 1; + + return 0; +} + + +#ifndef PRISM2_NO_STATION_MODES +static int hostap_join_ap(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hfa384x_join_request req; + unsigned long flags; + int i; + struct hfa384x_scan_result *entry; + + memcpy(req.bssid, local->preferred_ap, ETH_ALEN); + req.channel = 0; + + spin_lock_irqsave(&local->lock, flags); + for (i = 0; i < local->last_scan_results_count; i++) { + if (!local->last_scan_results) + break; + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, ETH_ALEN) == 0) { + req.channel = entry->chid; + break; + } + } + spin_unlock_irqrestore(&local->lock, flags); + + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest " MACSTR + " failed\n", + dev->name, MAC2STR(local->preferred_ap)); + return -1; + } + + printk(KERN_DEBUG "%s: Trying to join BSSID " MACSTR "\n", + dev->name, MAC2STR(local->preferred_ap)); + + return 0; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN); + + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) { + struct hfa384x_scan_request scan_req; + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, + &scan_req, sizeof(scan_req))) { + printk(KERN_DEBUG "%s: ScanResults request failed - " + "preferred AP delayed to next unsolicited " + "scan\n", dev->name); + } + } else if (local->host_roaming == 2 && + local->iw_mode == IW_MODE_INFRA) { + if (hostap_join_ap(dev)) + return -EINVAL; + } else { + printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only " + "in Managed mode when host_roaming is enabled\n", + dev->name); + } + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + +static int prism2_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + ap_addr->sa_family = ARPHRD_ETHER; + switch (iface->type) { + case HOSTAP_INTERFACE_AP: + memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_STA: + memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_WDS: + memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN); + break; + default: + if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID, + &ap_addr->sa_data, ETH_ALEN, 1) < 0) + return -EOPNOTSUPP; + + /* local->bssid is also updated in LinkStatus handler when in + * station mode */ + memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN); + break; + } + + return 0; +} + + +static int prism2_ioctl_siwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + memset(local->name, 0, sizeof(local->name)); + memcpy(local->name, nickname, data->length); + local->name_set = 1; + + if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int len; + char name[MAX_NAME_LEN + 3]; + u16 val; + + len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME, + &name, MAX_NAME_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) name); + if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN) + return -EOPNOTSUPP; + + name[val + 2] = '\0'; + data->length = val + 1; + memcpy(nickname, name + 2, val + 1); + + return 0; +} + + +static int prism2_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + /* freq => chan. */ + if (freq->e == 1 && + freq->m / 100000 >= freq_list[0] && + freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) { + int ch; + int fr = freq->m / 100000; + for (ch = 0; ch < FREQ_COUNT; ch++) { + if (fr == freq_list[ch]) { + freq->e = 0; + freq->m = ch + 1; + break; + } + } + } + + if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT || + !(local->channel_mask & (1 << (freq->m - 1)))) + return -EINVAL; + + local->channel = freq->m; /* channel is used in prism2_setup_rids() */ + if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) < + 0) + return -EINVAL; + + le16_to_cpus(&val); + if (val < 1 || val > FREQ_COUNT) + return -EINVAL; + + freq->m = freq_list[val - 1] * 100000; + freq->e = 1; + + return 0; +} + + +static void hostap_monitor_set_type(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return; + + if (local->monitor_type == PRISM2_MONITOR_PRISM || + local->monitor_type == PRISM2_MONITOR_CAPHDR) { + dev->type = ARPHRD_IEEE80211_PRISM; + dev->hard_header_parse = + hostap_80211_prism_header_parse; + } else { + dev->type = ARPHRD_IEEE80211; + dev->hard_header_parse = hostap_80211_header_parse; + } +} + + +static int prism2_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + if (data->flags == 0) + ssid[0] = '\0'; /* ANY */ + + if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') { + /* Setting SSID to empty string seems to kill the card in + * Host AP mode */ + printk(KERN_DEBUG "%s: Host AP mode does not support " + "'Any' essid\n", dev->name); + return -EINVAL; + } + + memcpy(local->essid, ssid, data->length); + local->essid[data->length] = '\0'; + + if ((!local->fw_ap && + hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid)) + || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 val; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + data->flags = 1; /* active */ + if (local->iw_mode == IW_MODE_MASTER) { + data->length = strlen(local->essid); + memcpy(essid, local->essid, IW_ESSID_MAX_SIZE); + } else { + int len; + char ssid[MAX_SSID_LEN + 2]; + memset(ssid, 0, sizeof(ssid)); + len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID, + &ssid, MAX_SSID_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) ssid); + if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) { + return -EOPNOTSUPP; + } + data->length = val; + memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE); + } + + return 0; +} + + +static int prism2_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct iw_range *range = (struct iw_range *) extra; + u8 rates[10]; + u16 val; + int i, len, over2; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: could fill num_txpower and txpower array with + * something; however, there are 128 different values.. */ + + range->txpower_capa = IW_TXPOW_DBM; + + if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC) + { + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | + IW_POWER_UNICAST_R | IW_POWER_ALL_R; + } + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 18; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + if (local->channel_mask & (1 << i)) { + range->freq[val].i = i + 1; + range->freq[val].m = freq_list[i] * 100000; + range->freq[val].e = 1; + val++; + } + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + range->max_qual.qual = 70; /* what is correct max? This was not + * documented exactly. At least + * 69 has been observed. */ + range->max_qual.level = 0; /* dB */ + range->max_qual.noise = 0; /* dB */ + + /* What would be suitable values for "average/typical" qual? */ + range->avg_qual.qual = 20; + range->avg_qual.level = -60; + range->avg_qual.noise = -95; + } else { + range->max_qual.qual = 92; /* 0 .. 92 */ + range->max_qual.level = 154; /* 27 .. 154 */ + range->max_qual.noise = 154; /* 27 .. 154 */ + } + range->sensitivity = 3; + + range->max_encoding_tokens = WEP_KEYS; + range->num_encoding_sizes = 2; + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + + over2 = 0; + len = prism2_get_datarates(dev, rates); + range->num_bitrates = 0; + for (i = 0; i < len; i++) { + if (range->num_bitrates < IW_MAX_BITRATES) { + range->bitrate[range->num_bitrates] = + rates[i] * 500000; + range->num_bitrates++; + } + if (rates[i] == 0x0b || rates[i] == 0x16) + over2 = 1; + } + /* estimated maximum TCP throughput values (bps) */ + range->throughput = over2 ? 5500000 : 1500000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + +#if WIRELESS_EXT > 16 + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | + IW_EVENT_CAPA_MASK(IWEVCUSTOM) | + IW_EVENT_CAPA_MASK(IWEVREGISTERED) | + IW_EVENT_CAPA_MASK(IWEVEXPIRED)); +#endif /* WIRELESS_EXT > 16 */ + +#if WIRELESS_EXT > 17 + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; +#endif /* WIRELESS_EXT > 17 */ + + return 0; +} + + +static int hostap_monitor_mode_enable(local_info_t *local) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "Enabling monitor mode\n"); + hostap_monitor_set_type(local); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_PSEUDO_IBSS)) { + printk(KERN_DEBUG "Port type setting for monitor mode " + "failed\n"); + return -EOPNOTSUPP; + } + + /* Host decrypt is needed to get the IV and ICV fields; + * however, monitor mode seems to remove WEP flag from frame + * control field */ + if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, + HFA384X_WEPFLAGS_HOSTENCRYPT | + HFA384X_WEPFLAGS_HOSTDECRYPT)) { + printk(KERN_DEBUG "WEP flags setting failed\n"); + return -EOPNOTSUPP; + } + + if (local->func->reset_port(dev) || + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_MONITOR << 8), + 0, NULL, NULL)) { + printk(KERN_DEBUG "Setting monitor mode failed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int hostap_monitor_mode_disable(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return -1; + + printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name); + dev->type = ARPHRD_ETHER; + dev->hard_header_parse = local->saved_eth_header_parse; + if (local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_STOP << 8), + 0, NULL, NULL)) + return -1; + return hostap_set_encryption(local); +} + + +static int prism2_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int double_reset = 0; + + if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA && + *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT && + *mode != IW_MODE_MONITOR) + return -EOPNOTSUPP; + +#ifdef PRISM2_NO_STATION_MODES + if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA) + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ + + if (*mode == local->iw_mode) + return 0; + + if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') { + printk(KERN_WARNING "%s: empty SSID not allowed in Master " + "mode\n", dev->name); + return -EINVAL; + } + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_disable(local); + + if (local->iw_mode == IW_MODE_ADHOC && *mode == IW_MODE_MASTER) { + /* There seems to be a firmware bug in at least STA f/w v1.5.6 + * that leaves beacon frames to use IBSS type when moving from + * IBSS to Host AP mode. Doing double Port0 reset seems to be + * enough to workaround this. */ + double_reset = 1; + } + + printk(KERN_DEBUG "prism2: %s: operating mode changed " + "%d -> %d\n", dev->name, local->iw_mode, *mode); + local->iw_mode = *mode; + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_enable(local); + else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + dev->name); + local->host_encrypt = 1; + } + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) + return -EOPNOTSUPP; + + if (local->func->reset_port(dev)) + return -EINVAL; + if (double_reset && local->func->reset_port(dev)) + return -EINVAL; + + if (local->iw_mode != IW_MODE_INFRA && local->iw_mode != IW_MODE_ADHOC) + { + /* netif_carrier is used only in client modes for now, so make + * sure carrier is on when moving to non-client modes. */ + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + } + return 0; +} + + +static int prism2_ioctl_giwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + switch (iface->type) { + case HOSTAP_INTERFACE_STA: + *mode = IW_MODE_INFRA; + break; + case HOSTAP_INTERFACE_WDS: + *mode = IW_MODE_REPEAT; + break; + default: + *mode = local->iw_mode; + break; + } + return 0; +} + + +static int prism2_ioctl_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + int ret = 0; + + if (wrq->disabled) + return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0); + + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ALL_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ON: + break; + default: + return -EINVAL; + } + + if (wrq->flags & IW_POWER_TIMEOUT) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + if (wrq->flags & IW_POWER_PERIOD) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + + return ret; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 enable, mcast; + + if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1) + < 0) + return -EINVAL; + + if (!__le16_to_cpu(enable)) { + rrq->disabled = 1; + return 0; + } + + rrq->disabled = 0; + + if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + u16 timeout; + if (local->func->get_rid(dev, + HFA384X_RID_CNFPMHOLDOVERDURATION, + &timeout, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_TIMEOUT; + rrq->value = __le16_to_cpu(timeout) * 1024; + } else { + u16 period; + if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + &period, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_PERIOD; + rrq->value = __le16_to_cpu(period) * 1024; + } + + if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast, + 2, 1) < 0) + return -EINVAL; + + if (__le16_to_cpu(mcast)) + rrq->flags |= IW_POWER_ALL_R; + else + rrq->flags |= IW_POWER_UNICAST_R; + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (rrq->disabled) + return -EINVAL; + + /* setting retry limits is not supported with the current station + * firmware code; simulate this with alternative retry count for now */ + if (rrq->flags == IW_RETRY_LIMIT) { + if (rrq->value < 0) { + /* disable manual retry count setting and use firmware + * defaults */ + local->manual_retry_count = -1; + local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY; + } else { + if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + rrq->value)) { + printk(KERN_DEBUG "%s: Alternate retry count " + "setting to %d failed\n", + dev->name, rrq->value); + return -EOPNOTSUPP; + } + + local->manual_retry_count = rrq->value; + local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY; + } + return 0; + } + + return -EOPNOTSUPP; + +#if 0 + /* what could be done, if firmware would support this.. */ + + if (rrq->flags & IW_RETRY_LIMIT) { + if (rrq->flags & IW_RETRY_MAX) + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + else if (rrq->flags & IW_RETRY_MIN) + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + else { + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + } + + } + + if (rrq->flags & IW_RETRY_LIFETIME) { + HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024; + } + + return 0; +#endif /* 0 */ +} + +static int prism2_ioctl_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 shortretry, longretry, lifetime, altretry; + + if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME, + &lifetime, 2, 1) < 0) + return -EINVAL; + + le16_to_cpus(&shortretry); + le16_to_cpus(&longretry); + le16_to_cpus(&lifetime); + + rrq->disabled = 0; + + if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + rrq->flags = IW_RETRY_LIFETIME; + rrq->value = lifetime * 1024; + } else { + if (local->manual_retry_count >= 0) { + rrq->flags = IW_RETRY_LIMIT; + if (local->func->get_rid(dev, + HFA384X_RID_CNFALTRETRYCOUNT, + &altretry, 2, 1) >= 0) + rrq->value = le16_to_cpu(altretry); + else + rrq->value = local->manual_retry_count; + } else if ((rrq->flags & IW_RETRY_MAX)) { + rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + rrq->value = longretry; + } else { + rrq->flags = IW_RETRY_LIMIT; + rrq->value = shortretry; + if (shortretry != longretry) + rrq->flags |= IW_RETRY_MIN; + } + } + return 0; +} + + +/* Note! This TX power controlling is experimental and should not be used in + * production use. It just sets raw power register and does not use any kind of + * feedback information from the measured TX power (CR58). This is now + * commented out to make sure that it is not used by accident. TX power + * configuration will be enabled again after proper algorithm using feedback + * has been implemented. */ + +#ifdef RAW_TXPOWER_SETTING +/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping.. + * This version assumes following mapping: + * CR31 is 7-bit value with -64 to +63 range. + * -64 is mapped into +20dBm and +63 into -43dBm. + * This is certainly not an exact mapping for every card, but at least + * increasing dBm value should correspond to increasing TX power. + */ + +static int prism2_txpower_hfa386x_to_dBm(u16 val) +{ + signed char tmp; + + if (val > 255) + val = 255; + + tmp = val; + tmp >>= 2; + + return -12 - tmp; +} + +static u16 prism2_txpower_dBm_to_hfa386x(int val) +{ + signed char tmp; + + if (val > 20) + return 128; + else if (val < -43) + return 127; + + tmp = val; + tmp = -12 - tmp; + tmp <<= 2; + + return (unsigned char) tmp; +} +#endif /* RAW_TXPOWER_SETTING */ + + +static int prism2_ioctl_siwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; +#ifdef RAW_TXPOWER_SETTING + char *tmp; +#endif + u16 val; + int ret = 0; + + if (rrq->disabled) { + if (local->txpower_type != PRISM2_TXPOWER_OFF) { + val = 0xff; /* use all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, + &val, NULL); + printk(KERN_DEBUG "%s: Turning radio off: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_OFF; + } + return (ret ? -EOPNOTSUPP : 0); + } + + if (local->txpower_type == PRISM2_TXPOWER_OFF) { + val = 0; /* disable all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, &val, NULL); + printk(KERN_DEBUG "%s: Turning radio on: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_UNKNOWN; + } + +#ifdef RAW_TXPOWER_SETTING + if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) { + printk(KERN_DEBUG "Setting ALC on\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_AUTO; + return 0; + } + + if (local->txpower_type != PRISM2_TXPOWER_FIXED) { + printk(KERN_DEBUG "Setting ALC off\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_FIXED; + } + + if (rrq->flags == IW_TXPOW_DBM) + tmp = "dBm"; + else if (rrq->flags == IW_TXPOW_MWATT) + tmp = "mW"; + else + tmp = "UNKNOWN"; + printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp); + + if (rrq->flags != IW_TXPOW_DBM) { + printk("SIOCSIWTXPOW with mW is not supported; use dBm\n"); + return -EOPNOTSUPP; + } + + local->txpower = rrq->value; + val = prism2_txpower_dBm_to_hfa386x(local->txpower); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_MANUAL_TX_POWER, &val, NULL)) + ret = -EOPNOTSUPP; +#else /* RAW_TXPOWER_SETTING */ + if (rrq->fixed) + ret = -EOPNOTSUPP; +#endif /* RAW_TXPOWER_SETTING */ + + return ret; +} + +static int prism2_ioctl_giwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef RAW_TXPOWER_SETTING + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 resp0; + + rrq->flags = IW_TXPOW_DBM; + rrq->disabled = 0; + rrq->fixed = 0; + + if (local->txpower_type == PRISM2_TXPOWER_AUTO) { + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_MANUAL_TX_POWER, + NULL, &resp0) == 0) { + rrq->value = prism2_txpower_hfa386x_to_dBm(resp0); + } else { + /* Could not get real txpower; guess 15 dBm */ + rrq->value = 15; + } + } else if (local->txpower_type == PRISM2_TXPOWER_OFF) { + rrq->value = 0; + rrq->disabled = 1; + } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) { + rrq->value = local->txpower; + rrq->fixed = 1; + } else { + printk("SIOCGIWTXPOW - unknown txpower_type=%d\n", + local->txpower_type); + } + return 0; +#else /* RAW_TXPOWER_SETTING */ + return -EOPNOTSUPP; +#endif /* RAW_TXPOWER_SETTING */ +} + + +#ifndef PRISM2_NO_STATION_MODES + +/* HostScan request works with and without host_roaming mode. In addition, it + * does not break current association. However, it requires newer station + * firmware version (>= 1.3.1) than scan request. */ +static int prism2_request_hostscan(struct net_device *dev, + u8 *ssid, u8 ssid_len) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hfa384x_hostscan_request scan_req; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(local->channel_mask); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + if (ssid) { + if (ssid_len > 32) + return -EINVAL; + scan_req.target_ssid_len = cpu_to_le16(ssid_len); + memcpy(scan_req.target_ssid, ssid, ssid_len); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "%s: HOSTSCAN failed\n", dev->name); + return -EINVAL; + } + return 0; +} + + +static int prism2_request_scan(struct net_device *dev) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct hfa384x_scan_request scan_req; + int ret = 0; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(local->channel_mask); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + + /* FIX: + * It seems to be enough to set roaming mode for a short moment to + * host-based and then setup scanrequest data and return the mode to + * firmware-based. + * + * Master mode would need to drop to Managed mode for a short while + * to make scanning work.. Or sweep through the different channels and + * use passive scan based on beacons. */ + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_HOST); + + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "SCANREQUEST failed\n"); + ret = -EINVAL; + } + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_FIRMWARE); + + return 0; +} + +#else /* !PRISM2_NO_STATION_MODES */ + +static inline int prism2_request_hostscan(struct net_device *dev, + u8 *ssid, u8 ssid_len) +{ + return -EOPNOTSUPP; +} + + +static inline int prism2_request_scan(struct net_device *dev) +{ + return -EOPNOTSUPP; +} + +#endif /* !PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret; + u8 *ssid = NULL, ssid_len = 0; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In master mode, we just return the results of our local + * tables, so we don't need to start anything... + * Jean II */ + data->length = 0; + return 0; + } + + if (!local->dev_enabled) + return -ENETDOWN; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) + ret = prism2_request_hostscan(dev, ssid, ssid_len); + else + ret = prism2_request_scan(dev); + + if (ret == 0) + local->scan_timestamp = jiffies; + + /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */ + + return ret; +} + + +#if WIRELESS_EXT > 17 +static int prism2_ioctl_siwscanext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret; + u8 *ssid = NULL, ssid_len = 0; + struct iw_scan_req *req = (struct iw_scan_req *) extra; + + if (data->length < sizeof(struct iw_scan_req)) + return -EINVAL; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In master mode, we just return the results of our local + * tables, so we don't need to start anything... + * Jean II */ + data->length = 0; + return 0; + } + + if (!local->dev_enabled) + return -ENETDOWN; + + if (data->flags & IW_SCAN_THIS_ESSID) { + ssid = req->essid; + ssid_len = req->essid_len; + + if (ssid_len && + ((local->iw_mode != IW_MODE_INFRA && + local->iw_mode != IW_MODE_ADHOC) || + (local->sta_fw_ver < PRISM2_FW_VER(1,3,1)))) + return -EOPNOTSUPP; + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) + ret = prism2_request_hostscan(dev, ssid, ssid_len); + else + ret = prism2_request_scan(dev); + + if (ret == 0) + local->scan_timestamp = jiffies; + + /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */ + + return ret; +} +#endif /* WIRELESS_EXT > 17 */ + + +#ifndef PRISM2_NO_STATION_MODES +static char * __prism2_translate_scan(local_info_t *local, + struct hfa384x_scan_result *scan, + struct hfa384x_hostscan_result *hscan, + int hostscan, + struct hostap_bss_info *bss, u8 *bssid, + char *current_ev, char *end_buf) +{ + int i, chan; + struct iw_event iwe; + char *current_val; + u16 capabilities; + u8 *pos; + u8 *ssid; + size_t ssid_len; +#if WIRELESS_EXT > 14 + char buf[MAX_WPA_IE_LEN * 2 + 30]; +#endif /* WIRELESS_EXT > 14 */ + + if (bss) { + ssid = bss->ssid; + ssid_len = bss->ssid_len; + } else { + ssid = hostscan ? hscan->ssid : scan->ssid; + ssid_len = le16_to_cpu(hostscan ? hscan->ssid_len : + scan->ssid_len); + } + if (ssid_len > 32) + ssid_len = 32; + + /* First entry *MUST* be the AP MAC address */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN); + /* FIX: + * I do not know how this is possible, but iwe_stream_add_event + * seems to re-order memcpy execution so that len is set only + * after copying.. Pre-setting len here "fixes" this, but real + * problems should be solved (after which these iwe.len + * settings could be removed from this function). */ + iwe.len = IW_EV_ADDR_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ssid_len; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (bss) { + capabilities = bss->capab_info; + } else { + capabilities = le16_to_cpu(hostscan ? hscan->capability : + scan->capability); + } + if (capabilities & (WLAN_CAPABILITY_ESS | + WLAN_CAPABILITY_IBSS)) { + if (capabilities & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + iwe.len = IW_EV_UINT_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_UINT_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + if (hscan || scan) { + chan = hostscan ? hscan->chid : scan->chid; + } else if (bss) { + chan = bss->chan; + } else { + chan = 0; + } + + if (chan > 0) { + iwe.u.freq.m = freq_list[le16_to_cpu(chan - 1)] * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + } + + if (scan || hscan) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (hostscan) { + iwe.u.qual.level = le16_to_cpu(hscan->sl); + iwe.u.qual.noise = le16_to_cpu(hscan->anl); + } else { + iwe.u.qual.level = + HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->sl)); + iwe.u.qual.noise = + HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->anl)); + } + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (capabilities & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + + /* TODO: add SuppRates into BSS table */ + if (scan || hscan) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + current_val = current_ev + IW_EV_LCP_LEN; + pos = hostscan ? hscan->sup_rates : scan->sup_rates; + for (i = 0; i < sizeof(scan->sup_rates); i++) { + if (pos[i] == 0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value( + current_ev, current_val, end_buf, &iwe, + IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + +#if WIRELESS_EXT > 14 + /* TODO: add BeaconInt,resp_rate,atim into BSS table */ + if (scan || hscan) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "bcn_int=%d", + le16_to_cpu(hostscan ? hscan->beacon_interval : + scan->beacon_interval)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "resp_rate=%d", le16_to_cpu(hostscan ? + hscan->rate : + scan->rate)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + + if (hostscan && (capabilities & WLAN_CAPABILITY_IBSS)) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "atim=%d", le16_to_cpu(hscan->atim)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + } + +#if WIRELESS_EXT > 17 + if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->wpa_ie_len; + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, bss->wpa_ie); + } + + if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->rsn_ie_len; + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, bss->rsn_ie); + } +#else /* WIRELESS_EXT > 17 */ + if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN) { + u8 *p = buf; + p += sprintf(p, "wpa_ie="); + for (i = 0; i < bss->wpa_ie_len; i++) { + p += sprintf(p, "%02x", bss->wpa_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, buf); + } + + if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) { + u8 *p = buf; + p += sprintf(p, "rsn_ie="); + for (i = 0; i < bss->rsn_ie_len; i++) { + p += sprintf(p, "%02x", bss->rsn_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, buf); + } +#endif /* WIRELESS_EXT > 17 */ +#endif /* WIRELESS_EXT > 14 */ + + return current_ev; +} + + +/* Translate scan data returned from the card to a card independant + * format that the Wireless Tools will understand - Jean II */ +static inline int prism2_translate_scan(local_info_t *local, + char *buffer, int buflen) +{ + struct hfa384x_scan_result *scan; + struct hfa384x_hostscan_result *hscan; + int entries, entry, hostscan; + char *current_ev = buffer; + char *end_buf = buffer + buflen; + u8 *bssid; + struct list_head *ptr; + + spin_lock_bh(&local->lock); + + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + bss->included = 0; + } + + hostscan = local->last_scan_type == PRISM2_HOSTSCAN; + entries = hostscan ? local->last_hostscan_results_count : + local->last_scan_results_count; + for (entry = 0; entry < entries; entry++) { + int found = 0; + scan = &local->last_scan_results[entry]; + hscan = &local->last_hostscan_results[entry]; + + bssid = hostscan ? hscan->bssid : scan->bssid; + + /* Report every SSID if the AP is using multiple SSIDs. If no + * BSS record is found (e.g., when WPA mode is disabled), + * report the AP once. */ + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { + bss->included = 1; + current_ev = __prism2_translate_scan( + local, scan, hscan, hostscan, bss, + bssid, current_ev, end_buf); + found++; + } + } + if (!found) { + current_ev = __prism2_translate_scan( + local, scan, hscan, hostscan, NULL, bssid, + current_ev, end_buf); + } +#if WIRELESS_EXT > 16 + /* Check if there is space for one more entry */ + if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + spin_unlock_bh(&local->lock); + return -E2BIG; + } +#endif /* WIRELESS_EXT > 16 */ + } + + /* Prism2 firmware has limits (32 at least in some versions) for number + * of BSSes in scan results. Extend this limit by using local BSS list. + */ + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + if (bss->included) + continue; + current_ev = __prism2_translate_scan(local, NULL, NULL, 0, bss, + bss->bssid, current_ev, + end_buf); +#if WIRELESS_EXT > 16 + /* Check if there is space for one more entry */ + if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + spin_unlock_bh(&local->lock); + return -E2BIG; + } +#endif /* WIRELESS_EXT > 16 */ + } + + spin_unlock_bh(&local->lock); + + return current_ev - buffer; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int res; + + /* Wait until the scan is finished. We can probably do better + * than that - Jean II */ + if (local->scan_timestamp && + time_before(jiffies, local->scan_timestamp + 3 * HZ)) { + /* Important note : we don't want to block the caller + * until results are ready for various reasons. + * First, managing wait queues is complex and racy + * (there may be multiple simultaneous callers). + * Second, we grab some rtnetlink lock before comming + * here (in dev_ioctl()). + * Third, the caller can wait on the Wireless Event + * - Jean II */ + return -EAGAIN; + } + local->scan_timestamp = 0; + + res = prism2_translate_scan(local, extra, data->length); + + if (res >= 0) { + data->length = res; + return 0; + } else { + data->length = 0; + return res; + } +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int res; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In MASTER mode, it doesn't make sense to go around + * scanning the frequencies and make the stations we serve + * wait when what the user is really interested about is the + * list of stations and access points we are talking to. + * So, just extract results from our cache... + * Jean II */ + + /* Translate to WE format */ + res = prism2_ap_translate_scan(dev, extra); + if (res >= 0) { + printk(KERN_DEBUG "Scan result translation succeeded " + "(length=%d)\n", res); + data->length = res; + return 0; + } else { + printk(KERN_DEBUG + "Scan result translation failed (res=%d)\n", + res); + data->length = 0; + return res; + } + } else { + /* Station mode */ + return prism2_ioctl_giwscan_sta(dev, info, data, extra); + } +} + + +static const struct iw_priv_args prism2_priv[] = { + { PRISM2_IOCTL_MONITOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" }, + { PRISM2_IOCTL_READMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" }, + { PRISM2_IOCTL_WRITEMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" }, + { PRISM2_IOCTL_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" }, + { PRISM2_IOCTL_INQUIRE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" }, + { PRISM2_IOCTL_SET_RID_WORD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" }, + { PRISM2_IOCTL_MACCMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" }, +#ifdef PRISM2_USE_WE_TYPE_ADDR + { PRISM2_IOCTL_WDS_ADD, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" }, + { PRISM2_IOCTL_WDS_DEL, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" }, + { PRISM2_IOCTL_ADDMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" }, + { PRISM2_IOCTL_DELMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" }, + { PRISM2_IOCTL_KICKMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" }, +#else /* PRISM2_USE_WE_TYPE_ADDR */ + { PRISM2_IOCTL_WDS_ADD, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "wds_add" }, + { PRISM2_IOCTL_WDS_DEL, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "wds_del" }, + { PRISM2_IOCTL_ADDMAC, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "addmac" }, + { PRISM2_IOCTL_DELMAC, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "delmac" }, + { PRISM2_IOCTL_KICKMAC, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "kickmac" }, +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + /* --- raw access to sub-ioctls --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" }, +#ifdef PRISM2_USE_WE_SUB_IOCTLS + /* --- sub-ioctls handlers --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, + /* --- sub-ioctls definitions --- */ + { PRISM2_PARAM_TXRATECTRL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" }, + { PRISM2_PARAM_TXRATECTRL, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" }, + { PRISM2_PARAM_BEACON_INT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" }, + { PRISM2_PARAM_BEACON_INT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_PSEUDO_IBSS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" }, + { PRISM2_PARAM_PSEUDO_IBSS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_ALC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" }, + { PRISM2_PARAM_ALC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" }, + { PRISM2_PARAM_DUMP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" }, + { PRISM2_PARAM_DUMP, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" }, + { PRISM2_PARAM_DTIM_PERIOD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" }, + { PRISM2_PARAM_DTIM_PERIOD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" }, + { PRISM2_PARAM_MAX_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" }, + { PRISM2_PARAM_MAX_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" }, + { PRISM2_PARAM_HOST_ENCRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" }, + { PRISM2_PARAM_HOST_ENCRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "busmaster_rx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbusmaster_rx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "busmaster_tx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbusmaster_tx" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_HOST_ROAMING, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" }, + { PRISM2_PARAM_HOST_ROAMING, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_BCRX_STA_KEY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" }, + { PRISM2_PARAM_BCRX_STA_KEY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" }, + { PRISM2_PARAM_IEEE_802_1X, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" }, + { PRISM2_PARAM_IEEE_802_1X, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" }, + { PRISM2_PARAM_ANTSEL_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" }, + { PRISM2_PARAM_ANTSEL_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" }, + { PRISM2_PARAM_ANTSEL_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" }, + { PRISM2_PARAM_ANTSEL_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" }, + { PRISM2_PARAM_MONITOR_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" }, + { PRISM2_PARAM_MONITOR_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" }, + { PRISM2_PARAM_WDS_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" }, + { PRISM2_PARAM_WDS_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" }, + { PRISM2_PARAM_HOSTSCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" }, + { PRISM2_PARAM_HOSTSCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" }, + { PRISM2_PARAM_AP_SCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" }, + { PRISM2_PARAM_AP_SCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" }, + { PRISM2_PARAM_ENH_SEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" }, + { PRISM2_PARAM_ENH_SEC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" }, +#ifdef PRISM2_IO_DEBUG + { PRISM2_PARAM_IO_DEBUG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" }, + { PRISM2_PARAM_IO_DEBUG, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" }, +#endif /* PRISM2_IO_DEBUG */ + { PRISM2_PARAM_BASIC_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" }, + { PRISM2_PARAM_BASIC_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" }, + { PRISM2_PARAM_OPER_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" }, + { PRISM2_PARAM_OPER_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" }, + { PRISM2_PARAM_HOSTAPD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" }, + { PRISM2_PARAM_HOSTAPD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" }, + { PRISM2_PARAM_HOSTAPD_STA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd_sta" }, + { PRISM2_PARAM_HOSTAPD_STA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd_sta" }, + { PRISM2_PARAM_WPA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" }, + { PRISM2_PARAM_WPA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwpa" }, + { PRISM2_PARAM_PRIVACY_INVOKED, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy_invoked" }, + { PRISM2_PARAM_PRIVACY_INVOKED, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprivacy_invo" }, + { PRISM2_PARAM_TKIP_COUNTERMEASURES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tkip_countermea" }, + { PRISM2_PARAM_TKIP_COUNTERMEASURES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettkip_counter" }, + { PRISM2_PARAM_DROP_UNENCRYPTED, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "drop_unencrypte" }, + { PRISM2_PARAM_DROP_UNENCRYPTED, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdrop_unencry" }, +#endif /* PRISM2_USE_WE_SUB_IOCTLS */ +}; + + +static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int *i = (int *) extra; + int param = *i; + int value = *(i + 1); + int ret = 0; + u16 val; + + switch (param) { + case PRISM2_PARAM_TXRATECTRL: + local->fw_tx_rate_control = value; + break; + + case PRISM2_PARAM_BEACON_INT: + if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) || + local->func->reset_port(dev)) + ret = -EINVAL; + else + local->beacon_int = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_PSEUDO_IBSS: + if (value == local->pseudo_adhoc) + break; + + if (value != 0 && value != 1) { + ret = -EINVAL; + break; + } + + printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n", + dev->name, local->pseudo_adhoc, value); + local->pseudo_adhoc = value; + if (local->iw_mode != IW_MODE_ADHOC) + break; + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) { + ret = -EOPNOTSUPP; + break; + } + + if (local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_ALC: + printk(KERN_DEBUG "%s: %s ALC\n", dev->name, + value == 0 ? "Disabling" : "Enabling"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), + value == 0 ? 0 : 1, &val, NULL); + break; + + case PRISM2_PARAM_DUMP: + local->frame_dump = value; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->ap_policy = value; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (value < 0 || value > 7 * 24 * 60 * 60) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->max_inactivity = value * HZ; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + local->ap->bridge_packets = value; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + if (value < 0 || value > 65535) { + ret = -EINVAL; + break; + } + if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value) + || local->func->reset_port(dev)) + ret = -EINVAL; + else + local->dtim_period = value; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + local->ap->nullfunc_ack = value; + break; + + case PRISM2_PARAM_MAX_WDS: + local->wds_max_connections = value; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) { + if (!local->ap->autom_ap_wds && value) { + /* add WDS link to all APs in STA table */ + hostap_add_wds_links(local); + } + local->ap->autom_ap_wds = value; + } + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + local->auth_algs = value; + if (hostap_set_auth_algs(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + local->monitor_allow_fcserr = value; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + local->host_encrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + local->host_decrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX: + local->bus_master_threshold_rx = value; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX: + local->bus_master_threshold_tx = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_HOST_ROAMING: + if (value < 0 || value > 2) { + ret = -EINVAL; + break; + } + local->host_roaming = value; + if (hostap_set_roaming(local) || local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_BCRX_STA_KEY: + local->bcrx_sta_key = value; + break; + + case PRISM2_PARAM_IEEE_802_1X: + local->ieee_802_1x = value; + break; + + case PRISM2_PARAM_ANTSEL_TX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_tx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_ANTSEL_RX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_rx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_MONITOR_TYPE: + if (value != PRISM2_MONITOR_80211 && + value != PRISM2_MONITOR_CAPHDR && + value != PRISM2_MONITOR_PRISM) { + ret = -EINVAL; + break; + } + local->monitor_type = value; + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_set_type(local); + break; + + case PRISM2_PARAM_WDS_TYPE: + local->wds_type = value; + break; + + case PRISM2_PARAM_HOSTSCAN: + { + struct hfa384x_hostscan_request scan_req; + u16 rate; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + switch (value) { + case 1: rate = HFA384X_RATES_1MBPS; break; + case 2: rate = HFA384X_RATES_2MBPS; break; + case 3: rate = HFA384X_RATES_5MBPS; break; + case 4: rate = HFA384X_RATES_11MBPS; break; + default: rate = HFA384X_RATES_1MBPS; break; + } + scan_req.txrate = cpu_to_le16(rate); + /* leave SSID empty to accept all SSIDs */ + + if (local->iw_mode == IW_MODE_MASTER) { + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_BSS) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Leaving Host AP mode " + "for HostScan failed\n"); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "HOSTSCAN failed\n"); + ret = -EINVAL; + } + if (local->iw_mode == IW_MODE_MASTER) { + wait_queue_t __wait; + init_waitqueue_entry(&__wait, current); + add_wait_queue(&local->hostscan_wq, &__wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + if (signal_pending(current)) + ret = -EINTR; + set_current_state(TASK_RUNNING); + remove_wait_queue(&local->hostscan_wq, &__wait); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_HOSTAP) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Returning to Host AP mode " + "after HostScan failed\n"); + } + break; + } + + case PRISM2_PARAM_AP_SCAN: + local->passive_scan_interval = value; + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + if (value > 0) { + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + add_timer(&local->passive_scan_timer); + } + break; + + case PRISM2_PARAM_ENH_SEC: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + local->enh_sec = value; + if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, + local->enh_sec) || + local->func->reset_port(dev)) { + printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w " + "1.6.3 or newer\n", dev->name); + ret = -EOPNOTSUPP; + } + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + local->io_debug_enabled = value; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + if ((value & local->tx_rate_control) != value || value == 0) { + printk(KERN_INFO "%s: invalid basic rate set - basic " + "rates must be in supported rate set\n", + dev->name); + ret = -EINVAL; + break; + } + local->basic_rates = value; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_OPER_RATES: + local->tx_rate_control = value; + if (hostap_set_rate(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOSTAPD: + ret = hostap_set_hostapd(local, value, 1); + break; + + case PRISM2_PARAM_HOSTAPD_STA: + ret = hostap_set_hostapd_sta(local, value, 1); + break; + + case PRISM2_PARAM_WPA: + local->wpa = value; + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + ret = -EOPNOTSUPP; + else if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, + value ? 1 : 0)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + local->privacy_invoked = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_TKIP_COUNTERMEASURES: + local->tkip_countermeasures = value; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + local->drop_unencrypted = value; + break; + + default: + printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n", + dev->name, param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int *param = (int *) extra; + int ret = 0; + + switch (*param) { + case PRISM2_PARAM_TXRATECTRL: + *param = local->fw_tx_rate_control; + break; + + case PRISM2_PARAM_BEACON_INT: + *param = local->beacon_int; + break; + + case PRISM2_PARAM_PSEUDO_IBSS: + *param = local->pseudo_adhoc; + break; + + case PRISM2_PARAM_ALC: + ret = -EOPNOTSUPP; /* FIX */ + break; + + case PRISM2_PARAM_DUMP: + *param = local->frame_dump; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (local->ap != NULL) + *param = local->ap->ap_policy; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (local->ap != NULL) + *param = local->ap->max_inactivity / HZ; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + *param = local->ap->bridge_packets; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + *param = local->dtim_period; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + *param = local->ap->nullfunc_ack; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_MAX_WDS: + *param = local->wds_max_connections; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) + *param = local->ap->autom_ap_wds; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + *param = local->auth_algs; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + *param = local->monitor_allow_fcserr; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + *param = local->host_encrypt; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + *param = local->host_decrypt; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX: + *param = local->bus_master_threshold_rx; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX: + *param = local->bus_master_threshold_tx; + break; + + case PRISM2_PARAM_HOST_ROAMING: + *param = local->host_roaming; + break; + + case PRISM2_PARAM_BCRX_STA_KEY: + *param = local->bcrx_sta_key; + break; + + case PRISM2_PARAM_IEEE_802_1X: + *param = local->ieee_802_1x; + break; + + case PRISM2_PARAM_ANTSEL_TX: + *param = local->antsel_tx; + break; + + case PRISM2_PARAM_ANTSEL_RX: + *param = local->antsel_rx; + break; + + case PRISM2_PARAM_MONITOR_TYPE: + *param = local->monitor_type; + break; + + case PRISM2_PARAM_WDS_TYPE: + *param = local->wds_type; + break; + + case PRISM2_PARAM_HOSTSCAN: + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_SCAN: + *param = local->passive_scan_interval; + break; + + case PRISM2_PARAM_ENH_SEC: + *param = local->enh_sec; + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + *param = local->io_debug_enabled; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + *param = local->basic_rates; + break; + + case PRISM2_PARAM_OPER_RATES: + *param = local->tx_rate_control; + break; + + case PRISM2_PARAM_HOSTAPD: + *param = local->hostapd; + break; + + case PRISM2_PARAM_HOSTAPD_STA: + *param = local->hostapd_sta; + break; + + case PRISM2_PARAM_WPA: + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + ret = -EOPNOTSUPP; + *param = local->wpa; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + *param = local->privacy_invoked; + break; + + case PRISM2_PARAM_TKIP_COUNTERMEASURES: + *param = local->tkip_countermeasures; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + *param = local->drop_unencrypted; + break; + + default: + printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n", + dev->name, *param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int prism2_ioctl_priv_readmif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 resp0; + + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL, + &resp0)) + return -EOPNOTSUPP; + else + *extra = resp0; + + return 0; +} + + +static int prism2_ioctl_priv_writemif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u16 cr, val; + + cr = *extra; + val = *(extra + 1); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret = 0; + u32 mode; + + printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor " + "- update software to use iwconfig mode monitor\n", + dev->name, current->pid, current->comm); + + /* Backward compatibility code - this can be removed at some point */ + + if (*i == 0) { + /* Disable monitor mode - old mode was not saved, so go to + * Master mode */ + mode = IW_MODE_MASTER; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + } else if (*i == 1) { + /* netlink socket mode is not supported anymore since it did + * not separate different devices from each other and was not + * best method for delivering large amount of packets to + * user space */ + ret = -EOPNOTSUPP; + } else if (*i == 2 || *i == 3) { + switch (*i) { + case 2: + local->monitor_type = PRISM2_MONITOR_80211; + break; + case 3: + local->monitor_type = PRISM2_MONITOR_PRISM; + break; + } + mode = IW_MODE_MONITOR; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + hostap_monitor_mode_enable(local); + } else + ret = -EINVAL; + + return ret; +} + + +static int prism2_ioctl_priv_reset(struct net_device *dev, int *i) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i); + switch (*i) { + case 0: + /* Disable and enable card */ + local->func->hw_shutdown(dev, 1); + local->func->hw_config(dev, 0); + break; + + case 1: + /* COR sreset */ + local->func->hw_reset(dev); + break; + + case 2: + /* Disable and enable port 0 */ + local->func->reset_port(dev); + break; + + case 3: + prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); + if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + case 4: + if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + default: + printk(KERN_DEBUG "Unknown reset request %d\n", *i); + return -EOPNOTSUPP; + } + + return 0; +} + + +#ifndef PRISM2_USE_WE_TYPE_ADDR +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + return -1; +} + +static int macstr2addr(char *macstr, u8 *addr) +{ + int i, val, val2; + char *pos = macstr; + + for (i = 0; i < 6; i++) { + val = hex2int(*pos++); + if (val < 0) + return -1; + val2 = hex2int(*pos++); + if (val2 < 0) + return -1; + addr[i] = (val * 16 + val2) & 0xff; + + if (i < 5 && *pos++ != ':') + return -1; + } + + return 0; +} + + +static int prism2_ioctl_priv_wds(struct net_device *dev, int add, char *macstr) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u8 addr[6]; + + if (macstr2addr(macstr, addr)) { + printk(KERN_DEBUG "Invalid MAC address\n"); + return -EINVAL; + } + + if (add) + return prism2_wds_add(local, addr, 1); + else + return prism2_wds_del(local, addr, 1, 0); +} +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + + +static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i) +{ + int rid = *i; + int value = *(i + 1); + + printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value); + + if (hostap_set_word(dev, rid, value)) + return -EINVAL; + + return 0; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd) +{ + int ret = 0; + + switch (*cmd) { + case AP_MAC_CMD_POLICY_OPEN: + local->ap->mac_restrictions.policy = MAC_POLICY_OPEN; + break; + case AP_MAC_CMD_POLICY_ALLOW: + local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW; + break; + case AP_MAC_CMD_POLICY_DENY: + local->ap->mac_restrictions.policy = MAC_POLICY_DENY; + break; + case AP_MAC_CMD_FLUSH: + ap_control_flush_macs(&local->ap->mac_restrictions); + break; + case AP_MAC_CMD_KICKALL: + ap_control_kickall(local->ap); + hostap_deauth_all_stas(local->dev, local->ap, 0); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +enum { AP_CTRL_MAC_ADD, AP_CTRL_MAC_DEL, AP_CTRL_MAC_KICK }; + +#ifndef PRISM2_USE_WE_TYPE_ADDR +static int ap_mac_ioctl(local_info_t *local, char *macstr, int cmd) +{ + u8 addr[6]; + + if (macstr2addr(macstr, addr)) { + printk(KERN_DEBUG "Invalid MAC address '%s'\n", macstr); + return -EINVAL; + } + + switch (cmd) { + case AP_CTRL_MAC_ADD: + return ap_control_add_mac(&local->ap->mac_restrictions, addr); + case AP_CTRL_MAC_DEL: + return ap_control_del_mac(&local->ap->mac_restrictions, addr); + case AP_CTRL_MAC_KICK: + return ap_control_kick_mac(local->ap, local->dev, addr); + default: + return -EOPNOTSUPP; + } +} +#endif /* PRISM2_USE_WE_TYPE_ADDR */ +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#ifdef PRISM2_DOWNLOAD_SUPPORT +static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p) +{ + struct prism2_download_param *param; + int ret = 0; + + if (p->length < sizeof(struct prism2_download_param) || + p->length > 1024 || !p->pointer) + return -EINVAL; + + param = (struct prism2_download_param *) + kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + if (p->length < sizeof(struct prism2_download_param) + + param->num_areas * sizeof(struct prism2_download_area)) { + ret = -EINVAL; + goto out; + } + + ret = local->func->download(local, param); + + out: + if (param != NULL) + kfree(param); + + return ret; +} +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + +static int prism2_set_genericelement(struct net_device *dev, u8 *elem, + size_t len) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + u8 *buf; + + /* Add 16-bit length in the beginning of the buffer because Prism2 RID + * includes it. */ + buf = kmalloc(len + 2, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + *((u16 *) buf) = cpu_to_le16(len); + memcpy(buf + 2, elem, len); + + kfree(local->generic_elem); + local->generic_elem = buf; + local->generic_elem_len = len + 2; + + return local->func->set_rid(local->dev, HFA384X_RID_GENERICELEMENT, + buf, len + 2); +} + + +#if WIRELESS_EXT > 17 +static int prism2_ioctl_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* Host AP driver does not use these parameters and allows + * wpa_supplicant to control them internally. */ + return -EOPNOTSUPP; + case IW_AUTH_TKIP_COUNTERMEASURES: + local->tkip_countermeasures = data->value; + break; + case IW_AUTH_DROP_UNENCRYPTED: + local->drop_unencrypted = data->value; + break; + case IW_AUTH_80211_AUTH_ALG: + local->auth_algs = data->value; + break; + case IW_AUTH_WPA_ENABLED: + if (data->value == 0) { + local->wpa = 0; + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + break; + prism2_set_genericelement(dev, "", 0); + local->host_roaming = 0; + local->privacy_invoked = 0; + if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, + 0) || + hostap_set_roaming(local) || + hostap_set_encryption(local) || + local->func->reset_port(dev)) + return -EINVAL; + break; + } + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + return -EOPNOTSUPP; + local->host_roaming = 2; + local->privacy_invoked = 1; + local->wpa = 1; + if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1) || + hostap_set_roaming(local) || + hostap_set_encryption(local) || + local->func->reset_port(dev)) + return -EINVAL; + break; + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + local->ieee_802_1x = data->value; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static int prism2_ioctl_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* Host AP driver does not use these parameters and allows + * wpa_supplicant to control them internally. */ + return -EOPNOTSUPP; + case IW_AUTH_TKIP_COUNTERMEASURES: + data->value = local->tkip_countermeasures; + break; + case IW_AUTH_DROP_UNENCRYPTED: + data->value = local->drop_unencrypted; + break; + case IW_AUTH_80211_AUTH_ALG: + data->value = local->auth_algs; + break; + case IW_AUTH_WPA_ENABLED: + data->value = local->wpa; + break; + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + data->value = local->ieee_802_1x; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static int prism2_ioctl_siwencodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; + int i, ret = 0; + struct hostap_crypto_ops *ops; + struct prism2_crypt_data **crypt; + void *sta_ptr; + u8 *addr; + const char *alg, *module; + + i = erq->flags & IW_ENCODE_INDEX; + if (i > WEP_KEYS) + return -EINVAL; + if (i < 1 || i > WEP_KEYS) + i = local->tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + addr = ext->addr.sa_data; + if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff && + addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) { + sta_ptr = NULL; + crypt = &local->crypt[i]; + } else { + if (i != 0) + return -EINVAL; + sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt); + if (sta_ptr == NULL) { + if (local->iw_mode == IW_MODE_INFRA) { + /* TODO: add STA entry for the current AP so + * that unicast key can be used. For now, this + * is emulated by using default key idx 0. */ + i = 0; + crypt = &local->crypt[i]; + } else + return -EINVAL; + } + } + + if ((erq->flags & IW_ENCODE_DISABLED) || + ext->alg == IW_ENCODE_ALG_NONE) { + if (*crypt) + prism2_crypt_delayed_deinit(local, crypt); + goto done; + } + + switch (ext->alg) { + case IW_ENCODE_ALG_WEP: + alg = "WEP"; + module = "hostap_crypt_wep"; + break; + case IW_ENCODE_ALG_TKIP: + alg = "TKIP"; + module = "hostap_crypt_tkip"; + break; + case IW_ENCODE_ALG_CCMP: + alg = "CCMP"; + module = "hostap_crypt_ccmp"; + break; + default: + printk(KERN_DEBUG "%s: unsupported algorithm %d\n", + local->dev->name, ext->alg); + ret = -EOPNOTSUPP; + goto done; + } + + ops = hostap_get_crypto_ops(alg); + if (ops == NULL) { + request_module(module); + ops = hostap_get_crypto_ops(alg); + } + if (ops == NULL) { + printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", + local->dev->name, alg); + ret = -EOPNOTSUPP; + goto done; + } + + if (sta_ptr || ext->alg != IW_ENCODE_ALG_WEP) { + /* station based encryption and other than WEP algorithms + * require host-based encryption, so force them on + * automatically */ + local->host_decrypt = local->host_encrypt = 1; + } + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct prism2_crypt_data *new_crypt; + + prism2_crypt_delayed_deinit(local, crypt); + + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = ops; + new_crypt->priv = new_crypt->ops->init(i); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + /* TODO: if ext_flags does not have IW_ENCODE_EXT_RX_SEQ_VALID, the + * existing seq# should not be changed. */ + /* TODO: if ext_flags has IW_ENCODE_EXT_TX_SEQ_VALID, next TX seq# + * should be changed to something else than zero. */ + if ((!(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) || ext->key_len > 0) + && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, + (*crypt)->priv) < 0) { + printk(KERN_DEBUG "%s: key setting failed\n", + local->dev->name); + ret = -EINVAL; + goto done; + } + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + if (!sta_ptr) + local->tx_keyidx = i; + else if (i) { + ret = -EINVAL; + goto done; + } + } + + + if (sta_ptr == NULL && ext->key_len > 0) { + int first = 1, j; + for (j = 0; j < WEP_KEYS; j++) { + if (j != i && local->crypt[j]) { + first = 0; + break; + } + } + if (first) + local->tx_keyidx = i; + } + + done: + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + local->open_wep = erq->flags & IW_ENCODE_OPEN; + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (ret == 0 && + (hostap_set_encryption(local) || + (local->iw_mode != IW_MODE_INFRA && + local->func->reset_port(local->dev)))) + ret = -EINVAL; + + return ret; +} + + +static int prism2_ioctl_giwencodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct prism2_crypt_data **crypt; + void *sta_ptr; + int max_key_len, i; + struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; + u8 *addr; + + max_key_len = erq->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > WEP_KEYS) + i = local->tx_keyidx; + else + i--; + + addr = ext->addr.sa_data; + if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff && + addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) { + sta_ptr = NULL; + crypt = &local->crypt[i]; + } else { + i = 0; + sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt); + if (sta_ptr == NULL) + return -EINVAL; + } + erq->flags = i + 1; + memset(ext, 0, sizeof(*ext)); + + if (*crypt == NULL || (*crypt)->ops == NULL) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + erq->flags |= IW_ENCODE_DISABLED; + } else { + if (strcmp((*crypt)->ops->name, "WEP") == 0) + ext->alg = IW_ENCODE_ALG_WEP; + else if (strcmp((*crypt)->ops->name, "TKIP") == 0) + ext->alg = IW_ENCODE_ALG_TKIP; + else if (strcmp((*crypt)->ops->name, "CCMP") == 0) + ext->alg = IW_ENCODE_ALG_CCMP; + else + return -EINVAL; + + if ((*crypt)->ops->get_key) { + ext->key_len = + (*crypt)->ops->get_key(ext->key, + max_key_len, + ext->tx_seq, + (*crypt)->priv); + if (ext->key_len && + (ext->alg == IW_ENCODE_ALG_TKIP || + ext->alg == IW_ENCODE_ALG_CCMP)) + ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; + } + } + + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + return 0; +} +#endif /* WIRELESS_EXT > 17 */ + + +static int prism2_ioctl_set_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int ret = 0; + struct hostap_crypto_ops *ops; + struct prism2_crypt_data **crypt; + void *sta_ptr; + + param->u.crypt.err = 0; + param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len != + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len) + return -EINVAL; + + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + if (param->u.crypt.idx >= WEP_KEYS) + return -EINVAL; + sta_ptr = NULL; + crypt = &local->crypt[param->u.crypt.idx]; + } else { + if (param->u.crypt.idx) + return -EINVAL; + sta_ptr = ap_crypt_get_ptrs( + local->ap, param->sta_addr, + (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT), + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (strcmp(param->u.crypt.alg, "none") == 0) { + if (crypt) + prism2_crypt_delayed_deinit(local, crypt); + goto done; + } + + ops = hostap_get_crypto_ops(param->u.crypt.alg); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { + request_module("hostap_crypt_wep"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { + request_module("hostap_crypt_tkip"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { + request_module("hostap_crypt_ccmp"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } + if (ops == NULL) { + printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", + local->dev->name, param->u.crypt.alg); + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; + ret = -EINVAL; + goto done; + } + + /* station based encryption and other than WEP algorithms require + * host-based encryption, so force them on automatically */ + local->host_decrypt = local->host_encrypt = 1; + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct prism2_crypt_data *new_crypt; + + prism2_crypt_delayed_deinit(local, crypt); + + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = ops; + new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED; + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) || + param->u.crypt.key_len > 0) && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(param->u.crypt.key, + param->u.crypt.key_len, param->u.crypt.seq, + (*crypt)->priv) < 0) { + printk(KERN_DEBUG "%s: key setting failed\n", + local->dev->name); + param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + + if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) { + if (!sta_ptr) + local->tx_keyidx = param->u.crypt.idx; + else if (param->u.crypt.idx) { + printk(KERN_DEBUG "%s: TX key idx setting failed\n", + local->dev->name); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + } + + done: + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (ret == 0 && + (hostap_set_encryption(local) || + (local->iw_mode != IW_MODE_INFRA && + local->func->reset_port(local->dev)))) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED; + return -EINVAL; + } + + return ret; +} + + +static int prism2_ioctl_get_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + struct prism2_crypt_data **crypt; + void *sta_ptr; + int max_key_len; + + param->u.crypt.err = 0; + + max_key_len = param_len - + (int) ((char *) param->u.crypt.key - (char *) param); + if (max_key_len < 0) + return -EINVAL; + + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + sta_ptr = NULL; + if (param->u.crypt.idx >= WEP_KEYS) + param->u.crypt.idx = local->tx_keyidx; + crypt = &local->crypt[param->u.crypt.idx]; + } else { + param->u.crypt.idx = 0; + sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0, + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (*crypt == NULL || (*crypt)->ops == NULL) { + memcpy(param->u.crypt.alg, "none", 5); + param->u.crypt.key_len = 0; + param->u.crypt.idx = 0xff; + } else { + strncpy(param->u.crypt.alg, (*crypt)->ops->name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.key_len = 0; + + memset(param->u.crypt.seq, 0, 8); + if ((*crypt)->ops->get_key) { + param->u.crypt.key_len = + (*crypt)->ops->get_key(param->u.crypt.key, + max_key_len, + param->u.crypt.seq, + (*crypt)->priv); + } + } + + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + return 0; +} + + +static int prism2_ioctl_get_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, res; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0) + return -EINVAL; + + res = local->func->get_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len, 0); + if (res >= 0) { + param->u.rid.len = res; + return 0; + } + + return res; +} + + +static int prism2_ioctl_set_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0 || max_len < param->u.rid.len) + return -EINVAL; + + return local->func->set_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len); +} + + +static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + printk(KERN_DEBUG "%ssta: associated as client with AP " MACSTR "\n", + local->dev->name, MAC2STR(param->sta_addr)); + memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN); + return 0; +} + + +#if WIRELESS_EXT > 17 +static int prism2_ioctl_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + return prism2_set_genericelement(dev, extra, data->length); +} + + +static int prism2_ioctl_giwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int len = local->generic_elem_len - 2; + + if (len <= 0 || local->generic_elem == NULL) { + data->length = 0; + return 0; + } + + if (data->length < len) + return -E2BIG; + + data->length = len; + memcpy(extra, local->generic_elem + 2, len); + + return 0; +} +#endif /* WIRELESS_EXT > 17 */ + + +static int prism2_ioctl_set_generic_element(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, len; + + len = param->u.generic_elem.len; + max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (max_len < 0 || max_len < len) + return -EINVAL; + + return prism2_set_genericelement(local->dev, + param->u.generic_elem.data, len); +} + + +#if WIRELESS_EXT > 17 +static int prism2_ioctl_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + struct iw_mlme *mlme = (struct iw_mlme *) extra; + u16 reason; + + reason = cpu_to_le16(mlme->reason_code); + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + return prism2_sta_send_mgmt(local, mlme->addr.sa_data, + WLAN_FC_STYPE_DEAUTH, + (u8 *) &reason, 2); + case IW_MLME_DISASSOC: + return prism2_sta_send_mgmt(local, mlme->addr.sa_data, + WLAN_FC_STYPE_DISASSOC, + (u8 *) &reason, 2); + default: + return -EOPNOTSUPP; + } +} +#endif /* WIRELESS_EXT > 17 */ + + +static int prism2_ioctl_mlme(local_info_t *local, + struct prism2_hostapd_param *param) +{ + u16 reason; + + reason = cpu_to_le16(param->u.mlme.reason_code); + switch (param->u.mlme.cmd) { + case MLME_STA_DEAUTH: + return prism2_sta_send_mgmt(local, param->sta_addr, + WLAN_FC_STYPE_DEAUTH, + (u8 *) &reason, 2); + case MLME_STA_DISASSOC: + return prism2_sta_send_mgmt(local, param->sta_addr, + WLAN_FC_STYPE_DISASSOC, + (u8 *) &reason, 2); + default: + return -EOPNOTSUPP; + } +} + + +static int prism2_ioctl_scan_req(local_info_t *local, + struct prism2_hostapd_param *param) +{ +#ifndef PRISM2_NO_STATION_MODES + if ((local->iw_mode != IW_MODE_INFRA && + local->iw_mode != IW_MODE_ADHOC) || + (local->sta_fw_ver < PRISM2_FW_VER(1,3,1))) + return -EOPNOTSUPP; + + if (!local->dev_enabled) + return -ENETDOWN; + + return prism2_request_hostscan(local->dev, param->u.scan_req.ssid, + param->u.scan_req.ssid_len); +#else /* PRISM2_NO_STATION_MODES */ + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p) +{ + struct prism2_hostapd_param *param; + int ret = 0; + int ap_ioctl = 0; + + if (p->length < sizeof(struct prism2_hostapd_param) || + p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) + return -EINVAL; + + param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + switch (param->cmd) { + case PRISM2_SET_ENCRYPTION: + ret = prism2_ioctl_set_encryption(local, param, p->length); + break; + case PRISM2_GET_ENCRYPTION: + ret = prism2_ioctl_get_encryption(local, param, p->length); + break; + case PRISM2_HOSTAPD_GET_RID: + ret = prism2_ioctl_get_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_RID: + ret = prism2_ioctl_set_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR: + ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT: + ret = prism2_ioctl_set_generic_element(local, param, + p->length); + break; + case PRISM2_HOSTAPD_MLME: + ret = prism2_ioctl_mlme(local, param); + break; + case PRISM2_HOSTAPD_SCAN_REQ: + ret = prism2_ioctl_scan_req(local, param); + break; + default: + ret = prism2_hostapd(local->ap, param); + ap_ioctl = 1; + break; + } + + if (ret == 1 || !ap_ioctl) { + if (copy_to_user(p->pointer, param, p->length)) { + ret = -EFAULT; + goto out; + } else if (ap_ioctl) + ret = 0; + } + + out: + if (param != NULL) + kfree(param); + + return ret; +} + + +static int prism2_ioctl_ethtool(local_info_t *local, void *useraddr) +{ + u32 ethcmd; + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + strncpy(info.driver, "hostap", sizeof(info.driver) - 1); + strncpy(info.version, PRISM2_VERSION, + sizeof(info.version) - 1); + snprintf(info.fw_version, sizeof(info.fw_version) - 1, + "%d.%d.%d", (local->sta_fw_ver >> 16) & 0xff, + (local->sta_fw_ver >> 8) & 0xff, + local->sta_fw_ver & 0xff); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + return -EOPNOTSUPP; +} + + +/* Structures to export the Wireless Handlers */ + +static const iw_handler prism2_handler[] = +{ + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) prism2_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */ + (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */ + (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */ + (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */ + (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ + (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ +#if WIRELESS_EXT > 15 + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else /* WIRELESS_EXT > 15 */ + (iw_handler) prism2_ioctl_siwspy, /* SIOCSIWSPY */ + (iw_handler) prism2_ioctl_giwspy, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 15 */ + (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */ + (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */ +#if WIRELESS_EXT > 17 + (iw_handler) prism2_ioctl_siwmlme, /* SIOCSIWMLME */ +#else /* WIRELESS_EXT > 17 */ + (iw_handler) NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 17 */ + (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */ + (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */ + (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */ + (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */ + (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */ + (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */ + (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */ + (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */ + (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */ + (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */ + (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */ + (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */ + (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */ + (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */ + (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */ + (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */ + (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */ + (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */ + (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */ +#if WIRELESS_EXT > 17 + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_siwgenie, /* SIOCSIWGENIE */ + (iw_handler) prism2_ioctl_giwgenie, /* SIOCGIWGENIT */ + (iw_handler) prism2_ioctl_siwauth, /* SIOCSIWAUTH */ + (iw_handler) prism2_ioctl_giwauth, /* SIOCGIWAUTH */ + (iw_handler) prism2_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) prism2_ioctl_giwencodeext, /* SIOCGIWENCODEEXT */ + (iw_handler) prism2_ioctl_siwscanext, /* SIOCSIWSCANEXT */ + (iw_handler) NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 17 */ +}; + +static const iw_handler prism2_private_handler[] = +{ /* SIOCIWFIRSTPRIV + */ + (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */ + (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */ + (iw_handler) prism2_ioctl_priv_writemif, /* 2 */ + (iw_handler) prism2_ioctl_priv_readmif, /* 3 */ +}; + +static const struct iw_handler_def hostap_iw_handler_def = +{ + .num_standard = sizeof(prism2_handler) / sizeof(iw_handler), + .num_private = sizeof(prism2_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(prism2_priv) / sizeof(struct iw_priv_args), + .standard = (iw_handler *) prism2_handler, + .private = (iw_handler *) prism2_private_handler, + .private_args = (struct iw_priv_args *) prism2_priv, +#if WIRELESS_EXT == 16 + .spy_offset = offsetof(struct hostap_interface, spy_data), +#endif /* WIRELESS_EXT == 16 */ +#if WIRELESS_EXT > 16 + .get_wireless_stats = hostap_get_wireless_stats, +#endif /* WIRELESS_EXT > 16 */ +}; + + +int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct iwreq *wrq = (struct iwreq *) ifr; + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + int ret = 0; + + switch (cmd) { + /* Private ioctls (iwpriv) that have not yet been converted + * into new wireless extensions API */ + + case PRISM2_IOCTL_INQUIRE: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_MONITOR: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_RESET: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name); + break; + +#ifdef PRISM2_USE_WE_TYPE_ADDR + case PRISM2_IOCTL_WDS_ADD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1); + break; + + case PRISM2_IOCTL_WDS_DEL: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0); + break; +#else /* PRISM2_USE_WE_TYPE_ADDR */ + case PRISM2_IOCTL_WDS_ADD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = prism2_ioctl_priv_wds(dev, 1, addrbuf); + } + break; + + case PRISM2_IOCTL_WDS_DEL: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = prism2_ioctl_priv_wds(dev, 0, addrbuf); + } + break; +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + + case PRISM2_IOCTL_SET_RID_WORD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_set_rid_word(dev, + (int *) wrq->u.name); + break; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + case PRISM2_IOCTL_MACCMD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name); + break; + +#ifdef PRISM2_USE_WE_TYPE_ADDR + case PRISM2_IOCTL_ADDMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_add_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_DELMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_del_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_KICKMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_kick_mac(local->ap, local->dev, + wrq->u.ap_addr.sa_data); + break; +#else /* PRISM2_USE_WE_TYPE_ADDR */ + case PRISM2_IOCTL_ADDMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = ap_mac_ioctl(local, addrbuf, AP_CTRL_MAC_ADD); + } + break; + + case PRISM2_IOCTL_DELMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = ap_mac_ioctl(local, addrbuf, AP_CTRL_MAC_DEL); + } + break; + + case PRISM2_IOCTL_KICKMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = ap_mac_ioctl(local, addrbuf, AP_CTRL_MAC_KICK); + } + break; +#endif /* PRISM2_USE_WE_TYPE_ADDR */ +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + + /* Private ioctls that are not used with iwpriv; + * in SIOCDEVPRIVATE range */ + +#ifdef PRISM2_DOWNLOAD_SUPPORT + case PRISM2_IOCTL_DOWNLOAD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_download(local, &wrq->u.data); + break; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + case PRISM2_IOCTL_HOSTAPD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data); + break; + + case SIOCETHTOOL: + ret = prism2_ioctl_ethtool(local, (void *) ifr->ifr_data); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_pci.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_pci.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_pci.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_pci.c 2005-02-05 02:20:09.000000000 -0500 @@ -0,0 +1,429 @@ +#define PRISM2_PCI + +/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on + * driver patches from Reyk Floeter and + * Andy Warner */ + +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include "hostap_wext.h" + +#include +#include +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *dev_info = "hostap_pci"; + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " + "PCI cards."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); +MODULE_LICENSE("GPL"); + + +/* FIX: do we need mb/wmb/rmb with memory operations? */ + + +static struct pci_device_id prism2_pci_id_table[] __devinitdata = { + /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */ + { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, + /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ + { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, + /* Samsung MagicLAN SWL-2210P */ + { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + writeb(v, (void *) dev->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + u8 v; + + spin_lock_irqsave(&local->lock, flags); + v = readb((void *) dev->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + writew(v, (void *) dev->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + u16 v; + + spin_lock_irqsave(&local->lock, flags); + v = readw((void *) dev->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v))) +#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a))) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) writeb((v), (void *) dev->mem_start + (a)) +#define HFA384X_INB(a) (u8) readb((void *) dev->mem_start + (a)) +#define HFA384X_OUTW(v,a) writew((v), (void *) dev->mem_start + (a)) +#define HFA384X_INW(a) (u16) readw((void *) dev->mem_start + (a)) +#define HFA384X_OUTW_DATA(v,a) \ + writew(cpu_to_le16(v), (void *) dev->mem_start + (a)) +#define HFA384X_INW_DATA(a) (u16) \ + le16_to_cpu(readw((void *) dev->mem_start + (a))) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + for ( ; len > 1; len -= 2) + *pos++ = HFA384X_INW_DATA(d_off); + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + for ( ; len > 1; len -= 2) + HFA384X_OUTW_DATA(*pos++, d_off); + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + +static void prism2_pci_cor_sreset(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 reg; + + reg = HFA384X_INB(HFA384X_PCICOR_OFF); + printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg); + + /* linux-wlan-ng uses extremely long hold and settle times for + * COR sreset. A comment in the driver code mentions that the long + * delays appear to be necessary. However, at least IBM 22P6901 seems + * to work fine with shorter delays. + * + * Longer delays can be configured by uncommenting following line: */ +/* #define PRISM2_PCI_USE_LONG_DELAYS */ + +#ifdef PRISM2_PCI_USE_LONG_DELAYS + int i; + + HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); + mdelay(250); + + HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); + mdelay(500); + + /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ + i = 2000000 / 10; + while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) + udelay(10); + +#else /* PRISM2_PCI_USE_LONG_DELAYS */ + + HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); + mdelay(2); + HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); + mdelay(2); + +#endif /* PRISM2_PCI_USE_LONG_DELAYS */ + + if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { + printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); + } +} + + +static void prism2_pci_genesis_reset(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + + HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF); + mdelay(10); + HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF); + mdelay(10); + HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF); + mdelay(10); +} + + +static struct prism2_helper_functions prism2_pci_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_pci_cor_sreset, + .dev_open = NULL, + .dev_close = NULL, + .genesis_reset = prism2_pci_genesis_reset, + .hw_type = HOSTAP_HW_PCI, +}; + + +static int prism2_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long phymem; + unsigned long mem = 0; + local_info_t *local = NULL; + struct net_device *dev = NULL; + static int cards_found /* = 0 */; + int irq_registered = 0; + struct hostap_interface *iface; + + if (pci_enable_device(pdev)) + return -EIO; + + phymem = pci_resource_start(pdev, 0); + + if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { + printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); + goto err_out_disable; + } + + mem = (unsigned long) ioremap(phymem, pci_resource_len(pdev, 0)); + if (!mem) { + printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; + goto fail; + } + +#ifdef PRISM2_BUS_MASTER + pci_set_master(pdev); +#endif /* PRISM2_BUS_MASTER */ + + dev = prism2_init_local_data(&prism2_pci_funcs, cards_found); + if (dev == NULL) + goto fail; + iface = dev->priv; + local = iface->local; + cards_found++; + + dev->irq = pdev->irq; + dev->mem_start = mem; + dev->mem_end = mem + pci_resource_len(pdev, 0); + + prism2_pci_cor_sreset(local); + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (!local->pri_only && prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " + "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); + + return hostap_hw_ready(dev); + + fail: + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (mem) + iounmap((void *) mem); + + release_mem_region(phymem, pci_resource_len(pdev, 0)); + + err_out_disable: + pci_disable_device(pdev); + prism2_free_local_data(dev); + + return -ENODEV; +} + + +static void prism2_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct hostap_interface *iface = dev->priv; + unsigned long mem_start; + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_pci_cor_sreset(iface->local); + hfa384x_disable_interrupts(dev); + + if (dev->irq) + free_irq(dev->irq, dev); + + mem_start = dev->mem_start; + prism2_free_local_data(dev); + + iounmap((void *) mem_start); + + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); +} + + +#ifdef CONFIG_PM +static int prism2_pci_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + prism2_suspend(dev); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) + pci_save_state(pdev, ((struct hostap_interface *) dev->priv)-> + local->pci_save_state); +#else + pci_save_state(pdev); +#endif + pci_disable_device(pdev); + pci_set_power_state(pdev, 3); + + return 0; +} + +static int prism2_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + pci_enable_device(pdev); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) + pci_restore_state(pdev, ((struct hostap_interface *) dev->priv)-> + local->pci_save_state); +#else + pci_restore_state(pdev); +#endif + prism2_hw_config(dev, 0); + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#endif /* CONFIG_PM */ + + +MODULE_DEVICE_TABLE(pci, prism2_pci_id_table); + +static struct pci_driver prism2_pci_drv_id = { + .name = "prism2_pci", + .id_table = prism2_pci_id_table, + .probe = prism2_pci_probe, + .remove = prism2_pci_remove, +#ifdef CONFIG_PM + .suspend = prism2_pci_suspend, + .resume = prism2_pci_resume, +#endif /* CONFIG_PM */ + /* Linux 2.4.6 added save_state and enable_wake that are not used here + */ +}; + + +static int __init init_prism2_pci(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) + if (pci_register_driver(&prism2_pci_drv_id) <= 0) { + printk("hostap_pci: No devices found, driver not " + "installed.\n"); + pci_unregister_driver(&prism2_pci_drv_id); + return -ENODEV; + } + + return 0; +#else + return pci_register_driver(&prism2_pci_drv_id); +#endif +} + + +static void __exit exit_prism2_pci(void) +{ + pci_unregister_driver(&prism2_pci_drv_id); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_pci); +module_exit(exit_prism2_pci); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_plx.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_plx.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_plx.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_plx.c 2004-11-30 00:40:09.000000000 -0500 @@ -0,0 +1,605 @@ +#define PRISM2_PLX + +/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is + * based on: + * - Host AP driver patch from james@madingley.org + * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include "hostap_wext.h" + +#include +#include +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *dev_info = "hostap_plx"; + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PLX)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)"); +MODULE_LICENSE("GPL"); + + +static int ignore_cis = 0; +MODULE_PARM(ignore_cis, "i"); +MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS"); + + +#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ +#define COR_SRESET 0x80 +#define COR_LEVLREQ 0x40 +#define COR_ENABLE_FUNC 0x01 +/* PCI Configuration Registers */ +#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */ +/* Local Configuration Registers */ +#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */ +#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */ +#define PLX_CNTRL 0x50 +#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28) + + +#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID } + +static struct pci_device_id prism2_plx_id_table[] __devinitdata = { + PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), + PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), + PLXDEV(0x126c, 0x8030, "Nortel emobility"), + PLXDEV(0x1385, 0x4100, "Netgear MA301"), + PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), + PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), + PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), + PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), + PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), + PLXDEV(0x16ab, 0x1103, "Longshine 8031"), + PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"), + PLXDEV(0xec80, 0xec00, "Belkin F5D6000"), + { 0 } +}; + + +/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid + * is not listed here, you will need to add it here to get the driver + * initialized. */ +static struct prism2_plx_manfid { + u16 manfid1, manfid2; +} prism2_plx_known_manfids[] = { + { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */, + { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */, + { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */, + { 0x0126, 0x8000 } /* Proxim RangeLAN */, + { 0x0138, 0x0002 } /* Compaq WL100 */, + { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */, + { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */, + { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */, + { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */, + { 0x028a, 0x0002 } /* D-Link DRC-650 */, + { 0x0250, 0x0002 } /* Samsung SWL2000-N */, + { 0xc250, 0x0002 } /* EMTAC A2424i */, + { 0xd601, 0x0002 } /* Z-Com XI300 */, + { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, + { 0, 0} +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + outb(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + u8 v; + + spin_lock_irqsave(&local->lock, flags); + v = inb(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + outw(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + u16 v; + + spin_lock_irqsave(&local->lock, flags); + v = inw(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outsw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); + outsw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline void hfa384x_insw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); + insw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) +#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) +#define HFA384X_INB(a) inb(dev->base_addr + (a)) +#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) +#define HFA384X_INW(a) inw(dev->base_addr + (a)) +#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) +#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_INSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_OUTSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + + +static void prism2_plx_cor_sreset(local_info_t *local) +{ + unsigned char corsave; + + printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n", + dev_info); + + /* Set sreset bit of COR and clear it after hold time */ + + if (local->attr_mem == NULL) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(local->cor_offset); + outb(corsave | COR_SRESET, local->cor_offset); + mdelay(2); + outb(corsave & ~COR_SRESET, local->cor_offset); + mdelay(2); + } else { + /* PLX9052 */ + corsave = readb(local->attr_mem + local->cor_offset); + writeb(corsave | COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(2); + writeb(corsave & ~COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(2); + } +} + + +static void prism2_plx_genesis_reset(local_info_t *local, int hcr) +{ + unsigned char corsave; + + if (local->attr_mem == NULL) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(local->cor_offset); + outb(corsave | COR_SRESET, local->cor_offset); + mdelay(10); + outb(hcr, local->cor_offset + 2); + mdelay(10); + outb(corsave & ~COR_SRESET, local->cor_offset); + mdelay(10); + } else { + /* PLX9052 */ + corsave = readb(local->attr_mem + local->cor_offset); + writeb(corsave | COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(10); + writeb(hcr, local->attr_mem + local->cor_offset + 2); + mdelay(10); + writeb(corsave & ~COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(10); + } +} + + +static struct prism2_helper_functions prism2_plx_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_plx_cor_sreset, + .dev_open = NULL, + .dev_close = NULL, + .genesis_reset = prism2_plx_genesis_reset, + .hw_type = HOSTAP_HW_PLX, +}; + + +static int prism2_plx_check_cis(void *attr_mem, int attr_len, + unsigned int *cor_offset, + unsigned int *cor_index) +{ +#define CISTPL_CONFIG 0x1A +#define CISTPL_MANFID 0x20 +#define CISTPL_END 0xFF +#define CIS_MAX_LEN 256 + u8 cis[CIS_MAX_LEN]; + int i, pos; + unsigned int rmsz, rasz, manfid1, manfid2; + struct prism2_plx_manfid *manfid; + + /* read CIS; it is in even offsets in the beginning of attr_mem */ + for (i = 0; i < CIS_MAX_LEN; i++) + cis[i] = readb(attr_mem + 2 * i); + printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n", + dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]); + + /* set reasonable defaults for Prism2 cards just in case CIS parsing + * fails */ + *cor_offset = 0x3e0; + *cor_index = 0x01; + manfid1 = manfid2 = 0; + + pos = 0; + while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) { + if (pos + cis[pos + 1] >= CIS_MAX_LEN) + goto cis_error; + + switch (cis[pos]) { + case CISTPL_CONFIG: + if (cis[pos + 1] < 1) + goto cis_error; + rmsz = (cis[pos + 2] & 0x3c) >> 2; + rasz = cis[pos + 2] & 0x03; + if (4 + rasz + rmsz > cis[pos + 1]) + goto cis_error; + *cor_index = cis[pos + 3] & 0x3F; + *cor_offset = 0; + for (i = 0; i <= rasz; i++) + *cor_offset += cis[pos + 4 + i] << (8 * i); + printk(KERN_DEBUG "%s: cor_index=0x%x " + "cor_offset=0x%x\n", dev_info, + *cor_index, *cor_offset); + if (*cor_offset > attr_len) { + printk(KERN_ERR "%s: COR offset not within " + "attr_mem\n", dev_info); + return -1; + } + break; + + case CISTPL_MANFID: + if (cis[pos + 1] < 4) + goto cis_error; + manfid1 = cis[pos + 2] + (cis[pos + 3] << 8); + manfid2 = cis[pos + 4] + (cis[pos + 5] << 8); + printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n", + dev_info, manfid1, manfid2); + break; + } + + pos += cis[pos + 1] + 2; + } + + if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END) + goto cis_error; + + for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++) + if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) + return 0; + + printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is" + " not supported card\n", dev_info, manfid1, manfid2); + goto fail; + + cis_error: + printk(KERN_WARNING "%s: invalid CIS data\n", dev_info); + + fail: + if (ignore_cis) { + printk(KERN_INFO "%s: ignore_cis parameter set - ignoring " + "errors during CIS verification\n", dev_info); + return 0; + } + return -1; +} + + +static int prism2_plx_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned int pccard_ioaddr, plx_ioaddr; + unsigned long pccard_attr_mem; + unsigned int pccard_attr_len; + void *attr_mem = NULL; + unsigned int cor_offset, cor_index; + u32 reg; + local_info_t *local = NULL; + struct net_device *dev = NULL; + struct hostap_interface *iface; + static int cards_found /* = 0 */; + int irq_registered = 0; + int tmd7160; + + if (pci_enable_device(pdev)) + return -EIO; + + /* National Datacomm NCP130 based on TMD7160, not PLX9052. */ + tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131); + + plx_ioaddr = pci_resource_start(pdev, 1); + pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3); + + if (tmd7160) { + /* TMD7160 */ + attr_mem = NULL; /* no access to PC Card attribute memory */ + + printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, " + "irq=%d, pccard_io=0x%x\n", + plx_ioaddr, pdev->irq, pccard_ioaddr); + + cor_offset = plx_ioaddr; + cor_index = 0x04; + + outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr); + mdelay(1); + reg = inb(plx_ioaddr); + if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) { + printk(KERN_ERR "%s: Error setting COR (expected=" + "0x%02x, was=0x%02x)\n", dev_info, + cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg); + goto fail; + } + } else { + /* PLX9052 */ + pccard_attr_mem = pci_resource_start(pdev, 2); + pccard_attr_len = pci_resource_len(pdev, 2); + if (pccard_attr_len < PLX_MIN_ATTR_LEN) + goto fail; + + + attr_mem = ioremap(pccard_attr_mem, pccard_attr_len); + if (attr_mem == NULL) { + printk(KERN_ERR "%s: cannot remap attr_mem\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: " + "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n", + pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr); + + if (prism2_plx_check_cis(attr_mem, pccard_attr_len, + &cor_offset, &cor_index)) { + printk(KERN_INFO "Unknown PC Card CIS - not a " + "Prism2/2.5 card?\n"); + goto fail; + } + + printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 " + "adapter\n"); + + /* Write COR to enable PC Card */ + writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, + attr_mem + cor_offset); + + /* Enable PCI interrupts if they are not already enabled */ + reg = inl(plx_ioaddr + PLX_INTCSR); + printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg); + if (!(reg & PLX_INTCSR_PCI_INTEN)) { + outl(reg | PLX_INTCSR_PCI_INTEN, + plx_ioaddr + PLX_INTCSR); + if (!(inl(plx_ioaddr + PLX_INTCSR) & + PLX_INTCSR_PCI_INTEN)) { + printk(KERN_WARNING "%s: Could not enable " + "Local Interrupts\n", dev_info); + goto fail; + } + } + + reg = inl(plx_ioaddr + PLX_CNTRL); + printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM " + "present=%d)\n", + reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0); + /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is + * not present; but are there really such cards in use(?) */ + } + + dev = prism2_init_local_data(&prism2_plx_funcs, cards_found); + if (dev == NULL) + goto fail; + iface = dev->priv; + local = iface->local; + cards_found++; + + dev->irq = pdev->irq; + dev->base_addr = pccard_ioaddr; + local->attr_mem = attr_mem; + local->cor_offset = cor_offset; + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + return hostap_hw_ready(dev); + + fail: + prism2_free_local_data(dev); + + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (attr_mem) + iounmap(attr_mem); + + pci_disable_device(pdev); + + return -ENODEV; +} + + +static void prism2_plx_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct hostap_interface *iface = dev->priv; + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_plx_cor_sreset(iface->local); + hfa384x_disable_interrupts(dev); + + if (iface->local->attr_mem) + iounmap(iface->local->attr_mem); + if (dev->irq) + free_irq(dev->irq, dev); + + prism2_free_local_data(dev); + pci_disable_device(pdev); +} + + +MODULE_DEVICE_TABLE(pci, prism2_plx_id_table); + +static struct pci_driver prism2_plx_drv_id = { + .name = "prism2_plx", + .id_table = prism2_plx_id_table, + .probe = prism2_plx_probe, + .remove = prism2_plx_remove, + .suspend = NULL, + .resume = NULL, + .enable_wake = NULL +}; + + +static int __init init_prism2_plx(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) + if (pci_register_driver(&prism2_plx_drv_id) <= 0) { + printk("hostap_plx: No devices found, driver not " + "installed.\n"); + pci_unregister_driver(&prism2_plx_drv_id); + return -ENODEV; + } + + return 0; +#else + return pci_register_driver(&prism2_plx_drv_id); +#endif +} + + +static void __exit exit_prism2_plx(void) +{ + pci_unregister_driver(&prism2_plx_drv_id); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_plx); +module_exit(exit_prism2_plx); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_proc.c linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_proc.c --- linux-2.6.9-intc1/drivers/net/wireless/hostap_proc.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_proc.c 2005-01-02 21:19:32.000000000 -0500 @@ -0,0 +1,466 @@ +/* /proc routines for Host AP driver */ + +#define PROC_LIMIT (PAGE_SIZE - 80) + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int i; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "next_txfid=%d next_alloc=%d\n", + local->next_txfid, local->next_alloc); + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + p += sprintf(p, "FID: tx=%04X intransmit=%04X\n", + local->txfid[i], local->intransmitfid[i]); + p += sprintf(p, "FW TX rate control: %d\n", local->fw_tx_rate_control); + p += sprintf(p, "beacon_int=%d\n", local->beacon_int); + p += sprintf(p, "dtim_period=%d\n", local->dtim_period); + p += sprintf(p, "wds_max_connections=%d\n", + local->wds_max_connections); + p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled); + p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck); + for (i = 0; i < WEP_KEYS; i++) { + if (local->crypt[i] && local->crypt[i]->ops) { + p += sprintf(p, "crypt[%d]=%s\n", + i, local->crypt[i]->ops->name); + } + } + p += sprintf(p, "pri_only=%d\n", local->pri_only); + p += sprintf(p, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI); + p += sprintf(p, "sram_type=%d\n", local->sram_type); + p += sprintf(p, "no_pri=%d\n", local->no_pri); + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static int prism2_stats_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct comm_tallies_sums *sums = (struct comm_tallies_sums *) + &local->comm_tallies; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "TxUnicastFrames=%u\n", sums->tx_unicast_frames); + p += sprintf(p, "TxMulticastframes=%u\n", sums->tx_multicast_frames); + p += sprintf(p, "TxFragments=%u\n", sums->tx_fragments); + p += sprintf(p, "TxUnicastOctets=%u\n", sums->tx_unicast_octets); + p += sprintf(p, "TxMulticastOctets=%u\n", sums->tx_multicast_octets); + p += sprintf(p, "TxDeferredTransmissions=%u\n", + sums->tx_deferred_transmissions); + p += sprintf(p, "TxSingleRetryFrames=%u\n", + sums->tx_single_retry_frames); + p += sprintf(p, "TxMultipleRetryFrames=%u\n", + sums->tx_multiple_retry_frames); + p += sprintf(p, "TxRetryLimitExceeded=%u\n", + sums->tx_retry_limit_exceeded); + p += sprintf(p, "TxDiscards=%u\n", sums->tx_discards); + p += sprintf(p, "RxUnicastFrames=%u\n", sums->rx_unicast_frames); + p += sprintf(p, "RxMulticastFrames=%u\n", sums->rx_multicast_frames); + p += sprintf(p, "RxFragments=%u\n", sums->rx_fragments); + p += sprintf(p, "RxUnicastOctets=%u\n", sums->rx_unicast_octets); + p += sprintf(p, "RxMulticastOctets=%u\n", sums->rx_multicast_octets); + p += sprintf(p, "RxFCSErrors=%u\n", sums->rx_fcs_errors); + p += sprintf(p, "RxDiscardsNoBuffer=%u\n", + sums->rx_discards_no_buffer); + p += sprintf(p, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa); + p += sprintf(p, "RxDiscardsWEPUndecryptable=%u\n", + sums->rx_discards_wep_undecryptable); + p += sprintf(p, "RxMessageInMsgFragments=%u\n", + sums->rx_message_in_msg_fragments); + p += sprintf(p, "RxMessageInBadMsgFragments=%u\n", + sums->rx_message_in_bad_msg_fragments); + /* FIX: this may grow too long for one page(?) */ + + return (p - page); +} + + +static int prism2_wds_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct list_head *ptr; + struct hostap_interface *iface; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + p += sprintf(p, "%s\t" MACSTR "\n", + iface->dev->name, + MAC2STR(iface->u.wds.remote_addr)); + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "%s: wds proc did not fit\n", + local->dev->name); + break; + } + } + read_unlock_bh(&local->iface_lock); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_bss_list_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct list_head *ptr; + struct hostap_bss_info *bss; + int i; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + p += sprintf(p, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t" + "SSID(hex)\tWPA IE\n"); + spin_lock_bh(&local->lock); + list_for_each(ptr, &local->bss_list) { + bss = list_entry(ptr, struct hostap_bss_info, list); + p += sprintf(p, MACSTR "\t%lu\t%u\t0x%x\t", + MAC2STR(bss->bssid), bss->last_update, + bss->count, bss->capab_info); + for (i = 0; i < bss->ssid_len; i++) { + p += sprintf(p, "%c", + bss->ssid[i] >= 32 && bss->ssid[i] < 127 ? + bss->ssid[i] : '_'); + } + p += sprintf(p, "\t"); + for (i = 0; i < bss->ssid_len; i++) { + p += sprintf(p, "%02x", bss->ssid[i]); + } + p += sprintf(p, "\t"); + for (i = 0; i < bss->wpa_ie_len; i++) { + p += sprintf(p, "%02x", bss->wpa_ie[i]); + } + p += sprintf(p, "\n"); + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "%s: BSS proc did not fit\n", + local->dev->name); + break; + } + } + spin_unlock_bh(&local->lock); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_crypt_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int i; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + p += sprintf(p, "tx_keyidx=%d\n", local->tx_keyidx); + for (i = 0; i < WEP_KEYS; i++) { + if (local->crypt[i] && local->crypt[i]->ops && + local->crypt[i]->ops->print_stats) { + p = local->crypt[i]->ops->print_stats( + p, local->crypt[i]->priv); + } + } + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_pda_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (local->pda == NULL || off >= PRISM2_PDA_SIZE) { + *eof = 1; + return 0; + } + + if (off + count > PRISM2_PDA_SIZE) + count = PRISM2_PDA_SIZE - off; + + memcpy(page, local->pda + off, count); + return count; +} + + +static int prism2_aux_dump_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (local->func->read_aux == NULL) { + *eof = 1; + return 0; + } + + if (local->func->read_aux(local->dev, off, count, page)) { + *eof = 1; + return 0; + } + *start = page; + + return count; +} + + +#ifdef PRISM2_IO_DEBUG +static int prism2_io_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + int head = local->io_debug_head; + int start_bytes, left, copy, copied; + + if (off + count > PRISM2_IO_DEBUG_SIZE * 4) { + *eof = 1; + if (off >= PRISM2_IO_DEBUG_SIZE * 4) + return 0; + count = PRISM2_IO_DEBUG_SIZE * 4 - off; + } + + copied = 0; + start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4; + left = count; + + if (off < start_bytes) { + copy = start_bytes - off; + if (copy > count) + copy = count; + memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy); + left -= copy; + if (left > 0) + memcpy(&page[copy], local->io_debug, left); + } else { + memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes), + left); + } + + *start = page; + + return count; +} +#endif /* PRISM2_IO_DEBUG */ + + +#ifndef PRISM2_NO_STATION_MODES +static int prism2_scan_results_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int entries, entry, i, len, total = 0, hostscan; + struct hfa384x_scan_result *scanres; + struct hfa384x_hostscan_result *hscanres; + u8 *pos; + + p += sprintf(p, "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates " + "SSID\n"); + + spin_lock_bh(&local->lock); + hostscan = local->last_scan_type == PRISM2_HOSTSCAN; + entries = hostscan ? local->last_hostscan_results_count : + local->last_scan_results_count; + for (entry = 0; entry < entries; entry++) { + hscanres = &local->last_hostscan_results[entry]; + scanres = &local->last_scan_results[entry]; + + if (total + (p - page) <= off) { + total += p - page; + p = page; + } + if (total + (p - page) > off + count) + break; + if ((p - page) > (PAGE_SIZE - 200)) + break; + + if (hostscan) { + p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR " %d ", + le16_to_cpu(hscanres->chid), + (s16) le16_to_cpu(hscanres->anl), + (s16) le16_to_cpu(hscanres->sl), + le16_to_cpu(hscanres->beacon_interval), + le16_to_cpu(hscanres->capability), + le16_to_cpu(hscanres->rate), + MAC2STR(hscanres->bssid), + le16_to_cpu(hscanres->atim)); + } else { + p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR + " N/A ", + le16_to_cpu(scanres->chid), + (s16) le16_to_cpu(scanres->anl), + (s16) le16_to_cpu(scanres->sl), + le16_to_cpu(scanres->beacon_interval), + le16_to_cpu(scanres->capability), + le16_to_cpu(scanres->rate), + MAC2STR(scanres->bssid)); + } + + pos = hostscan ? hscanres->sup_rates : scanres->sup_rates; + for (i = 0; i < sizeof(hscanres->sup_rates); i++) { + if (pos[i] == 0) + break; + p += sprintf(p, "<%02x>", pos[i]); + } + p += sprintf(p, " "); + + pos = hostscan ? hscanres->ssid : scanres->ssid; + len = le16_to_cpu(hostscan ? hscanres->ssid_len : + scanres->ssid_len); + if (len > 32) + len = 32; + for (i = 0; i < len; i++) { + unsigned char c = pos[i]; + if (c >= 32 && c < 127) + p += sprintf(p, "%c", c); + else + p += sprintf(p, "<%02x>", c); + } + p += sprintf(p, "\n"); + } + spin_unlock_bh(&local->lock); + + total += (p - page); + if (total >= off + count) + *eof = 1; + + if (total < off) { + *eof = 1; + return 0; + } + + len = total - off; + if (len > (p - page)) + len = p - page; + *start = p - len; + if (len > count) + len = count; + + return len; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_init_proc(local_info_t *local) +{ + local->proc = NULL; + + if (hostap_proc == NULL) { + printk(KERN_WARNING "%s: hostap proc directory not created\n", + local->dev->name); + return; + } + + local->proc = proc_mkdir(local->ddev->name, hostap_proc); + if (local->proc == NULL) { + printk(KERN_INFO "/proc/net/hostap/%s creation failed\n", + local->ddev->name); + return; + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("debug", 0, local->proc, + prism2_debug_proc_read, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + create_proc_read_entry("stats", 0, local->proc, + prism2_stats_proc_read, local); + create_proc_read_entry("wds", 0, local->proc, + prism2_wds_proc_read, local); + create_proc_read_entry("pda", 0, local->proc, + prism2_pda_proc_read, local); + create_proc_read_entry("aux_dump", 0, local->proc, + prism2_aux_dump_proc_read, local); + create_proc_read_entry("bss_list", 0, local->proc, + prism2_bss_list_proc_read, local); + create_proc_read_entry("crypt", 0, local->proc, + prism2_crypt_proc_read, local); +#ifdef PRISM2_IO_DEBUG + create_proc_read_entry("io_debug", 0, local->proc, + prism2_io_debug_proc_read, local); +#endif /* PRISM2_IO_DEBUG */ +#ifndef PRISM2_NO_STATION_MODES + create_proc_read_entry("scan_results", 0, local->proc, + prism2_scan_results_proc_read, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +void hostap_remove_proc(local_info_t *local) +{ + if (local->proc != NULL) { +#ifndef PRISM2_NO_STATION_MODES + remove_proc_entry("scan_results", local->proc); +#endif /* PRISM2_NO_STATION_MODES */ +#ifdef PRISM2_IO_DEBUG + remove_proc_entry("io_debug", local->proc); +#endif /* PRISM2_IO_DEBUG */ + remove_proc_entry("pda", local->proc); + remove_proc_entry("aux_dump", local->proc); + remove_proc_entry("wds", local->proc); + remove_proc_entry("stats", local->proc); + remove_proc_entry("bss_list", local->proc); + remove_proc_entry("crypt", local->proc); +#ifndef PRISM2_NO_PROCFS_DEBUG + remove_proc_entry("debug", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + if (hostap_proc != NULL) + remove_proc_entry(local->proc->name, hostap_proc); + } +} + + +EXPORT_SYMBOL(hostap_init_proc); +EXPORT_SYMBOL(hostap_remove_proc); diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_wext.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_wext.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_wext.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_wext.h 2003-10-14 22:02:27.000000000 -0400 @@ -0,0 +1,55 @@ +#ifndef HOSTAP_WEXT_H +#define HOSTAP_WEXT_H + +/* Linux Wireless Extensions compatibility code */ + +#include +#include + +/* if wireless ext is not supported */ +#ifndef IW_MODE_ADHOC +#define IW_MODE_ADHOC 1 +#endif +#ifndef IW_MODE_INFRA +#define IW_MODE_INFRA 2 +#endif +#ifndef IW_MODE_MASTER +#define IW_MODE_MASTER 3 +#endif +#ifndef IW_MODE_REPEAT +#define IW_MODE_REPEAT 4 +#endif +#ifndef IW_MODE_SECOND +#define IW_MODE_SECOND 5 +#endif +#ifndef IW_MODE_MONITOR +#define IW_MODE_MONITOR 6 +#endif + + +/* Conversion to new driver API by Jean II */ + +#if WIRELESS_EXT >= 15 +/* Wireless ext ver15 allows verification of iwpriv support and sub-ioctls can + * be included even if not especially configured. */ +#ifndef PRISM2_USE_WE_SUB_IOCTLS +#define PRISM2_USE_WE_SUB_IOCTLS +#endif /* PRISM2_USE_WE_SUB_IOCTLS */ + +/* Assume that hosts using new wireless ext also have new wireless tools + * (ver >= 25) */ +#ifndef PRISM2_USE_WE_TYPE_ADDR +#define PRISM2_USE_WE_TYPE_ADDR +#endif /* PRISM2_USE_WE_TYPE_ADDR */ +#endif /* WIRELESS_EXT >= 15 */ + + +#ifdef PRISM2_USE_WE_TYPE_ADDR +/* Added in WIRELESS_EXT 15, but can be used with older versions assuming + * iwpriv ver >= 25 */ +#ifndef IW_PRIV_TYPE_ADDR +#define IW_PRIV_TYPE_ADDR 0x6000 +#endif /* IW_PRIV_TYPE_ADDR */ +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + +#endif /* HOSTAP_WEXT_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/hostap_wlan.h linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_wlan.h --- linux-2.6.9-intc1/drivers/net/wireless/hostap_wlan.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/hostap_wlan.h 2004-12-17 23:54:33.000000000 -0500 @@ -0,0 +1,1090 @@ +#ifndef HOSTAP_WLAN_H +#define HOSTAP_WLAN_H + +#include "hostap_config.h" +#ifdef __KERNEL__ +#include "hostap_compat.h" +#endif +#include "hostap_crypt.h" +#include "hostap_common.h" + +#define MAX_PARM_DEVICES 8 +#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES) +#define DEF_INTS -1, -1, -1, -1, -1, -1, -1 +#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx] + + +/* Specific skb->protocol value that indicates that the packet already contains + * txdesc header. + * FIX: This might need own value that would be allocated especially for Prism2 + * txdesc; ETH_P_CONTROL is commented as "Card specific control frames". + * However, these skb's should have only minimal path in the kernel side since + * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */ +#define ETH_P_HOSTAP ETH_P_CONTROL + +#ifndef ARPHRD_IEEE80211 +#define ARPHRD_IEEE80211 801 +#endif +#ifndef ARPHRD_IEEE80211_PRISM +#define ARPHRD_IEEE80211_PRISM 802 +#endif + +/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header + * (from linux-wlan-ng) */ +struct linux_wlan_ng_val { + u32 did; + u16 status, len; + u32 data; +} __attribute__ ((packed)); + +struct linux_wlan_ng_prism_hdr { + u32 msgcode, msglen; + char devname[16]; + struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal, + noise, rate, istx, frmlen; +} __attribute__ ((packed)); + +struct linux_wlan_ng_cap_hdr { + u32 version; + u32 length; + u64 mactime; + u64 hosttime; + u32 phytype; + u32 channel; + u32 datarate; + u32 antenna; + u32 priority; + u32 ssi_type; + s32 ssi_signal; + s32 ssi_noise; + u32 preamble; + u32 encoding; +} __attribute__ ((packed)); + +#define LWNG_CAP_DID_BASE (4 | (1 << 6)) /* section 4, group 1 */ +#define LWNG_CAPHDR_VERSION 0x80211001 + +struct hfa384x_rx_frame { + /* HFA384X RX frame descriptor */ + u16 status; /* HFA384X_RX_STATUS_ flags */ + u32 time; /* timestamp, 1 microsecond resolution */ + u8 silence; /* 27 .. 154; seems to be 0 */ + u8 signal; /* 27 .. 154 */ + u8 rate; /* 10, 20, 55, or 110 */ + u8 rxflow; + u32 reserved; + + /* 802.11 */ + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; + u16 data_len; + + /* 802.3 */ + u8 dst_addr[6]; + u8 src_addr[6]; + u16 len; + + /* followed by frame data; max 2304 bytes */ +} __attribute__ ((packed)); + + +struct hfa384x_tx_frame { + /* HFA384X TX frame descriptor */ + u16 status; /* HFA384X_TX_STATUS_ flags */ + u16 reserved1; + u16 reserved2; + u32 sw_support; + u8 retry_count; /* not yet implemented */ + u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */ + u16 tx_control; /* HFA384X_TX_CTRL_ flags */ + + /* 802.11 */ + u16 frame_control; /* parts not used */ + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; /* filled by firmware */ + u8 addr3[6]; + u16 seq_ctrl; /* filled by firmware */ + u8 addr4[6]; + u16 data_len; + + /* 802.3 */ + u8 dst_addr[6]; + u8 src_addr[6]; + u16 len; + + /* followed by frame data; max 2304 bytes */ +} __attribute__ ((packed)); + + +struct hfa384x_rid_hdr +{ + u16 len; + u16 rid; +} __attribute__ ((packed)); + + +/* Macro for converting signal levels (range 27 .. 154) to wireless ext + * dBm value with some accuracy */ +#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100 + +#define HFA384X_LEVEL_TO_dBm_sign(v) (v) * 100 / 255 - 100 + +struct hfa384x_scan_request { + u16 channel_list; + u16 txrate; /* HFA384X_RATES_* */ +} __attribute__ ((packed)); + +struct hfa384x_hostscan_request { + u16 channel_list; + u16 txrate; + u16 target_ssid_len; + u8 target_ssid[32]; +} __attribute__ ((packed)); + +struct hfa384x_join_request { + u8 bssid[6]; + u16 channel; +} __attribute__ ((packed)); + +struct hfa384x_info_frame { + u16 len; + u16 type; +} __attribute__ ((packed)); + +struct hfa384x_comm_tallies { + u16 tx_unicast_frames; + u16 tx_multicast_frames; + u16 tx_fragments; + u16 tx_unicast_octets; + u16 tx_multicast_octets; + u16 tx_deferred_transmissions; + u16 tx_single_retry_frames; + u16 tx_multiple_retry_frames; + u16 tx_retry_limit_exceeded; + u16 tx_discards; + u16 rx_unicast_frames; + u16 rx_multicast_frames; + u16 rx_fragments; + u16 rx_unicast_octets; + u16 rx_multicast_octets; + u16 rx_fcs_errors; + u16 rx_discards_no_buffer; + u16 tx_discards_wrong_sa; + u16 rx_discards_wep_undecryptable; + u16 rx_message_in_msg_fragments; + u16 rx_message_in_bad_msg_fragments; +} __attribute__ ((packed)); + +struct hfa384x_comm_tallies32 { + u32 tx_unicast_frames; + u32 tx_multicast_frames; + u32 tx_fragments; + u32 tx_unicast_octets; + u32 tx_multicast_octets; + u32 tx_deferred_transmissions; + u32 tx_single_retry_frames; + u32 tx_multiple_retry_frames; + u32 tx_retry_limit_exceeded; + u32 tx_discards; + u32 rx_unicast_frames; + u32 rx_multicast_frames; + u32 rx_fragments; + u32 rx_unicast_octets; + u32 rx_multicast_octets; + u32 rx_fcs_errors; + u32 rx_discards_no_buffer; + u32 tx_discards_wrong_sa; + u32 rx_discards_wep_undecryptable; + u32 rx_message_in_msg_fragments; + u32 rx_message_in_bad_msg_fragments; +} __attribute__ ((packed)); + +struct hfa384x_scan_result_hdr { + u16 reserved; + u16 scan_reason; +#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */ +#define HFA384X_SCAN_HOST_INITIATED 1 +#define HFA384X_SCAN_FIRMWARE_INITIATED 2 +#define HFA384X_SCAN_INQUIRY_FROM_HOST 3 +} __attribute__ ((packed)); + +#define HFA384X_SCAN_MAX_RESULTS 32 + +struct hfa384x_scan_result { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[6]; + u16 beacon_interval; + u16 capability; + u16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + u16 rate; +} __attribute__ ((packed)); + +struct hfa384x_hostscan_result { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[6]; + u16 beacon_interval; + u16 capability; + u16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + u16 rate; + u16 atim; +} __attribute__ ((packed)); + +struct comm_tallies_sums { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_wep_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + + +struct hfa384x_regs { + u16 cmd; + u16 evstat; + u16 offset0; + u16 offset1; + u16 swsupport0; +}; + + +#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX) +/* I/O ports for HFA384X Controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x02 +#define HFA384X_PARAM1_OFF 0x04 +#define HFA384X_PARAM2_OFF 0x06 +#define HFA384X_STATUS_OFF 0x08 +#define HFA384X_RESP0_OFF 0x0A +#define HFA384X_RESP1_OFF 0x0C +#define HFA384X_RESP2_OFF 0x0E +#define HFA384X_INFOFID_OFF 0x10 +#define HFA384X_CONTROL_OFF 0x14 +#define HFA384X_SELECT0_OFF 0x18 +#define HFA384X_SELECT1_OFF 0x1A +#define HFA384X_OFFSET0_OFF 0x1C +#define HFA384X_OFFSET1_OFF 0x1E +#define HFA384X_RXFID_OFF 0x20 +#define HFA384X_ALLOCFID_OFF 0x22 +#define HFA384X_TXCOMPLFID_OFF 0x24 +#define HFA384X_SWSUPPORT0_OFF 0x28 +#define HFA384X_SWSUPPORT1_OFF 0x2A +#define HFA384X_SWSUPPORT2_OFF 0x2C +#define HFA384X_EVSTAT_OFF 0x30 +#define HFA384X_INTEN_OFF 0x32 +#define HFA384X_EVACK_OFF 0x34 +#define HFA384X_DATA0_OFF 0x36 +#define HFA384X_DATA1_OFF 0x38 +#define HFA384X_AUXPAGE_OFF 0x3A +#define HFA384X_AUXOFFSET_OFF 0x3C +#define HFA384X_AUXDATA_OFF 0x3E +#endif /* PRISM2_PCCARD || PRISM2_PLX */ + +#ifdef PRISM2_PCI +/* Memory addresses for ISL3874 controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x04 +#define HFA384X_PARAM1_OFF 0x08 +#define HFA384X_PARAM2_OFF 0x0C +#define HFA384X_STATUS_OFF 0x10 +#define HFA384X_RESP0_OFF 0x14 +#define HFA384X_RESP1_OFF 0x18 +#define HFA384X_RESP2_OFF 0x1C +#define HFA384X_INFOFID_OFF 0x20 +#define HFA384X_CONTROL_OFF 0x28 +#define HFA384X_SELECT0_OFF 0x30 +#define HFA384X_SELECT1_OFF 0x34 +#define HFA384X_OFFSET0_OFF 0x38 +#define HFA384X_OFFSET1_OFF 0x3C +#define HFA384X_RXFID_OFF 0x40 +#define HFA384X_ALLOCFID_OFF 0x44 +#define HFA384X_TXCOMPLFID_OFF 0x48 +#define HFA384X_PCICOR_OFF 0x4C +#define HFA384X_SWSUPPORT0_OFF 0x50 +#define HFA384X_SWSUPPORT1_OFF 0x54 +#define HFA384X_SWSUPPORT2_OFF 0x58 +#define HFA384X_PCIHCR_OFF 0x5C +#define HFA384X_EVSTAT_OFF 0x60 +#define HFA384X_INTEN_OFF 0x64 +#define HFA384X_EVACK_OFF 0x68 +#define HFA384X_DATA0_OFF 0x6C +#define HFA384X_DATA1_OFF 0x70 +#define HFA384X_AUXPAGE_OFF 0x74 +#define HFA384X_AUXOFFSET_OFF 0x78 +#define HFA384X_AUXDATA_OFF 0x7C +#define HFA384X_PCI_M0_ADDRH_OFF 0x80 +#define HFA384X_PCI_M0_ADDRL_OFF 0x84 +#define HFA384X_PCI_M0_LEN_OFF 0x88 +#define HFA384X_PCI_M0_CTL_OFF 0x8C +#define HFA384X_PCI_STATUS_OFF 0x98 +#define HFA384X_PCI_M1_ADDRH_OFF 0xA0 +#define HFA384X_PCI_M1_ADDRL_OFF 0xA4 +#define HFA384X_PCI_M1_LEN_OFF 0xA8 +#define HFA384X_PCI_M1_CTL_OFF 0xAC + +/* PCI bus master control bits (these are undocumented; based on guessing and + * experimenting..) */ +#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0)) +#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0)) + +#endif /* PRISM2_PCI */ + + +/* Command codes for CMD reg. */ +#define HFA384X_CMDCODE_INIT 0x00 +#define HFA384X_CMDCODE_ENABLE 0x01 +#define HFA384X_CMDCODE_DISABLE 0x02 +#define HFA384X_CMDCODE_ALLOC 0x0A +#define HFA384X_CMDCODE_TRANSMIT 0x0B +#define HFA384X_CMDCODE_INQUIRE 0x11 +#define HFA384X_CMDCODE_ACCESS 0x21 +#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8)) +#define HFA384X_CMDCODE_DOWNLOAD 0x22 +#define HFA384X_CMDCODE_READMIF 0x30 +#define HFA384X_CMDCODE_WRITEMIF 0x31 +#define HFA384X_CMDCODE_TEST 0x38 + +#define HFA384X_CMDCODE_MASK 0x3F + +/* Test mode operations */ +#define HFA384X_TEST_CHANGE_CHANNEL 0x08 +#define HFA384X_TEST_MONITOR 0x0B +#define HFA384X_TEST_STOP 0x0F +#define HFA384X_TEST_CFG_BITS 0x15 +#define HFA384X_TEST_CFG_BIT_ALC BIT(3) + +#define HFA384X_CMD_BUSY BIT(15) + +#define HFA384X_CMD_TX_RECLAIM BIT(8) + +#define HFA384X_OFFSET_ERR BIT(14) +#define HFA384X_OFFSET_BUSY BIT(15) + + +/* ProgMode for download command */ +#define HFA384X_PROGMODE_DISABLE 0 +#define HFA384X_PROGMODE_ENABLE_VOLATILE 1 +#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2 +#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3 + +#define HFA384X_AUX_MAGIC0 0xfe01 +#define HFA384X_AUX_MAGIC1 0xdc23 +#define HFA384X_AUX_MAGIC2 0xba45 + +#define HFA384X_AUX_PORT_DISABLED 0 +#define HFA384X_AUX_PORT_DISABLE BIT(14) +#define HFA384X_AUX_PORT_ENABLE BIT(15) +#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15)) +#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15)) + +#define PRISM2_PDA_SIZE 1024 + + +/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */ +#define HFA384X_EV_TICK BIT(15) +#define HFA384X_EV_WTERR BIT(14) +#define HFA384X_EV_INFDROP BIT(13) +#ifdef PRISM2_PCI +#define HFA384X_EV_PCI_M1 BIT(9) +#define HFA384X_EV_PCI_M0 BIT(8) +#endif /* PRISM2_PCI */ +#define HFA384X_EV_INFO BIT(7) +#define HFA384X_EV_DTIM BIT(5) +#define HFA384X_EV_CMD BIT(4) +#define HFA384X_EV_ALLOC BIT(3) +#define HFA384X_EV_TXEXC BIT(2) +#define HFA384X_EV_TX BIT(1) +#define HFA384X_EV_RX BIT(0) + + +/* HFA384X Information frames */ +#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */ +#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */ +#define HFA384X_INFO_COMMTALLIES 0xF100 +#define HFA384X_INFO_SCANRESULTS 0xF101 +#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */ +#define HFA384X_INFO_HOSTSCANRESULTS 0xF103 +#define HFA384X_INFO_LINKSTATUS 0xF200 +#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */ +#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */ +#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */ +#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */ + +enum { HFA384X_LINKSTATUS_CONNECTED = 1, + HFA384X_LINKSTATUS_DISCONNECTED = 2, + HFA384X_LINKSTATUS_AP_CHANGE = 3, + HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4, + HFA384X_LINKSTATUS_AP_IN_RANGE = 5, + HFA384X_LINKSTATUS_ASSOC_FAILED = 6 }; + +enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2, + HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0, + HFA384X_PORTTYPE_HOSTAP = 6 }; + +#define HFA384X_RATES_1MBPS BIT(0) +#define HFA384X_RATES_2MBPS BIT(1) +#define HFA384X_RATES_5MBPS BIT(2) +#define HFA384X_RATES_11MBPS BIT(3) + +#define HFA384X_ROAMING_FIRMWARE 1 +#define HFA384X_ROAMING_HOST 2 +#define HFA384X_ROAMING_DISABLED 3 + +#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0) +#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1) +#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4) +#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7) + +#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13)) +#define HFA384X_RX_STATUS_PCF BIT(12) +#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8)) +#define HFA384X_RX_STATUS_UNDECR BIT(1) +#define HFA384X_RX_STATUS_FCSERR BIT(0) + +#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \ +(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13) +#define HFA384X_RX_STATUS_GET_MACPORT(s) \ +(((s) & HFA384X_RX_STATUS_MACPORT) >> 8) + +enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1, + HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 }; + + +#define HFA384X_TX_CTRL_ALT_RTRY BIT(5) +#define HFA384X_TX_CTRL_802_11 BIT(3) +#define HFA384X_TX_CTRL_802_3 0 +#define HFA384X_TX_CTRL_TX_EX BIT(2) +#define HFA384X_TX_CTRL_TX_OK BIT(1) + +#define HFA384X_TX_STATUS_RETRYERR BIT(0) +#define HFA384X_TX_STATUS_AGEDERR BIT(1) +#define HFA384X_TX_STATUS_DISCON BIT(2) +#define HFA384X_TX_STATUS_FORMERR BIT(3) + +/* HFA3861/3863 (BBP) Control Registers */ +#define HFA386X_CR_TX_CONFIGURE 0x12 /* CR9 */ +#define HFA386X_CR_RX_CONFIGURE 0x14 /* CR10 */ +#define HFA386X_CR_A_D_TEST_MODES2 0x1A /* CR13 */ +#define HFA386X_CR_MANUAL_TX_POWER 0x3E /* CR31 */ +#define HFA386X_CR_MEASURED_TX_POWER 0x74 /* CR58 */ + + +#ifdef __KERNEL__ + +#define PRISM2_TXFID_COUNT 8 +#define PRISM2_DATA_MAXLEN 2304 +#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame)) +#define PRISM2_TXFID_EMPTY 0xffff +#define PRISM2_TXFID_RESERVED 0xfffe +#define PRISM2_DUMMY_FID 0xffff +#define MAX_SSID_LEN 32 +#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */ + +#define PRISM2_DUMP_RX_HDR BIT(0) +#define PRISM2_DUMP_TX_HDR BIT(1) +#define PRISM2_DUMP_TXEXC_HDR BIT(2) + +struct hostap_tx_callback_info { + u16 idx; + void (*func)(struct sk_buff *, int ok, void *); + void *data; + struct hostap_tx_callback_info *next; +}; + + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define PRISM2_FRAG_CACHE_LEN 4 + +struct prism2_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + + +struct prism2_crypt_data { + struct list_head list; /* delayed deletion list */ + struct hostap_crypto_ops *ops; + void *priv; + atomic_t refcnt; +}; + +struct hostap_cmd_queue { + struct list_head list; + wait_queue_head_t compl; + volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type; + void (*callback)(struct net_device *dev, void *context, u16 resp0, + u16 res); + void *context; + u16 cmd, param0, param1; + u16 resp0, res; + volatile int issued, issuing; + + atomic_t usecnt; + int del_req; +}; + +/* options for hw_shutdown */ +#define HOSTAP_HW_NO_DISABLE BIT(0) +#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1) + +typedef struct local_info local_info_t; + +struct prism2_helper_functions { + /* these functions are defined in hardware model specific files + * (hostap_{cs,plx,pci}.c */ + int (*card_present)(local_info_t *local); + void (*cor_sreset)(local_info_t *local); + int (*dev_open)(local_info_t *local); + int (*dev_close)(local_info_t *local); + void (*genesis_reset)(local_info_t *local, int hcr); + + /* the following functions are from hostap_hw.c, but they may have some + * hardware model specific code */ + + /* FIX: low-level commands like cmd might disappear at some point to + * make it easier to change them if needed (e.g., cmd would be replaced + * with write_mif/read_mif/testcmd/inquire); at least get_rid and + * set_rid might move to hostap_{cs,plx,pci}.c */ + int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1, + u16 *resp0); + void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs); + int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len); + int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len); + int (*hw_enable)(struct net_device *dev, int initial); + int (*hw_config)(struct net_device *dev, int initial); + void (*hw_reset)(struct net_device *dev); + void (*hw_shutdown)(struct net_device *dev, int no_disable); + int (*reset_port)(struct net_device *dev); + void (*schedule_reset)(local_info_t *local); + int (*download)(local_info_t *local, + struct prism2_download_param *param); + int (*tx)(struct sk_buff *skb, struct net_device *dev); + int (*set_tim)(struct net_device *dev, int aid, int set); + int (*read_aux)(struct net_device *dev, unsigned addr, int len, + u8 *buf); + + int need_tx_headroom; /* number of bytes of headroom needed before + * IEEE 802.11 header */ + enum { HOSTAP_HW_PCCARD, HOSTAP_HW_PLX, HOSTAP_HW_PCI } hw_type; +}; + + +struct prism2_download_data { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_data_area { + u32 addr; /* wlan card address */ + u32 len; + u8 *data; /* allocated data */ + } data[0]; +}; + + +#define HOSTAP_MAX_BSS_COUNT 64 +#define MAX_WPA_IE_LEN 64 + +struct hostap_bss_info { + struct list_head list; + unsigned long last_update; + unsigned int count; + u8 bssid[ETH_ALEN]; + u16 capab_info; + u8 ssid[32]; + size_t ssid_len; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + int chan; + int included; +}; + + +/* Per radio private Host AP data - shared by all net devices interfaces used + * by each radio (wlan#, wlan#ap, wlan#sta, WDS). + * ((struct hostap_interface *) dev->priv)->local points to this structure. */ +struct local_info { + struct module *hw_module; + int card_idx; + int dev_enabled; + int master_dev_auto_open; /* was master device opened automatically */ + int num_dev_open; /* number of open devices */ + struct net_device *dev; /* master radio device */ + struct net_device *ddev; /* main data device */ + struct list_head hostap_interfaces; /* Host AP interface list (contains + * struct hostap_interface entries) + */ + rwlock_t iface_lock; /* hostap_interfaces read lock; use write lock + * when removing entries from the list. + * TX and RX paths can use read lock. */ + spinlock_t cmdlock, baplock, lock; + struct semaphore rid_bap_sem; + u16 infofid; /* MAC buffer id for info frame */ + /* txfid, intransmitfid, next_txtid, and next_alloc are protected by + * txfidlock */ + spinlock_t txfidlock; + int txfid_len; /* length of allocated TX buffers */ + u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */ + /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if + * corresponding txfid is free for next TX frame */ + u16 intransmitfid[PRISM2_TXFID_COUNT]; + int next_txfid; /* index to the next txfid to be checked for + * availability */ + int next_alloc; /* index to the next intransmitfid to be checked for + * allocation events */ + + /* bitfield for atomic bitops */ +#define HOSTAP_BITS_TRANSMIT 0 +#define HOSTAP_BITS_BAP_TASKLET 1 +#define HOSTAP_BITS_BAP_TASKLET2 2 + long bits; + + struct ap_data *ap; + + char essid[MAX_SSID_LEN + 1]; + char name[MAX_NAME_LEN + 1]; + int name_set; + u16 channel_mask; + struct comm_tallies_sums comm_tallies; + struct net_device_stats stats; + struct proc_dir_entry *proc; + int iw_mode; /* operating mode (IW_MODE_*) */ + int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS + * 1: IW_MODE_ADHOC is "pseudo IBSS" */ + char bssid[ETH_ALEN]; + int channel; + int beacon_int; + int dtim_period; + int mtu; + int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */ + int fw_tx_rate_control; + u16 tx_rate_control; + u16 basic_rates; + int hw_resetting; + int hw_ready; + int hw_reset_tries; /* how many times reset has been tried */ + int hw_downloading; + int shutdown; + int pri_only; + int no_pri; /* no PRI f/w present */ + int sram_type; /* 8 = x8 SRAM, 16 = x16 SRAM, -1 = unknown */ + + enum { + PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF, + PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN + } txpower_type; + int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */ + + /* command queue for hfa384x_cmd(); protected with cmdlock */ + struct list_head cmd_queue; + /* max_len for cmd_queue; in addition, cmd_callback can use two + * additional entries to prevent sleeping commands from stopping + * transmits */ +#define HOSTAP_CMD_QUEUE_MAX_LEN 16 + int cmd_queue_len; /* number of entries in cmd_queue */ + + /* if card timeout is detected in interrupt context, reset_queue is + * used to schedule card reseting to be done in user context */ + HOSTAP_QUEUE reset_queue; + + /* For scheduling a change of the promiscuous mode RID */ + int is_promisc; + HOSTAP_QUEUE set_multicast_list_queue; + + HOSTAP_QUEUE set_tim_queue; + struct list_head set_tim_list; + spinlock_t set_tim_lock; + + int wds_max_connections; + int wds_connections; +#define HOSTAP_WDS_BROADCAST_RA BIT(0) +#define HOSTAP_WDS_AP_CLIENT BIT(1) +#define HOSTAP_WDS_STANDARD_FRAME BIT(2) + u32 wds_type; + u16 tx_control; /* flags to be used in TX description */ + int manual_retry_count; /* -1 = use f/w default; otherwise retry count + * to be used with all frames */ + + struct iw_statistics wstats; + unsigned long scan_timestamp; /* Time started to scan */ + enum { + PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1, + PRISM2_MONITOR_CAPHDR = 2 + } monitor_type; + int (*saved_eth_header_parse)(struct sk_buff *skb, + unsigned char *haddr); + int monitor_allow_fcserr; + + int hostapd; /* whether user space daemon, hostapd, is used for AP + * management */ + int hostapd_sta; /* whether hostapd is used with an extra STA interface + */ + struct net_device *apdev; + struct net_device_stats apdevstats; + + char assoc_ap_addr[ETH_ALEN]; + struct net_device *stadev; + struct net_device_stats stadevstats; + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + struct prism2_crypt_data *crypt[WEP_KEYS]; + int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */ + struct timer_list crypt_deinit_timer; + struct list_head crypt_deinit_list; + + int open_wep; /* allow unencrypted frames */ + int host_encrypt; + int host_decrypt; + int privacy_invoked; /* force privacy invoked flag even if no keys are + * configured */ + int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working + * in Host AP mode (STA f/w 1.4.9 or newer) */ + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + + int ieee_802_1x; /* is IEEE 802.1X used */ + + int antsel_tx, antsel_rx; + int rts_threshold; /* dot11RTSThreshold */ + int fragm_threshold; /* dot11FragmentationThreshold */ + int auth_algs; /* PRISM2_AUTH_ flags */ + + int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */ + int tallies32; /* 32-bit tallies in use */ + + struct prism2_helper_functions *func; + + int bus_master_threshold_tx; + int bus_master_threshold_rx; + u8 *bus_m1_buf; + + u8 *pda; + int fw_ap; +#define PRISM2_FW_VER(major, minor, variant) \ +(((major) << 16) | ((minor) << 8) | variant) + u32 sta_fw_ver; + + /* Tasklets for handling hardware IRQ related operations outside hw IRQ + * handler */ + HOSTAP_TASKLET bap_tasklet; + + HOSTAP_TASKLET info_tasklet; + struct sk_buff_head info_list; /* info frames as skb's for + * info_tasklet */ + + struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks + */ + + HOSTAP_TASKLET rx_tasklet; + struct sk_buff_head rx_list; + + HOSTAP_TASKLET sta_tx_exc_tasklet; + struct sk_buff_head sta_tx_exc_list; + + int host_roaming; + unsigned long last_join_time; /* time of last JoinRequest */ + struct hfa384x_scan_result *last_scan_results; + int last_scan_results_count; + struct hfa384x_hostscan_result *last_hostscan_results; + int last_hostscan_results_count; + enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type; + HOSTAP_QUEUE info_queue; + long pending_info; /* bit field of pending info_queue items */ +#define PRISM2_INFO_PENDING_LINKSTATUS 0 +#define PRISM2_INFO_PENDING_SCANRESULTS 1 + int prev_link_status; /* previous received LinkStatus info */ + u8 preferred_ap[6]; /* use this AP if possible */ + +#ifdef PRISM2_CALLBACK + void *callback_data; /* Can be used in callbacks; e.g., allocate + * on enable event and free on disable event. + * Host AP driver code does not touch this. */ +#endif /* PRISM2_CALLBACK */ + + wait_queue_head_t hostscan_wq; + + /* Passive scan in Host AP mode */ + struct timer_list passive_scan_timer; + int passive_scan_interval; /* in seconds, 0 = disabled */ + int passive_scan_channel; + enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state; + + struct timer_list tick_timer; + unsigned long last_tick_timer; + unsigned int sw_tick_stuck; + + /* commsQuality / dBmCommsQuality data from periodic polling; only + * valid for Managed and Ad-hoc modes */ + unsigned long last_comms_qual_update; + int comms_qual; /* in some odd unit.. */ + int avg_signal; /* in dB (note: negative) */ + int avg_noise; /* in dB (note: negative) */ + HOSTAP_QUEUE comms_qual_update; + + /* RSSI to dBm adjustment (for RX descriptor fields) */ + int rssi_to_dBm; /* substract from RSSI to get approximate dBm value */ + + /* BSS list / protected by local->lock */ + struct list_head bss_list; + int num_bss_info; + int wpa; /* WPA support enabled */ + int tkip_countermeasures; + int drop_unencrypted; + /* Generic IEEE 802.11 info element to be added to + * ProbeResp/Beacon/(Re)AssocReq */ + u8 *generic_elem; + size_t generic_elem_len; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + /* Persistent volatile download data */ + struct prism2_download_data *dl_pri; + struct prism2_download_data *dl_sec; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_IO_DEBUG +#define PRISM2_IO_DEBUG_SIZE 10000 + u32 io_debug[PRISM2_IO_DEBUG_SIZE]; + int io_debug_head; + int io_debug_enabled; +#endif /* PRISM2_IO_DEBUG */ + + /* struct local_info is used also in hostap.o that does not define + * any PRISM2_{PCCARD,PLX,PCI}. Make sure that the hardware version + * specific fields are in the end of the struct (these could also be + * moved to void *priv or something like that). */ +#ifdef PRISM2_PCCARD + dev_node_t node; + dev_link_t *link; + int sandisk_connectplus; +#endif /* PRISM2_PCCARD */ + +#ifdef PRISM2_PLX + void *attr_mem; + unsigned int cor_offset; +#endif /* PRISM2_PLX */ + +#ifdef PRISM2_PCI +#ifdef PRISM2_BUS_MASTER + /* bus master for BAP0 (TX) */ + int bus_m0_tx_idx; + u8 *bus_m0_buf; + + /* bus master for BAP1 (RX) */ + struct sk_buff *rx_skb; +#endif /* PRISM2_BUS_MASTER */ +#ifdef CONFIG_PM +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) + u32 pci_save_state[16]; +#endif +#endif /* CONFIG_PM */ +#endif /* PRISM2_PCI */ + + /* NOTE! Do not add common entries here after hardware version + * specific blocks. */ +}; + + +/* Per interface private Host AP data + * Allocated for each net device that Host AP uses (wlan#, wlan#ap, wlan#sta, + * WDS) and dev->priv points to this structure. */ +struct hostap_interface { + struct list_head list; /* list entry in Host AP interface list */ + struct net_device *dev; /* pointer to this device */ + struct local_info *local; /* pointer to shared private data */ + struct net_device_stats stats; +#if WIRELESS_EXT > 15 + /* Note: this data area must be at a fixed offset from dev->priv. + * Unfortunately, this model does not fit the current Host AP netdev + * data structure because this should really be in local_into_t that is + * shared by all virtual interfaces. Currently, only the main data + * device (wlan#) is used for iwspy entries. */ + struct iw_spy_data spy_data; /* iwspy support */ +#if WIRELESS_EXT > 16 + /* This is the fix for the problem described above. Jean II */ + struct iw_public_data wireless_data; +#endif /* WIRELESS_EXT > 16 */ +#endif /* WIRELESS_EXT > 15 */ + + enum { + HOSTAP_INTERFACE_MASTER, + HOSTAP_INTERFACE_MAIN, + HOSTAP_INTERFACE_AP, + HOSTAP_INTERFACE_STA, + HOSTAP_INTERFACE_WDS, + } type; + + union { + struct hostap_interface_wds { + u8 remote_addr[ETH_ALEN]; + } wds; + } u; +}; + + +#define HOSTAP_SKB_TX_DATA_MAGIC 0xf08a36a2 + +/* TX meta data - stored in skb->cb buffer, so this must be not increase over + * 48-byte limit */ +struct hostap_skb_tx_data { + unsigned int magic; /* HOSTAP_SKB_TX_DATA_MAGIC */ + int rate; /* transmit rate */ + struct hostap_interface *iface; + unsigned long jiffies; /* queueing timestamp */ + int wds; + unsigned short ethertype; + int tx_cb_idx; +}; + + +#ifndef PRISM2_NO_DEBUG + +#define DEBUG_FID BIT(0) +#define DEBUG_PS BIT(1) +#define DEBUG_FLOW BIT(2) +#define DEBUG_AP BIT(3) +#define DEBUG_HW BIT(4) +#define DEBUG_EXTRA BIT(5) +#define DEBUG_EXTRA2 BIT(6) +#define DEBUG_PS2 BIT(7) +#define DEBUG_MASK (DEBUG_PS | DEBUG_AP | DEBUG_HW | DEBUG_EXTRA) +#define PDEBUG(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0) +#define PDEBUG2(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(args); } while (0) + +#else /* PRISM2_NO_DEBUG */ + +#define PDEBUG(n, args...) +#define PDEBUG2(n, args...) + +#endif /* PRISM2_NO_DEBUG */ + +enum { BAP0 = 0, BAP1 = 1 }; + +#define PRISM2_IO_DEBUG_CMD_INB 0 +#define PRISM2_IO_DEBUG_CMD_INW 1 +#define PRISM2_IO_DEBUG_CMD_INSW 2 +#define PRISM2_IO_DEBUG_CMD_OUTB 3 +#define PRISM2_IO_DEBUG_CMD_OUTW 4 +#define PRISM2_IO_DEBUG_CMD_OUTSW 5 +#define PRISM2_IO_DEBUG_CMD_ERROR 6 +#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7 + +#ifdef PRISM2_IO_DEBUG + +#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \ +(((cmd) << 24) | ((reg) << 16) | value) + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + + if (!local->io_debug_enabled) + return; + + local->io_debug[local->io_debug_head] = jiffies & 0xffffffff; + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; + local->io_debug[local->io_debug_head] = + PRISM2_IO_DEBUG_ENTRY(cmd, reg, value); + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; +} + + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ + struct hostap_interface *iface = dev->priv; + local_info_t *local = iface->local; + unsigned long flags; + + if (!local->io_debug_enabled) + return; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err); + if (local->io_debug_enabled == 1) { + local->io_debug_enabled = 0; + printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name); + } + spin_unlock_irqrestore(&local->lock, flags); +} + +#else /* PRISM2_IO_DEBUG */ + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ +} + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ +} + +#endif /* PRISM2_IO_DEBUG */ + + +#ifdef PRISM2_CALLBACK +enum { + /* Called when card is enabled */ + PRISM2_CALLBACK_ENABLE, + + /* Called when card is disabled */ + PRISM2_CALLBACK_DISABLE, + + /* Called when RX/TX starts/ends */ + PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END, + PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END +}; +void prism2_callback(local_info_t *local, int event); +#else /* PRISM2_CALLBACK */ +#define prism2_callback(d, e) do { } while (0) +#endif /* PRISM2_CALLBACK */ + +#endif /* __KERNEL__ */ + +#endif /* HOSTAP_WLAN_H */ diff -Nur linux-2.6.9-intc1/drivers/net/wireless/Kconfig linux-2.6.9-intc1-mih1/drivers/net/wireless/Kconfig --- linux-2.6.9-intc1/drivers/net/wireless/Kconfig 2004-10-18 17:53:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/Kconfig 2005-03-10 18:39:05.000000000 -0500 @@ -355,6 +355,74 @@ say M here and read . The module will be called prism54.ko. +config HOSTAP + tristate "Host AP support for Prism2/2.5/3 IEEE 802.11b" + depends on NET_RADIO + ---help--- + A driver for 802.11b wireless cards based on Intersil Prism2/2.5/3 + chipset. This driver supports so called Host AP mode that allows + the card to act as an IEEE 802.11 access point. + + See for more information about the + Host AP driver configuration and tools. + + This option includes the base Host AP driver code that is shared by + different hardware models. You will also need to enable support for + PLX/PCI/CS version of the driver to actually use the driver. + + The driver can be compiled as modules and they will be called + "hostap.o" and "hostap_crypt_wep.o". + +config HOSTAP_FIRMWARE + bool "Support downloading firmware images with Host AP driver" + depends on HOSTAP + ---help--- + Configure Host AP driver to include support for firmware image + download. Current version supports only downloading to volatile, i.e., + RAM memory. Flash upgrade is not yet supported. + + Firmware image downloading needs user space tool, prism2_srec. It is + available from http://hostap.epitest.fi/. + +config HOSTAP_PLX + tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based + PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_plx.o". + +config HOSTAP_PCI + tristate "Host AP driver for Prism2.5 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2.5 PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_pci.o". + +config HOSTAP_CS + tristate "Host AP driver for Prism2/2.5/3 PC Cards" + depends on PCMCIA!=n && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named "hostap_cs.o". + # yes, this works even when no drivers are selected config NET_WIRELESS bool diff -Nur linux-2.6.9-intc1/drivers/net/wireless/Makefile linux-2.6.9-intc1-mih1/drivers/net/wireless/Makefile --- linux-2.6.9-intc1/drivers/net/wireless/Makefile 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/net/wireless/Makefile 2005-03-10 18:39:05.000000000 -0500 @@ -28,6 +28,12 @@ obj-$(CONFIG_PRISM54) += prism54/ +obj-$(CONFIG_HOSTAP) += hostap.o hostap_crypt_wep.o \ + hostap_crypt_tkip.o hostap_crypt_ccmp.o +obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o +obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o +obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o + # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o diff -Nur linux-2.6.9-intc1/drivers/pcmcia/Makefile linux-2.6.9-intc1-mih1/drivers/pcmcia/Makefile --- linux-2.6.9-intc1/drivers/pcmcia/Makefile 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/pcmcia/Makefile 2005-02-21 22:23:43.000000000 -0500 @@ -50,4 +50,5 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o +pxa2xx_cs-$(CONFIG_MACH_GLENCOE) += pxa2xx_glencoe.o diff -Nur linux-2.6.9-intc1/drivers/pcmcia/pxa2xx_base.c linux-2.6.9-intc1-mih1/drivers/pcmcia/pxa2xx_base.c --- linux-2.6.9-intc1/drivers/pcmcia/pxa2xx_base.c 2004-10-18 17:55:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/pcmcia/pxa2xx_base.c 2005-04-21 20:52:45.000000000 -0400 @@ -140,7 +140,7 @@ unsigned long val, struct cpufreq_freqs *freqs) { -#warning "it's not clear if this is right since the core CPU (N) clock has no effect on the memory (L) clock" +#warning "pcmcia_frequency_change: it's not clear if this is right since the core CPU (N) clock has no effect on the memory (L) clock" switch (val) { case CPUFREQ_PRECHANGE: if (freqs->new > freqs->old) { diff -Nur linux-2.6.9-intc1/drivers/pcmcia/pxa2xx_glencoe.c linux-2.6.9-intc1-mih1/drivers/pcmcia/pxa2xx_glencoe.c --- linux-2.6.9-intc1/drivers/pcmcia/pxa2xx_glencoe.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/pcmcia/pxa2xx_glencoe.c 2005-05-02 16:45:58.000000000 -0400 @@ -0,0 +1,184 @@ +/* + * linux/drivers/pcmcia/pxa2xx_glencoe.c + * + * Glencoe CompactFlash specific routines, + * based on pxa2xx_mainstone.c by Nicolas Pitre + * adapted for CompactFlash by Matthias Ihmig + * + * 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 "soc_common.h" + + +static struct pcmcia_irqs irqs[] = { + { 0, GLENCOE_S0_CD_IRQ, "CF0 CD" }, + { 1, GLENCOE_S1_CD_IRQ, "CF1 CD" }, +}; + +static int gln_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + /* + * Setup default state of GPIO outputs + * before we enable them as outputs. + */ + GPSR(GPIO48_nPOE) = + GPIO_bit(GPIO48_nPOE) | + GPIO_bit(GPIO49_nPWE) | + GPIO_bit(GPIO50_nPIOR) | + GPIO_bit(GPIO51_nPIOW) | + GPIO_bit(GPIO85_nPCE_1) | + GPIO_bit(GPIO54_nPCE_2); + + pxa_gpio_mode(GPIO48_nPOE_MD); + pxa_gpio_mode(GPIO49_nPWE_MD); + pxa_gpio_mode(GPIO50_nPIOR_MD); + pxa_gpio_mode(GPIO51_nPIOW_MD); + pxa_gpio_mode(GPIO85_nPCE_1_MD); + pxa_gpio_mode(GPIO54_nPCE_2_MD); + pxa_gpio_mode(GPIO79_pSKTSEL_MD); + pxa_gpio_mode(GPIO55_nPREG_MD); + pxa_gpio_mode(GPIO56_nPWAIT_MD); + pxa_gpio_mode(GPIO57_nIOIS16_MD); + + GPIO_clr(GLN_GPIO_CF_PWR_ON); + + pxa_gpio_mode(GLN_GPIO_nCF1_IRQ | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nCF2_IRQ | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nCF1_CARD_DET | GPIO_IN); + pxa_gpio_mode(GLN_GPIO_nCF2_CARD_DET | GPIO_IN); + + pxa_gpio_mode(GLN_GPIO_CF_PWR_ON | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_CF1_RST | GPIO_OUT); + pxa_gpio_mode(GLN_GPIO_CF2_RST | GPIO_OUT); + + GPIO_set(GLN_GPIO_CF_PWR_ON); + + skt->irq = (skt->nr == 0) ? GLENCOE_S0_IRQ : GLENCOE_S1_IRQ; + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static void gln_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + GPIO_clr(GLN_GPIO_CF_PWR_ON); +} + +static void gln_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + if (skt->nr == 0) { + state->detect = (GPIO_lev(GLN_GPIO_nCF1_CARD_DET)) ? 0 : 1; + state->ready = (GPIO_lev(GLN_GPIO_nCF1_IRQ)) ? 1 : 0; + } else { + state->detect = (GPIO_lev(GLN_GPIO_nCF2_CARD_DET)) ? 0 : 1; + state->ready = (GPIO_lev(GLN_GPIO_nCF2_IRQ)) ? 1 : 0; + } + state->bvd1 = 1; + state->bvd2 = 1; + state->vs_3v = 1; + state->vs_Xv = 0; + state->wrprot = 0; /* not available */ +} + +static int gln_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + int ret = 0; + + GPIO_set(GLN_GPIO_CF_PWR_ON); /* no need to set voltage -> just switch power on */ + + printk(KERN_NOTICE "%s(): configuring sockekt %u, Vcc=%u, Vpp=%u\n", + __FUNCTION__, skt->nr, state->Vcc, state->Vpp); + + ret = -1; + if (skt->nr == 0) { + if (state->flags & SS_RESET) + GPIO_set(GLN_GPIO_CF1_RST); + else + GPIO_clr(GLN_GPIO_CF1_RST); + ret = 0; + }; + if (skt->nr == 1) { + if (state->flags & SS_RESET) + GPIO_set(GLN_GPIO_CF2_RST); + else + GPIO_clr(GLN_GPIO_CF2_RST); + ret = 0; + } + + return ret; +} + +static void gln_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void gln_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level gln_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = gln_pcmcia_hw_init, + .hw_shutdown = gln_pcmcia_hw_shutdown, + .socket_state = gln_pcmcia_socket_state, + .configure_socket = gln_pcmcia_configure_socket, + .socket_init = gln_pcmcia_socket_init, + .socket_suspend = gln_pcmcia_socket_suspend, + .nr = 2, +}; + +static struct platform_device *gln_pcmcia_device; + +static int __init gln_pcmcia_init(void) +{ + int ret; + + gln_pcmcia_device = kmalloc(sizeof(*gln_pcmcia_device), GFP_KERNEL); + if (!gln_pcmcia_device) + return -ENOMEM; + memset(gln_pcmcia_device, 0, sizeof(*gln_pcmcia_device)); + gln_pcmcia_device->name = "pxa2xx-pcmcia"; + gln_pcmcia_device->dev.platform_data = &gln_pcmcia_ops; + + ret = platform_device_register(gln_pcmcia_device); + if (ret) + kfree(gln_pcmcia_device); + + return ret; +} + +static void __exit gln_pcmcia_exit(void) +{ + /* + * This call is supposed to free our gln_pcmcia_device. + * Unfortunately platform_device don't have a free method, and + * we can't assume it's free of any reference at this point so we + * can't free it either. + */ + platform_device_unregister(gln_pcmcia_device); +} + +module_init(gln_pcmcia_init); +module_exit(gln_pcmcia_exit); + +MODULE_AUTHOR("Matthias Ihmig "); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/drivers/serial/pxa.c linux-2.6.9-intc1-mih1/drivers/serial/pxa.c --- linux-2.6.9-intc1/drivers/serial/pxa.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/serial/pxa.c 2005-05-01 14:54:02.000000000 -0400 @@ -48,8 +48,8 @@ #include -#ifdef CONFIG_PXA27x -#include +#ifdef CONFIG_MACH_MAINSTONE + #include #endif struct uart_pxa_port { @@ -396,6 +396,16 @@ } #endif +static void +serial_pxa_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + pxa_set_cken(up->cken, !state); + if (!state) + udelay(1); +} + static int serial_pxa_startup(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; @@ -411,18 +421,35 @@ if (retval) return retval; -/* This enable the BTUART. FFUART has been enabled in bootloader. - * STUART will be enabled in IrDA driver. Put these codes after - * request irq successful. - */ + /* Enable the requested serial port. Don't rely on previous activation, that might + * not have taken place - just do it here. + * Put these codes after request irq is successful. + */ + + if ( IRQ_FFUART == up->port.irq) { + pxa_gpio_mode(GPIO34_FFRXD_MD); + pxa_gpio_mode(GPIO35_FFCTS_MD); + pxa_gpio_mode(GPIO36_FFDCD_MD); + pxa_gpio_mode(GPIO37_FFDSR_MD); + pxa_gpio_mode(GPIO38_FFRI_MD); + pxa_gpio_mode(GPIO39_FFTXD_MD); + pxa_gpio_mode(GPIO40_FFDTR_MD); + pxa_gpio_mode(GPIO41_FFRTS_MD); + } if ( IRQ_BTUART == up->port.irq) { +#ifdef CONFIG_MACH_MAINSTONE MST_MSCWR1 |= (0x03<<7); - CKEN |= CKEN7_BTUART; +#endif pxa_gpio_mode(GPIO42_BTRXD_MD); pxa_gpio_mode(GPIO43_BTTXD_MD); pxa_gpio_mode(GPIO44_BTCTS_MD); pxa_gpio_mode(GPIO45_BTRTS_MD); } + if ( IRQ_STUART == up->port.irq) { + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + } + serial_pxa_pm((struct uart_port *)up, 0, 0); // enable clock /* * Clear the FIFO buffers and disable them. @@ -605,16 +632,6 @@ spin_unlock_irqrestore(&up->port.lock, flags); } -static void -serial_pxa_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - pxa_set_cken(up->cken, !state); - if (!state) - udelay(1); -} - static void serial_pxa_release_port(struct uart_port *port) { } diff -Nur linux-2.6.9-intc1/drivers/serial/pxa.c~kgdb linux-2.6.9-intc1-mih1/drivers/serial/pxa.c~kgdb --- linux-2.6.9-intc1/drivers/serial/pxa.c~kgdb 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/drivers/serial/pxa.c~kgdb 2005-05-01 02:11:07.000000000 -0400 @@ -0,0 +1,943 @@ +/* + * linux/drivers/serial/pxa.c + * + * Based on drivers/serial/8250.c by Russell King. + * + * Author: Nicolas Pitre + * Created: Feb 20, 2003 + * Copyright: (C) 2003 Monta Vista Software, 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. + * + * Note 1: This driver is made separate from the already too overloaded + * 8250.c because it needs some kirks of its own and that'll make it + * easier to add DMA support. + * + * Note 2: I'm too sick of device allocation policies for serial ports. + * If someone else wants to request an "official" allocation of major/minor + * for this driver please be my guest. And don't forget that new hardware + * to come from Intel might have more than 3 or 4 of those UARTs. Let's + * hope for a better port registration and dynamic device allocation scheme + * with the serial core maintainer satisfaction to appear soon. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#ifdef CONFIG_MACH_MAINSTONE + #include +#endif + +struct uart_pxa_port { + struct uart_port port; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned int lsr_break_flag; + unsigned int cken; + char *name; +}; + +static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) +{ + offset <<= 2; + return readl(up->port.membase + offset); +} + +static inline void serial_out(struct uart_pxa_port *up, int offset, int value) +{ + offset <<= 2; + writel(value, up->port.membase + offset); +} + +static void serial_pxa_enable_ms(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void serial_pxa_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_pxa_stop_rx(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static inline void +receive_chars(struct uart_pxa_port *up, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = up->port.info->tty; + unsigned char ch; + int max_count = 256; + + do { + if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { + /* + * FIXME: Deadlock can happen here if we're a + * low-latency port. We're holding the per-port + * spinlock, and we call flush_to_ldisc-> + * n_tty_receive_buf->n_tty_receive_char-> + * opost->uart_put_char. + */ + tty->flip.work.func((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_in(up, UART_RX); + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_PXA_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch, regs)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_pxa_port *up) +{ + struct circ_buf *xmit = &up->port.info->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_pxa_stop_tx(&up->port, 0); + return; + } + + count = up->port.fifosize / 2; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + + if (uart_circ_empty(xmit)) + serial_pxa_stop_tx(&up->port, 0); +} + +static void serial_pxa_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static inline void check_modem_status(struct uart_pxa_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.info->delta_msr_wait); +} + +#ifdef CONFIG_PXA27x_E20 +static inline void disable_timeout_int(struct uart_pxa_port *up) +{ + up->ier &= ~UART_IER_RTOIE; + serial_out(up, UART_IER, up->ier); +} + +static inline void enable_timeout_int(struct uart_pxa_port *up) +{ + up->ier |= UART_IER_RTOIE; + serial_out(up, UART_IER, up->ier); +} +#else +static inline void disable_timeout_int(struct uart_pxa_port *up) {} +static inline void enable_timeout_int(struct uart_pxa_port *up) {} +#endif + +/* + * This handles the interrupt from one port. + */ +static inline irqreturn_t +serial_pxa_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)dev_id; + unsigned int iir, lsr; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + lsr = serial_in(up, UART_LSR); + if (lsr & UART_LSR_DR) { +/*FIXME: PXA27x E20 */ + disable_timeout_int(up); + receive_chars(up, &lsr, regs); + enable_timeout_int(up); + } + check_modem_status(up); + if (lsr & UART_LSR_THRE) + transmit_chars(up); + return IRQ_HANDLED; +} + +static unsigned int serial_pxa_tx_empty(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_pxa_get_mctrl(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + unsigned char status; + unsigned int ret; + +return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + spin_lock_irqsave(&up->port.lock, flags); + status = serial_in(up, UART_MSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char mcr = 0; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr |= up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial_pxa_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +#if 0 +static void serial_pxa_dma_init(struct pxa_uart *up) +{ + up->rxdma = + pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up); + if (up->rxdma < 0) + goto out; + up->txdma = + pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up); + if (up->txdma < 0) + goto err_txdma; + up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL); + if (!up->dmadesc) + goto err_alloc; + + /* ... */ +err_alloc: + pxa_free_dma(up->txdma); +err_rxdma: + pxa_free_dma(up->rxdma); +out: + return; +} +#endif + +static void +serial_pxa_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + pxa_set_cken(up->cken, !state); + if (!state) + udelay(1); +} + +static int serial_pxa_startup(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + int retval; + + up->mcr = 0; + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up); + if (retval) + return retval; + + /* Enable the requested serial port. Don't rely on previous activation, that might + * not have taken place - just do it here. + * Put these codes after request irq is successful. + */ + + if ( IRQ_FFUART == up->port.irq) { + pxa_gpio_mode(GPIO34_FFRXD_MD); + pxa_gpio_mode(GPIO35_FFCTS_MD); + pxa_gpio_mode(GPIO36_FFDCD_MD); + pxa_gpio_mode(GPIO37_FFDSR_MD); + pxa_gpio_mode(GPIO38_FFRI_MD); + pxa_gpio_mode(GPIO39_FFTXD_MD); + pxa_gpio_mode(GPIO40_FFDTR_MD); + pxa_gpio_mode(GPIO41_FFRTS_MD); + } + if ( IRQ_BTUART == up->port.irq) { +#ifdef CONFIG_MACH_MAINSTONE + MST_MSCWR1 |= (0x03<<7); +#endif + pxa_gpio_mode(GPIO42_BTRXD_MD); + pxa_gpio_mode(GPIO43_BTTXD_MD); + pxa_gpio_mode(GPIO44_BTCTS_MD); + pxa_gpio_mode(GPIO45_BTRTS_MD); + } + if ( IRQ_STUART == up->port.irq) { + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + } + serial_pxa_pm((struct uart_port *)up, 0, 0); // enable clock + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl |= TIOCM_OUT2; + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occuring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; + serial_out(up, UART_IER, up->ier); + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + return 0; +} + +static void serial_pxa_shutdown(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + + free_irq(up->port.irq, up); + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +static void +serial_pxa_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= 0x04; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Ensure the port will be enabled. + * This is required especially for serial console. + */ + up->ier |= IER_UUE; + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, quot); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + serial_out(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + serial_out(up, UART_FCR, fcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void serial_pxa_release_port(struct uart_port *port) +{ +} + +static int serial_pxa_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_pxa_config_port(struct uart_port *port, int flags) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + up->port.type = PORT_PXA; +} + +static int +serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + +static const char * +serial_pxa_type(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + return up->name; +} + +#ifdef CONFIG_SERIAL_PXA_CONSOLE + +extern struct uart_pxa_port serial_pxa_ports[]; +extern struct uart_driver serial_pxa_reg; + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_pxa_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +serial_pxa_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_pxa_port *up = &serial_pxa_ports[co->index]; + unsigned int ier; + int i; + + /* + * First save the UER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, UART_IER_UUE); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(up); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(up); + serial_out(up, UART_TX, 13); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); +} + +static int __init +serial_pxa_console_setup(struct console *co, char *options) +{ + struct uart_pxa_port *up; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index == -1 || co->index >= serial_pxa_reg.nr) + co->index = 0; + up = &serial_pxa_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_pxa_console = { + .name = "ttyS", + .write = serial_pxa_console_write, + .device = uart_console_device, + .setup = serial_pxa_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_pxa_reg, +}; + +static int __init +serial_pxa_console_init(void) +{ + register_console(&serial_pxa_console); + return 0; +} + +console_initcall(serial_pxa_console_init); + +#define PXA_CONSOLE &serial_pxa_console +#else +#define PXA_CONSOLE NULL +#endif + +struct uart_ops serial_pxa_pops = { + .tx_empty = serial_pxa_tx_empty, + .set_mctrl = serial_pxa_set_mctrl, + .get_mctrl = serial_pxa_get_mctrl, + .stop_tx = serial_pxa_stop_tx, + .start_tx = serial_pxa_start_tx, + .stop_rx = serial_pxa_stop_rx, + .enable_ms = serial_pxa_enable_ms, + .break_ctl = serial_pxa_break_ctl, + .startup = serial_pxa_startup, + .shutdown = serial_pxa_shutdown, + .set_termios = serial_pxa_set_termios, + .pm = serial_pxa_pm, + .type = serial_pxa_type, + .release_port = serial_pxa_release_port, + .request_port = serial_pxa_request_port, + .config_port = serial_pxa_config_port, + .verify_port = serial_pxa_verify_port, +}; + +static struct uart_pxa_port serial_pxa_ports[] = { + { /* FFUART */ + .name = "FFUART", + .cken = CKEN6_FFUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&FFUART, + .mapbase = __PREG(FFUART), + .irq = IRQ_FFUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = 0, + }, + } , + { /* BTUART */ + .name = "BTUART", + .cken = CKEN7_BTUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&BTUART, + .mapbase = __PREG(BTUART), + .irq = IRQ_BTUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = 1, + }, + } , + { /* STUART */ + .name = "STUART", + .cken = CKEN5_STUART, + .port = { + .type = PORT_PXA, + .iotype = UPIO_MEM, + .membase = (void *)&STUART, + .mapbase = __PREG(STUART), + .irq = IRQ_STUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .ops = &serial_pxa_pops, + .line = 2, + }, + } + +}; + +static struct uart_driver serial_pxa_reg = { + .owner = THIS_MODULE, + .driver_name = "PXA serial", + .devfs_name = "tts/", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = ARRAY_SIZE(serial_pxa_ports), + .cons = PXA_CONSOLE, +}; + +static int serial_pxa_suspend(struct device *_dev, u32 state, u32 level) +{ + struct uart_pxa_port *sport = dev_get_drvdata(_dev); + + if (sport && level == SUSPEND_DISABLE) + uart_suspend_port(&serial_pxa_reg, &sport->port); + + return 0; +} + +static int serial_pxa_resume(struct device *_dev, u32 level) +{ + struct uart_pxa_port *sport = dev_get_drvdata(_dev); + + if (sport && level == RESUME_ENABLE) + uart_resume_port(&serial_pxa_reg, &sport->port); + + return 0; +} + +static int serial_pxa_probe(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + + serial_pxa_ports[dev->id].port.dev = _dev; + uart_add_one_port(&serial_pxa_reg, &serial_pxa_ports[dev->id].port); + dev_set_drvdata(_dev, &serial_pxa_ports[dev->id]); + return 0; +} + +static int serial_pxa_remove(struct device *_dev) +{ + struct uart_pxa_port *sport = dev_get_drvdata(_dev); + + dev_set_drvdata(_dev, NULL); + + if (sport) + uart_remove_one_port(&serial_pxa_reg, &sport->port); + + return 0; +} + +static struct device_driver serial_pxa_driver = { + .name = "pxa2xx-uart", + .bus = &platform_bus_type, + .probe = serial_pxa_probe, + .remove = serial_pxa_remove, + + .suspend = serial_pxa_suspend, + .resume = serial_pxa_resume, +}; + +int __init serial_pxa_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_pxa_reg); + if (ret != 0) + return ret; + + ret = driver_register(&serial_pxa_driver); + if (ret != 0) + uart_unregister_driver(&serial_pxa_reg); + + return ret; +} + +void __exit serial_pxa_exit(void) +{ + driver_unregister(&serial_pxa_driver); + uart_unregister_driver(&serial_pxa_reg); +} + +module_init(serial_pxa_init); +module_exit(serial_pxa_exit); + +MODULE_LICENSE("GPL"); + diff -Nur linux-2.6.9-intc1/drivers/usb/gadget/Kconfig linux-2.6.9-intc1-mih1/drivers/usb/gadget/Kconfig --- linux-2.6.9-intc1/drivers/usb/gadget/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/usb/gadget/Kconfig 2005-02-21 15:42:26.000000000 -0500 @@ -103,6 +103,9 @@ config USB_GADGET_PXA27X depends on ARCH_PXA && PXA27x bool "PXA27x" + help + Intel's USB Client support for PXA27x ARM processor. + For Mainstone and Glencoe, use this one for now. config USB_PXA27X bool diff -Nur linux-2.6.9-intc1/drivers/usb/gadget/pxa27x_udc.c linux-2.6.9-intc1-mih1/drivers/usb/gadget/pxa27x_udc.c --- linux-2.6.9-intc1/drivers/usb/gadget/pxa27x_udc.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/usb/gadget/pxa27x_udc.c 2005-02-21 15:48:54.000000000 -0500 @@ -58,6 +58,10 @@ #include #include +#ifdef CONFIG_MACH_MAINSTONE + #include +#endif + #include #include @@ -1390,7 +1394,9 @@ { udc_clear_mask_UDCCR(UDCCR_UDE); +#ifdef CONFIG_MACH_MAINSTONE MST_MSCWR2 &= ~(MST_MSCWR2_nUSBC_SC); +#endif /* Enable clock for USB device */ pxa_set_cken(CKEN11_USB, 1); @@ -2270,7 +2276,9 @@ udc_clear_mask_UDCCR(UDCCR_UDE); pxa_set_cken(CKEN11_USB, 0); +#ifdef CONFIG_MACH_MAINSTONE MST_MSCWR2 |= MST_MSCWR2_nUSBC_SC; +#endif } return 0; diff -Nur linux-2.6.9-intc1/drivers/usb/host/Kconfig linux-2.6.9-intc1-mih1/drivers/usb/host/Kconfig --- linux-2.6.9-intc1/drivers/usb/host/Kconfig 2004-10-18 17:55:28.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/usb/host/Kconfig 2005-02-21 15:52:04.000000000 -0500 @@ -62,6 +62,7 @@ controller doesn't use PCI, this is probably appropriate. For a PCI based system where you're not sure, the "lspci -v" entry will list the right "prog-if" for your USB controller(s): EHCI, OHCI, or UHCI. + This is also the driver you want to use with a PXA27x chip. To compile this driver as a module, choose M here: the module will be called ohci-hcd. diff -Nur linux-2.6.9-intc1/drivers/usb/host/ohci-pxa.c linux-2.6.9-intc1-mih1/drivers/usb/host/ohci-pxa.c --- linux-2.6.9-intc1/drivers/usb/host/ohci-pxa.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/usb/host/ohci-pxa.c 2005-02-26 19:53:46.000000000 -0500 @@ -105,8 +105,8 @@ while ( UHCHR & UHCHR_FSBIR ) ; - if (machine_is_mainstone()) - UHCHR |= (UHCHR_PCPL | UHCHR_PSPL); + if (machine_is_mainstone() || machine_is_glencoe()) + UHCHR |= (UHCHR_PCPL | UHCHR_PSPL); // Set Signal Polarity to Active Low UHCHR &= ~UHCHR_SSEP3; UHCHR &= ~UHCHR_SSEP2; diff -Nur linux-2.6.9-intc1/drivers/video/pxafb.c linux-2.6.9-intc1-mih1/drivers/video/pxafb.c --- linux-2.6.9-intc1/drivers/video/pxafb.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/drivers/video/pxafb.c 2005-02-22 19:06:43.000000000 -0500 @@ -897,7 +897,6 @@ { ou_count++; } - //MST_LEDDAT1 = ou_count; LCSR = lcsr; return IRQ_HANDLED; @@ -931,6 +930,8 @@ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { fbi->state = state; //TODO __pxafb_lcd_power(fbi, 0); + __pxafb_lcd_power(fbi, 0); + __pxafb_backlight_power(fbi, 0); if(fbi->set_overlay_ctrlr_state) fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); pxafb_disable_controller(fbi); @@ -962,6 +963,8 @@ fbi->state = C_ENABLE; pxafb_enable_controller(fbi); //TODO __pxafb_lcd_power(fbi, 1); + __pxafb_lcd_power(fbi, 1); + __pxafb_backlight_power(fbi, 1); if(fbi->set_overlay_ctrlr_state) fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); } diff -Nur linux-2.6.9-intc1/fs/jffs2/build.c linux-2.6.9-intc1-mih1/fs/jffs2/build.c --- linux-2.6.9-intc1/fs/jffs2/build.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/build.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,17 +3,19 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ + * $Id: build.c,v 1.70 2005/02/28 08:21:05 dedekind Exp $ * */ #include #include #include +#include +#include #include "nodelist.h" static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); @@ -62,6 +64,7 @@ if (!child_ic) { printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", fd->name, fd->ino, ic->ino); + jffs2_mark_node_obsolete(c, fd->raw); continue; } @@ -88,21 +91,22 @@ int ret; int i; struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; /* First, scan the medium and build all the inode caches with lists of physical nodes */ - c->flags |= JFFS2_SB_FLAG_MOUNTING; + c->flags |= JFFS2_SB_FLAG_SCANNING; ret = jffs2_scan_medium(c); - c->flags &= ~JFFS2_SB_FLAG_MOUNTING; - + c->flags &= ~JFFS2_SB_FLAG_SCANNING; if (ret) - return ret; + goto exit; D1(printk(KERN_DEBUG "Scanned flash completely\n")); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); + c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); @@ -114,6 +118,7 @@ cond_resched(); } } + D1(printk(KERN_DEBUG "Pass 1 complete\n")); /* Next, scan for inodes with nlink == 0 and remove them. If @@ -135,9 +140,7 @@ D1(printk(KERN_DEBUG "Pass 2a starting\n")); while (dead_fds) { - struct jffs2_inode_cache *ic; - struct jffs2_full_dirent *fd = dead_fds; - + fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); @@ -152,7 +155,6 @@ /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { - struct jffs2_full_dirent *fd; D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); while(ic->scan_dents) { @@ -163,12 +165,27 @@ ic->scan_dents = NULL; cond_resched(); } + c->flags &= ~JFFS2_SB_FLAG_BUILDING; + D1(printk(KERN_DEBUG "Pass 3 complete\n")); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); + ret = 0; + +exit: + if (ret) { + for_each_inode(i, c, ic) { + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } + } + } + return ret; } @@ -179,9 +196,12 @@ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); - for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { + raw = ic->nodes; + while (raw != (void *)ic) { + struct jffs2_raw_node_ref *next = raw->next_in_ino; D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); jffs2_mark_node_obsolete(c, raw); + raw = next; } if (ic->scan_dents) { @@ -297,7 +317,10 @@ c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; - c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks); + else + c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); if (!c->blocks) return -ENOMEM; for (i=0; inr_blocks; i++) { @@ -310,6 +333,7 @@ c->blocks[i].used_size = 0; c->blocks[i].first_node = NULL; c->blocks[i].last_node = NULL; + c->blocks[i].bad_count = 0; } init_MUTEX(&c->alloc_sem); @@ -336,7 +360,11 @@ D1(printk(KERN_DEBUG "build_fs failed\n")); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) { + vfree(c->blocks); + } else { + kfree(c->blocks); + } return -EIO; } diff -Nur linux-2.6.9-intc1/fs/jffs2/compr.c linux-2.6.9-intc1-mih1/fs/jffs2/compr.c --- linux-2.6.9-intc1/fs/jffs2/compr.c 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/compr.c 2005-03-21 21:28:41.000000000 -0500 @@ -9,13 +9,13 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $ + * $Id: compr.c,v 1.44 2005/03/17 20:23:06 gleixner Exp $ * */ #include "compr.h" -static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(jffs2_compressor_list_lock); /* Available compressors are on this list */ static LIST_HEAD(jffs2_compressor_list); @@ -23,16 +23,6 @@ /* Actual compression mode */ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; -void jffs2_set_compression_mode(int mode) -{ - jffs2_compression_mode = mode; -} - -int jffs2_get_compression_mode(void) -{ - return jffs2_compression_mode; -} - /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; @@ -180,6 +170,11 @@ struct jffs2_compressor *this; int ret; + /* Older code had a bug where it would write non-zero 'usercompr' + fields. Deal with it. */ + if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) + comprtype &= 0xff; + switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ @@ -208,7 +203,7 @@ return ret; } } - printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype); + printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); spin_unlock(&jffs2_compressor_list_lock); return -EIO; } diff -Nur linux-2.6.9-intc1/fs/jffs2/compr.h linux-2.6.9-intc1-mih1/fs/jffs2/compr.h --- linux-2.6.9-intc1/fs/jffs2/compr.h 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/compr.h 2005-03-21 21:28:41.000000000 -0500 @@ -7,7 +7,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: compr.h,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $ + * $Id: compr.h,v 1.7 2005/03/17 20:23:06 gleixner Exp $ * */ @@ -41,9 +41,6 @@ #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 -void jffs2_set_compression_mode(int mode); -int jffs2_get_compression_mode(void); - struct jffs2_compressor { struct list_head list; int priority; /* used by prirority comr. mode */ diff -Nur linux-2.6.9-intc1/fs/jffs2/compr_rtime.c linux-2.6.9-intc1-mih1/fs/jffs2/compr_rtime.c --- linux-2.6.9-intc1/fs/jffs2/compr_rtime.c 2004-10-18 17:54:38.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/compr_rtime.c 2005-03-21 21:28:41.000000000 -0500 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_rtime.c,v 1.15 2005/03/17 20:23:06 gleixner Exp $ * * * Very simple lz77-ish encoder. @@ -29,8 +29,10 @@ #include "compr.h" /* _compress returns the compressed size, -1 if bigger */ -int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static int jffs2_rtime_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) { short positions[256]; int outpos = 0; @@ -69,8 +71,10 @@ } -int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen, void *model) +static int jffs2_rtime_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) { short positions[256]; int outpos = 0; diff -Nur linux-2.6.9-intc1/fs/jffs2/compr_zlib.c linux-2.6.9-intc1-mih1/fs/jffs2/compr_zlib.c --- linux-2.6.9-intc1/fs/jffs2/compr_zlib.c 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/compr_zlib.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff -Nur linux-2.6.9-intc1/fs/jffs2/dir.c linux-2.6.9-intc1-mih1/fs/jffs2/dir.c --- linux-2.6.9-intc1/fs/jffs2/dir.c 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/dir.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir.c,v 1.82 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: dir.c,v 1.85 2005/03/01 10:34:03 dedekind Exp $ * */ @@ -217,7 +217,6 @@ dentry->d_name.name, dentry->d_name.len); if (ret) { - jffs2_clear_inode(inode); make_bad_inode(inode); iput(inode); jffs2_free_raw_inode(ri); @@ -297,11 +296,11 @@ struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; - int ret; + int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target if it grows much more than this */ - if (strlen(target) > 254) + if (targetlen > 254) return -EINVAL; ri = jffs2_alloc_raw_inode(); @@ -315,7 +314,7 @@ * Just the node will do for now, though */ namelen = dentry->d_name.len; - ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL); + ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL); if (ret) { jffs2_free_raw_inode(ri); @@ -334,16 +333,16 @@ f = JFFS2_INODE_INFO(inode); - inode->i_size = strlen(target); + inode->i_size = targetlen; ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; - ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target))); + ri->data_crc = cpu_to_je32(crc32(0, target, targetlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); @@ -354,6 +353,20 @@ jffs2_clear_inode(inode); return PTR_ERR(fn); } + + /* We use f->dents field to store the target path. */ + f->dents = kmalloc(targetlen + 1, GFP_KERNEL); + if (!f->dents) { + printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); + up(&f->sem); + jffs2_complete_reservation(c); + jffs2_clear_inode(inode); + return -ENOMEM; + } + + memcpy(f->dents, target, targetlen + 1); + D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents)); + /* No data here. Only a metadata node, which will be obsoleted by the first data write */ diff -Nur linux-2.6.9-intc1/fs/jffs2/erase.c linux-2.6.9-intc1-mih1/fs/jffs2/erase.c --- linux-2.6.9-intc1/fs/jffs2/erase.c 2004-10-18 17:53:13.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/erase.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.60 2004/06/30 17:26:15 dbrown Exp $ + * $Id: erase.c,v 1.74 2005/03/20 17:46:20 dedekind Exp $ * */ @@ -33,7 +33,8 @@ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static void jffs2_erase_block(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { int ret; uint32_t bad_offset; @@ -43,9 +44,11 @@ jffs2_erase_succeeded(c, jeb); return; } + bad_offset = jeb->offset; #else /* Linux */ struct erase_info *instr; + D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size)); instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); @@ -231,7 +234,7 @@ continue; } - if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) { + if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) { /* It's in the block we're erasing */ struct jffs2_raw_node_ref *this; @@ -275,11 +278,8 @@ printk("\n"); }); - if (ic->nodes == (void *)ic) { - D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino)); + if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); - jffs2_free_inode_cache(ic); - } } static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) @@ -308,7 +308,7 @@ int ret; uint32_t bad_offset; - if (!jffs2_cleanmarker_oob(c)) { + if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) { marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n"); @@ -349,7 +349,7 @@ bad_offset += i; printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset); bad: - if (!jffs2_cleanmarker_oob(c)) + if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) jffs2_free_raw_node_ref(marker_ref); kfree(ebuf); bad2: @@ -385,7 +385,15 @@ jeb->used_size = 0; jeb->dirty_size = 0; jeb->wasted_size = 0; + } else if (c->cleanmarker_size == 0) { + jeb->first_node = jeb->last_node = NULL; + + jeb->free_size = c->sector_size; + jeb->used_size = 0; + jeb->dirty_size = 0; + jeb->wasted_size = 0; } else { + struct kvec vecs[1]; struct jffs2_unknown_node marker = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), @@ -394,8 +402,10 @@ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); - /* We only write the header; the rest was noise or padding anyway */ - ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); + vecs[0].iov_base = (unsigned char *) ▮ + vecs[0].iov_len = sizeof(marker); + ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); + if (ret) { printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); diff -Nur linux-2.6.9-intc1/fs/jffs2/file.c linux-2.6.9-intc1-mih1/fs/jffs2/file.c --- linux-2.6.9-intc1/fs/jffs2/file.c 2004-10-18 17:55:29.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/file.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $ + * $Id: file.c,v 1.100 2005/03/17 20:23:06 gleixner Exp $ * */ @@ -25,6 +25,11 @@ extern int generic_file_open(struct inode *, struct file *) __attribute__((weak)); extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak)); +static int jffs2_commit_write (struct file *filp, struct page *pg, + unsigned start, unsigned end); +static int jffs2_prepare_write (struct file *filp, struct page *pg, + unsigned start, unsigned end); +static int jffs2_readpage (struct file *filp, struct page *pg); int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) { @@ -65,7 +70,7 @@ .commit_write = jffs2_commit_write }; -int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) +static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); @@ -105,7 +110,7 @@ } -int jffs2_readpage (struct file *filp, struct page *pg) +static int jffs2_readpage (struct file *filp, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; @@ -116,7 +121,8 @@ return ret; } -int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end) +static int jffs2_prepare_write (struct file *filp, struct page *pg, + unsigned start, unsigned end) { struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); @@ -198,7 +204,8 @@ return ret; } -int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end) +static int jffs2_commit_write (struct file *filp, struct page *pg, + unsigned start, unsigned end) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple diff -Nur linux-2.6.9-intc1/fs/jffs2/fs.c linux-2.6.9-intc1-mih1/fs/jffs2/fs.c --- linux-2.6.9-intc1/fs/jffs2/fs.c 2004-10-18 17:54:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/fs.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $ + * $Id: fs.c,v 1.55 2005/03/18 10:17:18 gleixner Exp $ * */ @@ -20,10 +20,12 @@ #include #include #include +#include #include #include #include "nodelist.h" +static int jffs2_flash_setup(struct jffs2_sb_info *c); static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) { @@ -202,7 +204,7 @@ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); spin_unlock(&c->erase_completion_lock); @@ -426,7 +428,7 @@ inode->i_mode = jemode_to_cpu(ri->mode); inode->i_gid = je16_to_cpu(ri->gid); inode->i_uid = je16_to_cpu(ri->uid); - inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); inode->i_blksize = PAGE_SIZE; @@ -448,11 +450,15 @@ c = JFFS2_SB_INFO(sb); -#ifndef CONFIG_JFFS2_FS_NAND +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER if (c->mtd->type == MTD_NANDFLASH) { printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); return -EINVAL; } + if (c->mtd->type == MTD_DATAFLASH) { + printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n"); + return -EINVAL; + } #endif c->flash_size = c->mtd->size; @@ -463,11 +469,13 @@ */ c->sector_size = c->mtd->erasesize; blocks = c->flash_size / c->sector_size; - while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { - blocks >>= 1; - c->sector_size <<= 1; - } - + if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) { + while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { + blocks >>= 1; + c->sector_size <<= 1; + } + } + /* * Size alignment check */ @@ -533,7 +541,10 @@ out_nodes: jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + vfree(c->blocks); + else + kfree(c->blocks); out_inohash: kfree(c->inocache_list); out_wbuf: @@ -638,7 +649,7 @@ page_cache_release(pg); } -int jffs2_flash_setup(struct jffs2_sb_info *c) { +static int jffs2_flash_setup(struct jffs2_sb_info *c) { int ret = 0; if (jffs2_cleanmarker_oob(c)) { @@ -649,6 +660,19 @@ } /* add setups for other bizarre flashes here... */ + if (jffs2_nor_ecc(c)) { + ret = jffs2_nor_ecc_flash_setup(c); + if (ret) + return ret; + } + + /* and Dataflash */ + if (jffs2_dataflash(c)) { + ret = jffs2_dataflash_setup(c); + if (ret) + return ret; + } + return ret; } @@ -659,4 +683,12 @@ } /* add cleanups for other bizarre flashes here... */ + if (jffs2_nor_ecc(c)) { + jffs2_nor_ecc_flash_cleanup(c); + } + + /* and DataFlash */ + if (jffs2_dataflash(c)) { + jffs2_dataflash_cleanup(c); + } } diff -Nur linux-2.6.9-intc1/fs/jffs2/gc.c linux-2.6.9-intc1-mih1/fs/jffs2/gc.c --- linux-2.6.9-intc1/fs/jffs2/gc.c 2004-10-18 17:53:45.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/gc.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.136 2004/05/27 19:06:09 gleixner Exp $ + * $Id: gc.c,v 1.147 2005/03/20 21:43:22 dedekind Exp $ * */ @@ -50,6 +50,7 @@ put the clever wear-levelling algorithms. Eventually. */ /* We possibly want to favour the dirtier blocks more when the number of free blocks is low. */ +again: if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); nextlist = &c->bad_used_list; @@ -79,6 +80,13 @@ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); nextlist = &c->erasable_list; + } else if (!list_empty(&c->erasable_pending_wbuf_list)) { + /* There are blocks are wating for the wbuf sync */ + D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); + spin_unlock(&c->erase_completion_lock); + jffs2_flush_wbuf_pad(c); + spin_lock(&c->erase_completion_lock); + goto again; } else { /* Eep. All were empty */ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); @@ -103,7 +111,7 @@ ret->wasted_size = 0; } - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); return ret; } @@ -134,7 +142,7 @@ if (c->checked_ino > c->highest_ino) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); spin_unlock(&c->erase_completion_lock); BUG(); } @@ -359,10 +367,14 @@ spin_unlock(&c->inocache_lock); f = jffs2_gc_fetch_inode(c, inum, nlink); - if (IS_ERR(f)) - return PTR_ERR(f); - if (!f) - return 0; + if (IS_ERR(f)) { + ret = PTR_ERR(f); + goto release_sem; + } + if (!f) { + ret = 0; + goto release_sem; + } ret = jffs2_garbage_collect_live(c, jeb, raw, f); @@ -598,7 +610,7 @@ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); jffs2_free_raw_node_ref(nraw); } - if (!retried && (nraw == jffs2_alloc_raw_node_ref())) { + if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; @@ -624,6 +636,7 @@ jffs2_free_raw_node_ref(nraw); } + jffs2_free_raw_node_ref(nraw); if (!ret) ret = -EIO; goto out_node; @@ -633,10 +646,12 @@ /* Link into per-inode list. This is safe because of the ic state being INO_STATE_GC. Note that if we're doing this - for an inode which is in-code, the 'nraw' pointer is then + for an inode which is in-core, the 'nraw' pointer is then going to be fetched from ic->nodes by our caller. */ + spin_lock(&c->erase_completion_lock); nraw->next_in_ino = ic->nodes; ic->nodes = nraw; + spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); @@ -809,8 +824,7 @@ /* Doesn't matter if there's one in the same erase block. We're going to delete it too at the same time. */ - if ((raw->flash_offset & ~(c->sector_size-1)) == - (fd->raw->flash_offset & ~(c->sector_size-1))) + if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset)) continue; D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); diff -Nur linux-2.6.9-intc1/fs/jffs2/ioctl.c linux-2.6.9-intc1-mih1/fs/jffs2/ioctl.c --- linux-2.6.9-intc1/fs/jffs2/ioctl.c 2004-10-18 17:54:40.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/ioctl.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ + * $Id: ioctl.c,v 1.9 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff -Nur linux-2.6.9-intc1/fs/jffs2/Makefile linux-2.6.9-intc1-mih1/fs/jffs2/Makefile --- linux-2.6.9-intc1/fs/jffs2/Makefile 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/Makefile 2005-03-21 21:28:41.000000000 -0500 @@ -1,7 +1,7 @@ # # Makefile for the Linux Journalling Flash File System v2 (JFFS2) # -# $Id: Makefile.common,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $ +# $Id: Makefile.common,v 1.9 2005/02/09 09:23:53 pavlov Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o @@ -11,7 +11,7 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o -jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff -Nur linux-2.6.9-intc1/fs/jffs2/malloc.c linux-2.6.9-intc1-mih1/fs/jffs2/malloc.c --- linux-2.6.9-intc1/fs/jffs2/malloc.c 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/malloc.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ + * $Id: malloc.c,v 1.28 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff -Nur linux-2.6.9-intc1/fs/jffs2/nodelist.c linux-2.6.9-intc1-mih1/fs/jffs2/nodelist.c --- linux-2.6.9-intc1/fs/jffs2/nodelist.c 2004-10-18 17:54:31.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/nodelist.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $ + * $Id: nodelist.c,v 1.93 2005/02/27 23:01:32 dwmw2 Exp $ * */ @@ -92,42 +92,53 @@ } } +/* Returns first valid node after 'ref'. May return 'ref' */ +static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) +{ + while (ref && ref->next_in_ino) { + if (!ref_obsolete(ref)) + return ref; + D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); + ref = ref->next_in_ino; + } + return NULL; +} /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated with this ino, returning the former in order of version */ -int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, +int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, uint32_t *mctime_ver) { - struct jffs2_raw_node_ref *ref = f->inocache->nodes; + struct jffs2_raw_node_ref *ref, *valid_ref; struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; struct jffs2_full_dirent *fd, *ret_fd = NULL; - union jffs2_node_union node; size_t retlen; int err; *mctime_ver = 0; - - D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); - if (!f->inocache->nodes) { - printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino); - } + + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino)); spin_lock(&c->erase_completion_lock); - for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) { - /* Work out whether it's a data node or a dirent node */ - if (ref_obsolete(ref)) { - /* FIXME: On NAND flash we may need to read these */ - D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); - continue; - } + valid_ref = jffs2_first_valid_node(f->inocache->nodes); + + if (!valid_ref && (f->inocache->ino != 1)) + printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino); + + while (valid_ref) { /* We can hold a pointer to a non-obsolete node without the spinlock, but _obsolete_ nodes may disappear at any time, if the block - they're in gets erased */ + they're in gets erased. So if we mark 'ref' obsolete while we're + not holding the lock, it can go away immediately. For that reason, + we find the next valid node first, before processing 'ref'. + */ + ref = valid_ref; + valid_ref = jffs2_first_valid_node(ref->next_in_ino); spin_unlock(&c->erase_completion_lock); cond_resched(); @@ -183,7 +194,6 @@ err = -ENOMEM; goto free_out; } - memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1); fd->raw = ref; fd->version = je32_to_cpu(node.d.version); fd->ino = je32_to_cpu(node.d.ino); @@ -221,6 +231,7 @@ } fd->nhash = full_name_hash(fd->name, node.d.nsize); fd->next = NULL; + fd->name[node.d.nsize] = '\0'; /* Wheee. We now have a complete jffs2_full_dirent structure, with the name in it and everything. Link it into the list */ @@ -479,6 +490,8 @@ { struct jffs2_inode_cache **prev; D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino)); + BUG_ON(!new->ino); + spin_lock(&c->inocache_lock); prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; @@ -495,7 +508,7 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) { struct jffs2_inode_cache **prev; - D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino)); + D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino)); spin_lock(&c->inocache_lock); prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE]; @@ -507,6 +520,14 @@ *prev = old->next; } + /* Free it now unless it's in READING or CLEARING state, which + are the transitions upon read_inode() and clear_inode(). The + rest of the time we know nobody else is looking at it, and + if it's held by read_inode() or clear_inode() they'll free it + for themselves. */ + if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING) + jffs2_free_inode_cache(old); + spin_unlock(&c->inocache_lock); } @@ -519,7 +540,6 @@ this = c->inocache_list[i]; while (this) { next = this->next; - D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this)); jffs2_free_inode_cache(this); this = next; } diff -Nur linux-2.6.9-intc1/fs/jffs2/nodelist.h linux-2.6.9-intc1-mih1/fs/jffs2/nodelist.h --- linux-2.6.9-intc1/fs/jffs2/nodelist.h 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/nodelist.h 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.119 2004/05/26 12:28:12 gleixner Exp $ + * $Id: nodelist.h,v 1.129 2005/03/17 20:23:06 gleixner Exp $ * */ @@ -107,16 +107,6 @@ #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) -/* - Used for keeping track of deletion nodes &c, which can only be marked - as obsolete when the node which they mark as deleted has actually been - removed from the flash. -*/ -struct jffs2_raw_node_ref_list { - struct jffs2_raw_node_ref *rew; - struct jffs2_raw_node_ref_list *next; -}; - /* For each inode in the filesystem, we need to keep a record of nlink, because it would be a PITA to scan the whole directory tree at read_inode() time to calculate it, and to keep sufficient information @@ -145,16 +135,10 @@ #define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ #define INO_STATE_GC 4 /* GCing a 'pristine' node */ #define INO_STATE_READING 5 /* In read_inode() */ +#define INO_STATE_CLEARING 6 /* In clear_inode() */ #define INOCACHE_HASHSIZE 128 -struct jffs2_scan_info { - struct jffs2_full_dirent *dents; - struct jffs2_tmp_dnode_info *tmpnodes; - /* Latest i_size info */ - uint32_t version; - uint32_t isize; -}; /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. @@ -163,12 +147,11 @@ struct jffs2_full_dnode { struct jffs2_raw_node_ref *raw; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which the data of this node belongs */ uint32_t size; uint32_t frags; /* Number of fragments which currently refer to this node. When this reaches zero, - the node is obsolete. - */ + the node is obsolete. */ }; /* @@ -193,6 +176,7 @@ unsigned char type; unsigned char name[0]; }; + /* Fragments - used to build a map of which raw node to obtain data from for each part of the ino @@ -202,7 +186,7 @@ struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which this fragment belongs */ }; struct jffs2_eraseblock @@ -221,14 +205,6 @@ struct jffs2_raw_node_ref *last_node; struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */ - - /* For deletia. When a dirent node in this eraseblock is - deleted by a node elsewhere, that other node can only - be marked as obsolete when this block is actually erased. - So we keep a list of the nodes to mark as obsolete when - the erase is completed. - */ - // MAYBE struct jffs2_raw_node_ref_list *deletia; }; #define ACCT_SANITY_CHECK(c, jeb) do { \ @@ -281,9 +257,14 @@ else if (!ref_obsolete(ref2)) \ my_used_size += ref_totlen(c, jeb, ref2); \ if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \ - printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \ - ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \ - jeb->last_node, ref_offset(jeb->last_node)); \ + if (!ref2->next_phys) \ + printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \ + ref2, ref_offset(ref2), ref2->next_phys, \ + jeb->last_node, ref_offset(jeb->last_node)); \ + else \ + printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \ + ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \ + jeb->last_node, ref_offset(jeb->last_node)); \ paranoia_failed_dump(jeb); \ BUG(); \ } \ @@ -391,9 +372,9 @@ #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ -D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); +D2(void jffs2_print_frag_list(struct jffs2_inode_info *f)); void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); -int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, +int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, uint32_t *mctime_ver); @@ -480,10 +461,9 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ -void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* wbuf.c */ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); diff -Nur linux-2.6.9-intc1/fs/jffs2/nodemgmt.c linux-2.6.9-intc1-mih1/fs/jffs2/nodemgmt.c --- linux-2.6.9-intc1/fs/jffs2/nodemgmt.c 2004-10-18 17:53:41.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/nodemgmt.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.120 2005/03/20 17:46:20 dedekind Exp $ * */ @@ -308,7 +308,10 @@ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); #if 1 - if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { + /* we could get some obsolete nodes after nextblock was refiled + in wbuf.c */ + if ((c->nextblock || !ref_obsolete(new)) + &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) { printk(KERN_WARNING "argh. node added in wrong place\n"); jffs2_free_raw_node_ref(new); return -EINVAL; @@ -332,7 +335,7 @@ c->used_size += len; } - if (!jeb->free_size && !jeb->dirty_size) { + if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) { /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); @@ -399,6 +402,17 @@ } jeb = &c->blocks[blocknr]; + if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && + !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) { + /* Hm. This may confuse static lock analysis. If any of the above + three conditions is false, we're going to return from this + function without actually obliterating any nodes or freeing + any jffs2_raw_node_refs. So we don't need to stop erases from + happening, or protect against people holding an obsolete + jffs2_raw_node_ref without the erase_completion_lock. */ + down(&c->erase_free_sem); + } + spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { @@ -456,13 +470,14 @@ D1(ACCT_PARANOIA_CHECK(jeb)); - if (c->flags & JFFS2_SB_FLAG_MOUNTING) { - /* Mount in progress. Don't muck about with the block + if (c->flags & JFFS2_SB_FLAG_SCANNING) { + /* Flash scanning is in progress. Don't muck about with the block lists because they're not ready yet, and don't actually obliterate nodes that look obsolete. If they weren't marked obsolete on the flash at the time they _became_ obsolete, there was probably a reason for that. */ spin_unlock(&c->erase_completion_lock); + /* We didn't lock the erase_free_sem */ return; } @@ -515,43 +530,126 @@ spin_unlock(&c->erase_completion_lock); - if (!jffs2_can_mark_obsolete(c)) - return; - if (jffs2_is_readonly(c)) + if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) || + (c->flags & JFFS2_SB_FLAG_BUILDING)) { + /* We didn't lock the erase_free_sem */ return; + } + + /* The erase_free_sem is locked, and has been since before we marked the node obsolete + and potentially put its eraseblock onto the erase_pending_list. Thus, we know that + the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet + by jffs2_free_all_node_refs() in erase.c. Which is nice. */ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); - return; + goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); - return; + goto out_erase_sem; } /* XXX FIXME: This is ugly now */ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; + } + + /* Nodes which have been marked obsolete no longer need to be + associated with any inode. Remove them from the per-inode list. + + Note we can't do this for NAND at the moment because we need + obsolete dirent nodes to stay on the lists, because of the + horridness in jffs2_garbage_collect_deletion_dirent(). Also + because we delete the inocache, and on NAND we need that to + stay around until all the nodes are actually erased, in order + to stop us from giving the same inode number to another newly + created inode. */ + if (ref->next_in_ino) { + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref **p; + + spin_lock(&c->erase_completion_lock); + + ic = jffs2_raw_ref_to_ic(ref); + for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) + ; + + *p = ref->next_in_ino; + ref->next_in_ino = NULL; + + if (ic->nodes == (void *)ic && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + + spin_unlock(&c->erase_completion_lock); + } + + + /* Merge with the next node in the physical list, if there is one + and if it's also obsolete and if it doesn't belong to any inode */ + if (ref->next_phys && ref_obsolete(ref->next_phys) && + !ref->next_phys->next_in_ino) { + struct jffs2_raw_node_ref *n = ref->next_phys; + + spin_lock(&c->erase_completion_lock); + + ref->__totlen += n->__totlen; + ref->next_phys = n->next_phys; + if (jeb->last_node == n) jeb->last_node = ref; + if (jeb->gc_node == n) { + /* gc will be happy continuing gc on this node */ + jeb->gc_node=ref; + } + spin_unlock(&c->erase_completion_lock); + + jffs2_free_raw_node_ref(n); + } + + /* Also merge with the previous node in the list, if there is one + and that one is obsolete */ + if (ref != jeb->first_node ) { + struct jffs2_raw_node_ref *p = jeb->first_node; + + spin_lock(&c->erase_completion_lock); + + while (p->next_phys != ref) + p = p->next_phys; + + if (ref_obsolete(p) && !ref->next_in_ino) { + p->__totlen += ref->__totlen; + if (jeb->last_node == ref) { + jeb->last_node = p; + } + if (jeb->gc_node == ref) { + /* gc will be happy continuing gc on this node */ + jeb->gc_node=p; + } + p->next_phys = ref->next_phys; + jffs2_free_raw_node_ref(ref); + } + spin_unlock(&c->erase_completion_lock); } + out_erase_sem: + up(&c->erase_free_sem); } -#if CONFIG_JFFS2_FS_DEBUG > 0 +#if CONFIG_JFFS2_FS_DEBUG >= 2 void jffs2_dump_block_lists(struct jffs2_sb_info *c) { diff -Nur linux-2.6.9-intc1/fs/jffs2/os-linux.h linux-2.6.9-intc1-mih1/fs/jffs2/os-linux.h --- linux-2.6.9-intc1/fs/jffs2/os-linux.h 2004-10-18 17:53:13.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/os-linux.h 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2002-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $ + * $Id: os-linux.h,v 1.55 2005/03/17 20:23:06 gleixner Exp $ * */ @@ -97,9 +97,12 @@ #endif } + #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) +#define jffs2_is_writebuffered(c) (c->wbuf != NULL) -#ifndef CONFIG_JFFS2_FS_NAND +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER +#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) ) #define jffs2_can_mark_obsolete(c) (1) #define jffs2_cleanmarker_oob(c) (0) #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) @@ -115,10 +118,15 @@ #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL +#define jffs2_nor_ecc(c) (0) +#define jffs2_dataflash(c) (0) +#define jffs2_nor_ecc_flash_setup(c) (0) +#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) -#else /* NAND support present */ +#else /* NAND and/or ECC'd NOR support present */ -#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM) +#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size ) +#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM) #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) @@ -135,9 +143,20 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); void jffs2_wbuf_timeout(unsigned long data); void jffs2_wbuf_process(void *data); +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); -#endif /* NAND */ + +#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC)) +int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c); +void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c); + +#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH) +int jffs2_dataflash_setup(struct jffs2_sb_info *c); +void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); + +#endif /* WRITEBUFFER */ /* erase.c */ static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) @@ -159,11 +178,7 @@ extern struct inode_operations jffs2_file_inode_operations; extern struct address_space_operations jffs2_file_address_operations; int jffs2_fsync(struct file *, struct dentry *, int); -int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); -int jffs2_readpage (struct file *, struct page *); -int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); -int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); /* ioctl.c */ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); @@ -194,7 +209,6 @@ void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); -int jffs2_flash_setup(struct jffs2_sb_info *c); void jffs2_flash_cleanup(struct jffs2_sb_info *c); diff -Nur linux-2.6.9-intc1/fs/jffs2/pushpull.h linux-2.6.9-intc1-mih1/fs/jffs2/pushpull.h --- linux-2.6.9-intc1/fs/jffs2/pushpull.h 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/pushpull.h 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff -Nur linux-2.6.9-intc1/fs/jffs2/read.c linux-2.6.9-intc1-mih1/fs/jffs2/read.c --- linux-2.6.9-intc1/fs/jffs2/read.c 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/read.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $ + * $Id: read.c,v 1.39 2005/03/01 10:34:03 dedekind Exp $ * */ @@ -174,7 +174,7 @@ if (frag) { D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); holesize = min(holesize, frag->ofs - offset); - D1(jffs2_print_frag_list(f)); + D2(jffs2_print_frag_list(f)); } D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); memset(buf, 0, holesize); @@ -214,33 +214,3 @@ return 0; } -/* Core function to read symlink target. */ -char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) -{ - char *buf; - int ret; - - down(&f->sem); - - if (!f->metadata) { - printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino); - up(&f->sem); - return ERR_PTR(-EINVAL); - } - buf = kmalloc(f->metadata->size+1, GFP_USER); - if (!buf) { - up(&f->sem); - return ERR_PTR(-ENOMEM); - } - buf[f->metadata->size]=0; - - ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size); - - up(&f->sem); - - if (ret) { - kfree(buf); - return ERR_PTR(ret); - } - return buf; -} diff -Nur linux-2.6.9-intc1/fs/jffs2/readinode.c linux-2.6.9-intc1-mih1/fs/jffs2/readinode.c --- linux-2.6.9-intc1/fs/jffs2/readinode.c 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/readinode.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $ + * $Id: readinode.c,v 1.119 2005/03/01 10:34:03 dedekind Exp $ * */ @@ -22,7 +22,7 @@ static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); -#if CONFIG_JFFS2_FS_DEBUG >= 1 +#if CONFIG_JFFS2_FS_DEBUG >= 2 static void jffs2_print_fragtree(struct rb_root *list, int permitbug) { struct jffs2_node_frag *this = frag_first(list); @@ -56,7 +56,9 @@ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); } } +#endif +#if CONFIG_JFFS2_FS_DEBUG >= 1 static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; @@ -225,7 +227,7 @@ If so, both 'this' and the new node get marked REF_NORMAL so the GC can take a look. */ - if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { + if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); @@ -510,7 +512,7 @@ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink)); /* Grab all nodes relevant to this ino */ - ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret); @@ -621,6 +623,40 @@ case. */ if (!je32_to_cpu(latest_node->isize)) latest_node->isize = latest_node->dsize; + + if (f->inocache->state != INO_STATE_CHECKING) { + /* Symlink's inode data is the target path. Read it and + * keep in RAM to facilitate quick follow symlink operation. + * We use f->dents field to store the target path, which + * is somewhat ugly. */ + f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); + if (!f->dents) { + printk(KERN_WARNING "Can't allocate %d bytes of memory " + "for the symlink target path cache\n", + je32_to_cpu(latest_node->csize)); + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ENOMEM; + } + + ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), + je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents); + + if (ret || retlen != je32_to_cpu(latest_node->csize)) { + if (retlen != je32_to_cpu(latest_node->csize)) + ret = -EIO; + kfree(f->dents); + f->dents = NULL; + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ret; + } + + ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0'; + D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n", + (char *)f->dents)); + } + /* fall through... */ case S_IFBLK: @@ -670,6 +706,9 @@ down(&f->sem); deleted = f->inocache && !f->inocache->nlink; + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING); + if (f->metadata) { if (deleted) jffs2_mark_node_obsolete(c, f->metadata->raw); @@ -678,16 +717,27 @@ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); - fds = f->dents; + /* For symlink inodes we us f->dents to store the target path name */ + if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) { + if (f->dents) { + kfree(f->dents); + f->dents = NULL; + } + } else { + fds = f->dents; - while(fds) { - fd = fds; - fds = fd->next; - jffs2_free_full_dirent(fd); + while(fds) { + fd = fds; + fds = fd->next; + jffs2_free_full_dirent(fd); + } } - if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) { jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + if (f->inocache->nodes == (void *)f->inocache) + jffs2_del_ino_cache(c, f->inocache); + } up(&f->sem); } diff -Nur linux-2.6.9-intc1/fs/jffs2/scan.c linux-2.6.9-intc1-mih1/fs/jffs2/scan.c --- linux-2.6.9-intc1/fs/jffs2/scan.c 2004-10-18 17:55:21.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/scan.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.110 2004/06/17 17:15:31 gleixner Exp $ + * $Id: scan.c,v 1.119 2005/02/17 17:51:13 dedekind Exp $ * */ #include @@ -19,7 +19,7 @@ #include #include "nodelist.h" -#define EMPTY_SCAN_SIZE 1024 +#define DEFAULT_EMPTY_SCAN_SIZE 1024 #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->dirty_size += _x; \ @@ -68,13 +68,21 @@ static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif return min; } + +static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { + if (sector_size < DEFAULT_EMPTY_SCAN_SIZE) + return sector_size; + else + return DEFAULT_EMPTY_SCAN_SIZE; +} + int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; @@ -104,6 +112,10 @@ else buf_size = PAGE_SIZE; + /* Respect kmalloc limitations */ + if (buf_size > 128*1024) + buf_size = 128*1024; + D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); flashbuf = kmalloc(buf_size, GFP_KERNEL); if (!flashbuf) @@ -156,11 +168,8 @@ case BLK_STATE_PARTDIRTY: /* Some data, but not full. Dirty list. */ - /* Except that we want to remember the block with most free space, - and stick it in the 'nextblock' position to start writing to it. - Later when we do snapshots, this must be the most recent block, - not the one with most free space. - */ + /* We want to remember the block with most free space + and stick it in the 'nextblock' position to start writing to it. */ if (jeb->free_size > min_free(c) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ @@ -219,7 +228,7 @@ c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { /* If we're going to start writing into a block which already contains data, and the end of the data isn't page-aligned, @@ -237,7 +246,7 @@ } #endif if (c->nr_erasing_blocks) { - if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { + if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); ret = -EIO; @@ -285,7 +294,7 @@ uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER int cleanmarkerfound = 0; #endif @@ -294,7 +303,7 @@ D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { int ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); @@ -315,7 +324,7 @@ if (!buf_size) { buf_len = c->sector_size; } else { - buf_len = EMPTY_SCAN_SIZE; + buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; @@ -325,11 +334,11 @@ ofs = 0; /* Scan only 4KiB of 0xFF before declaring it's empty */ - while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) + while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; - if (ofs == EMPTY_SCAN_SIZE) { -#ifdef CONFIG_JFFS2_FS_NAND + if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { /* scan oob, take care of cleanmarker */ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); @@ -342,7 +351,10 @@ } #endif D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); - return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ + if (c->cleanmarker_size == 0) + return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */ + else + return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ } if (ofs) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, @@ -421,8 +433,8 @@ /* If we're only checking the beginning of a block with a cleanmarker, bail now */ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && - c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) { - D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE)); + c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) { + D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } @@ -617,7 +629,7 @@ } if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size - && (!jeb->first_node || !jeb->first_node->next_in_ino) ) + && (!jeb->first_node || !jeb->first_node->next_phys) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ diff -Nur linux-2.6.9-intc1/fs/jffs2/super.c linux-2.6.9-intc1-mih1/fs/jffs2/super.c --- linux-2.6.9-intc1/fs/jffs2/super.c 2004-10-18 17:53:46.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/super.c 2005-03-22 00:01:30.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.99 2004/08/24 07:59:57 dwmw2 Exp $ + * $Id: super.c,v 1.105 2005/02/09 09:23:54 pavlov Exp $ * */ @@ -56,6 +56,16 @@ } } +static int jffs2_sync_fs(struct super_block *sb, int wait) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + + down(&c->alloc_sem); + jffs2_flush_wbuf_pad(c); + up(&c->alloc_sem); + return 0; +} + static struct super_operations jffs2_super_operations = { .alloc_inode = jffs2_alloc_inode, @@ -67,6 +77,7 @@ .remount_fs = jffs2_remount_fs, .clear_inode = jffs2_clear_inode, .dirty_inode = jffs2_dirty_inode, + .sync_fs = jffs2_sync_fs, }; static int jffs2_sb_compare(struct super_block *sb, void *data) @@ -266,7 +277,10 @@ up(&c->alloc_sem); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + vfree(c->blocks); + else + kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) @@ -288,6 +302,8 @@ .name = "jffs2", .get_sb = jffs2_get_sb, .kill_sb = jffs2_kill_sb, + /* added NCB */ + .fs_flags = FS_REQUIRES_DEV, }; static int __init init_jffs2_fs(void) @@ -295,7 +311,7 @@ int ret; printk(KERN_INFO "JFFS2 version 2.2." -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER " (NAND)" #endif " (C) 2001-2003 Red Hat, Inc.\n"); diff -Nur linux-2.6.9-intc1/fs/jffs2/symlink.c linux-2.6.9-intc1-mih1/fs/jffs2/symlink.c --- linux-2.6.9-intc1/fs/jffs2/symlink.c 2004-10-18 17:53:05.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/symlink.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $ + * $Id: symlink.c,v 1.16 2005/03/01 10:50:48 dedekind Exp $ * */ @@ -19,27 +19,45 @@ #include "nodelist.h" static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); -static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd); struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = jffs2_follow_link, - .put_link = jffs2_put_link, .setattr = jffs2_setattr }; static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) { - unsigned char *buf; - buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); - nd_set_link(nd, buf); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); + + /* + * We don't acquire the f->sem mutex here since the only data we + * use is f->dents which in case of the symlink inode points to the + * symlink's target path. + * + * 1. If we are here the inode has already built and f->dents has + * to point to the target path. + * 2. Nobody uses f->dents (if the inode is symlink's inode). The + * exception is inode freeing function which frees f->dents. But + * it can't be called while we are here and before VFS has + * stopped using our f->dents string which we provide by means of + * nd_set_link() call. + */ + + if (!f->dents) { + printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); + return -EIO; + } + D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents)); + + nd_set_link(nd, (char *)f->dents); + + /* + * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe + * since the only way that may cause f->dents to be changed is iput() operation. + * But VFS will not use f->dents after iput() has been called. + */ return 0; } -static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd) -{ - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - kfree(s); -} diff -Nur linux-2.6.9-intc1/fs/jffs2/wbuf.c linux-2.6.9-intc1-mih1/fs/jffs2/wbuf.c --- linux-2.6.9-intc1/fs/jffs2/wbuf.c 2004-10-18 17:53:44.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/wbuf.c 2005-03-21 21:28:41.000000000 -0500 @@ -4,12 +4,12 @@ * Copyright (C) 2001-2003 Red Hat, Inc. * Copyright (C) 2004 Thomas Gleixner * - * Created by David Woodhouse + * Created by David Woodhouse * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.70 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: wbuf.c,v 1.91 2005/03/18 09:58:06 dedekind Exp $ * */ @@ -83,7 +83,7 @@ struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ - OFNI_BS_2SFFJ(c)->s_dirt = 1; + jffs2_erase_pending_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; @@ -130,22 +130,11 @@ } } -/* Recover from failure to write wbuf. Recover the nodes up to the - * wbuf, not the one which we were starting to try to write. */ +#define REFILE_NOTEMPTY 0 +#define REFILE_ANYWAY 1 -static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) { - struct jffs2_eraseblock *jeb, *new_jeb; - struct jffs2_raw_node_ref **first_raw, **raw; - size_t retlen; - int ret; - unsigned char *buf; - uint32_t start, end, ofs, len; - - spin_lock(&c->erase_completion_lock); - - jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; - D1(printk("About to refile bad block at %08x\n", jeb->offset)); D2(jffs2_dump_block_lists(c)); @@ -158,7 +147,7 @@ D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset)); list_add(&jeb->list, &c->bad_used_list); } else { - BUG(); + BUG_ON(allow_empty == REFILE_NOTEMPTY); /* It has to have had some nodes or we couldn't be here */ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); @@ -175,6 +164,25 @@ ACCT_SANITY_CHECK(c,jeb); D1(ACCT_PARANOIA_CHECK(jeb)); +} + +/* Recover from failure to write wbuf. Recover the nodes up to the + * wbuf, not the one which we were starting to try to write. */ + +static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb, *new_jeb; + struct jffs2_raw_node_ref **first_raw, **raw; + size_t retlen; + int ret; + unsigned char *buf; + uint32_t start, end, ofs, len; + + spin_lock(&c->erase_completion_lock); + + jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + + jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ @@ -224,8 +232,12 @@ } /* Do the read... */ - ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); - if (ret == -EIO && retlen == c->wbuf_ofs - start) { + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); + else + ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); + + if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ ret = 0; } @@ -255,17 +267,16 @@ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len); if (ret) { printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); - if (buf) - kfree(buf); + kfree(buf); return; } if (end-start >= c->wbuf_pagesize) { - /* Need to do another write immediately. This, btw, - means that we'll be writing from 'buf' and not from - the wbuf. Since if we're writing from the wbuf there - won't be more than a wbuf full of data, now will - there? :) */ - + /* Need to do another write immediately, but it's possible + that this is just because the wbuf itself is completely + full, and there's nothing earlier read back from the + flash. Hence 'buf' isn't necessarily what we're writing + from. */ + unsigned char *rewrite_buf = buf?:c->wbuf; uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n", @@ -281,8 +292,11 @@ ret = -EIO; } else #endif + if (jffs2_cleanmarker_oob(c)) ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, - buf, NULL, c->oobinfo); + rewrite_buf, NULL, c->oobinfo); + else + ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ @@ -309,10 +323,10 @@ c->wbuf_len = (end - start) - towrite; c->wbuf_ofs = ofs + towrite; - memcpy(c->wbuf, buf + towrite, c->wbuf_len); + memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ - - kfree(buf); + if (buf) + kfree(buf); } else { /* OK, now we're left with the dregs in whichever buffer we're using */ if (buf) { @@ -392,14 +406,18 @@ 1: Pad, do not adjust nextblock free_size 2: Pad, adjust nextblock free_size */ +#define NOPAD 0 +#define PAD_NOACCOUNT 1 +#define PAD_ACCOUNTING 2 + static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { int ret; size_t retlen; - /* Nothing to do if not NAND flash. In particular, we shouldn't + /* Nothing to do if not write-buffering the flash. In particular, we shouldn't del_timer() the timer we never initialised. */ - if (jffs2_can_mark_obsolete(c)) + if (!jffs2_is_writebuffered(c)) return 0; if (!down_trylock(&c->alloc_sem)) { @@ -408,7 +426,7 @@ BUG(); } - if(!c->wbuf || !c->wbuf_len) + if (!c->wbuf_len) /* already checked c->wbuf above */ return 0; /* claim remaining space on the page @@ -417,8 +435,12 @@ if we have a switch to next page, we will not have enough remaining space for this. */ - if (pad) { + if (pad && !jffs2_dataflash(c)) { c->wbuf_len = PAD(c->wbuf_len); + + /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR + with 8 byte page size */ + memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); @@ -426,9 +448,6 @@ padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); - } else { - /* Pad with JFFS2_DIRTY_BITMASK */ - memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); } } /* else jffs2_flash_writev has actually filled in the rest of the @@ -444,8 +463,11 @@ ret = -EIO; } else #endif - ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); - + + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); + else + ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); if (ret || retlen != c->wbuf_pagesize) { if (ret) @@ -458,13 +480,13 @@ jffs2_wbuf_recover(c); - return ret; + return ret; } spin_lock(&c->erase_completion_lock); /* Adjust free size of the block if we padded. */ - if (pad) { + if (pad && !jffs2_dataflash(c)) { struct jffs2_eraseblock *jeb; jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; @@ -512,6 +534,9 @@ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino)); + if (!c->wbuf) + return 0; + down(&c->alloc_sem); if (!jffs2_wbuf_pending_for_ino(c, ino)) { D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); @@ -525,7 +550,13 @@ if (c->unchecked_size) { /* GC won't make any progress for a while */ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); - ret = __jffs2_flush_wbuf(c, 2); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + /* retry flushing wbuf in case jffs2_wbuf_recover + left some data in the wbuf */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { @@ -537,7 +568,13 @@ if (ret) { /* GC failed. Flush it with padding instead */ down(&c->alloc_sem); - ret = __jffs2_flush_wbuf(c, 2); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + /* retry flushing wbuf in case jffs2_wbuf_recover + left some data in the wbuf */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); break; } down(&c->alloc_sem); @@ -552,12 +589,29 @@ /* Pad write-buffer to end and write it, wasting space. */ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) { - return __jffs2_flush_wbuf(c, 1); -} + int ret; + + if (!c->wbuf) + return 0; + + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + /* retry - maybe wbuf recover left some data in wbuf. */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + up_write(&c->wbuf_sem); + return ret; +} +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER +#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) ) +#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) ) +#else #define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) #define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) +#endif + int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) { struct kvec outvecs[3]; @@ -572,9 +626,11 @@ uint32_t outvec_to = to; /* If not NAND flash, don't bother */ - if (!c->wbuf) + if (!jffs2_is_writebuffered(c)) return jffs2_flash_direct_writev(c, invecs, count, to, retlen); + down_write(&c->wbuf_sem); + /* If wbuf_ofs is not initialized, set it to target address */ if (c->wbuf_ofs == 0xFFFFFFFF) { c->wbuf_ofs = PAGE_DIV(to); @@ -582,22 +638,33 @@ memset(c->wbuf,0xff,c->wbuf_pagesize); } + /* Fixup the wbuf if we are moving to a new eraseblock. The checks below + fail for ECC'd NOR because cleanmarker == 16, so a block starts at + xxx0010. */ + if (jffs2_nor_ecc(c)) { + if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) { + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + memset(c->wbuf,0xff,c->wbuf_pagesize); + } + } + /* Sanity checks on target address. It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to write at the beginning of a new erase block. Anything else, and you die. New block starts at xxx000c (0-b = block header) */ - if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) { + if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { /* It's a write to a new block */ if (c->wbuf_len) { D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); - ret = jffs2_flush_wbuf_pad(c); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); *retlen = 0; - return ret; + goto exit; } } /* set pointer to new block */ @@ -623,7 +690,6 @@ invec = 0; outvec = 0; - /* Fill writebuffer first, if already in use */ if (c->wbuf_len) { uint32_t invec_ofs = 0; @@ -658,14 +724,14 @@ } /* write buffer is full, flush buffer */ - ret = __jffs2_flush_wbuf(c, 0); + ret = __jffs2_flush_wbuf(c, NOPAD); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); /* Retlen zero to make sure our caller doesn't mark the space dirty. We've already done everything that's necessary */ *retlen = 0; - return ret; + goto exit; } outvec_to += donelen; c->wbuf_ofs = outvec_to; @@ -709,19 +775,31 @@ if (splitvec != -1) { uint32_t remainder; - int ret; remainder = outvecs[splitvec].iov_len - split_ofs; outvecs[splitvec].iov_len = split_ofs; /* We did cross a page boundary, so we write some now */ - ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); + else + ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); + if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { /* At this point we have no problem, - c->wbuf is empty. + c->wbuf is empty. However refile nextblock to avoid + writing again to same address. */ - *retlen = donelen; - return ret; + struct jffs2_eraseblock *jeb; + + spin_lock(&c->erase_completion_lock); + + jeb = &c->blocks[outvec_to / c->sector_size]; + jffs2_block_refile(c, jeb, REFILE_ANYWAY); + + *retlen = 0; + spin_unlock(&c->erase_completion_lock); + goto exit; } donelen += wbuf_retlen; @@ -760,7 +838,11 @@ if (c->wbuf_len && ino) jffs2_wbuf_dirties_inode(c, ino); - return 0; + ret = 0; + +exit: + up_write(&c->wbuf_sem); + return ret; } /* @@ -771,7 +853,7 @@ { struct kvec vecs[1]; - if (jffs2_can_mark_obsolete(c)) + if (!jffs2_is_writebuffered(c)) return c->mtd->write(c->mtd, ofs, len, retlen, buf); vecs[0].iov_base = (unsigned char *) buf; @@ -787,47 +869,55 @@ loff_t orbf = 0, owbf = 0, lwbf = 0; int ret; + if (!jffs2_is_writebuffered(c)) + return c->mtd->read(c->mtd, ofs, len, retlen, buf); + /* Read flash */ - if (!jffs2_can_mark_obsolete(c)) { + if (jffs2_cleanmarker_oob(c)) ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); + else + ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); - if ( (ret == -EIO) && (*retlen == len) ) { - printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", - len, ofs); - /* - * We have the raw data without ECC correction in the buffer, maybe - * we are lucky and all data or parts are correct. We check the node. - * If data are corrupted node check will sort it out. - * We keep this block, it will fail on write or erase and the we - * mark it bad. Or should we do that now? But we should give him a chance. - * Maybe we had a system crash or power loss before the ecc write or - * a erase was completed. - * So we return success. :) - */ - ret = 0; - } - } else - return c->mtd->read(c->mtd, ofs, len, retlen, buf); + if ( (ret == -EBADMSG) && (*retlen == len) ) { + printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", + len, ofs); + /* + * We have the raw data without ECC correction in the buffer, maybe + * we are lucky and all data or parts are correct. We check the node. + * If data are corrupted node check will sort it out. + * We keep this block, it will fail on write or erase and the we + * mark it bad. Or should we do that now? But we should give him a chance. + * Maybe we had a system crash or power loss before the ecc write or + * a erase was completed. + * So we return success. :) + */ + ret = 0; + } /* if no writebuffer available or write buffer empty, return */ if (!c->wbuf_pagesize || !c->wbuf_len) - return ret; + return ret;; /* if we read in a different block, return */ - if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) - return ret; + if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs)) + return ret; + + /* Lock only if we have reason to believe wbuf contains relevant data, + so that checking an erased block during wbuf recovery space allocation + does not deadlock. */ + down_read(&c->wbuf_sem); if (ofs >= c->wbuf_ofs) { owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ - return ret; + goto exit; lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ if (lwbf > len) lwbf = len; } else { orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ if (orbf > len) /* is write beyond write buffer ? */ - return ret; + goto exit; lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; @@ -835,6 +925,8 @@ if (lwbf > 0) memcpy(buf+orbf,c->wbuf+owbf,lwbf); +exit: + up_read(&c->wbuf_sem); return ret; } @@ -1032,7 +1124,7 @@ }; -int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) +static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { struct nand_oobinfo *oinfo = &c->mtd->oobinfo; @@ -1047,7 +1139,7 @@ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); /* Get the position of the free bytes */ - if (!oinfo->oobfree[0][0]) { + if (!oinfo->oobfree[0][1]) { printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); return -ENOSPC; } @@ -1079,9 +1171,9 @@ int res; /* Initialise write buffer */ + init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->oobblock; c->wbuf_ofs = 0xFFFFFFFF; - c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) @@ -1105,3 +1197,44 @@ { kfree(c->wbuf); } + +int jffs2_dataflash_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; /* No cleanmarkers needed */ + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + c->wbuf_pagesize = c->sector_size; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize); + + return 0; +} + +void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} + +int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) { + /* Cleanmarker is actually larger on the flashes */ + c->cleanmarker_size = 16; + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + c->wbuf_pagesize = c->mtd->eccsize; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + return 0; +} + +void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} diff -Nur linux-2.6.9-intc1/fs/jffs2/write.c linux-2.6.9-intc1-mih1/fs/jffs2/write.c --- linux-2.6.9-intc1/fs/jffs2/write.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/write.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.85 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: write.c,v 1.91 2005/03/01 10:34:03 dedekind Exp $ * */ @@ -136,6 +136,15 @@ raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; + if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) { + BUG_ON(!retried); + D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, " + "highest version %d -> updating dnode\n", + je32_to_cpu(ri->version), f->highest_version)); + ri->version = cpu_to_je32(++f->highest_version); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + } + ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:f->inocache->ino); @@ -213,8 +222,10 @@ jffs2_add_physical_node_ref(c, raw); /* Link into per-inode list */ + spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; + spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), @@ -278,6 +289,16 @@ raw->__totlen = PAD(sizeof(*rd)+namelen); raw->next_phys = NULL; + if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) { + BUG_ON(!retried); + D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, " + "highest version %d -> updating dirent\n", + je32_to_cpu(rd->version), f->highest_version)); + rd->version = cpu_to_je32(++f->highest_version); + fd->version = je32_to_cpu(rd->version); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + } + ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); if (ret || (retlen != sizeof(*rd) + namelen)) { @@ -333,8 +354,10 @@ raw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, raw); + spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; + spin_unlock(&c->erase_completion_lock); if (retried) { ACCT_SANITY_CHECK(c,NULL); @@ -621,20 +644,23 @@ down(&dead_f->sem); - while (dead_f->dents) { - /* There can be only deleted ones */ - fd = dead_f->dents; - - dead_f->dents = fd->next; - - if (fd->ino) { - printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", - dead_f->inocache->ino, fd->name, fd->ino); - } else { - D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); + if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { + while (dead_f->dents) { + /* There can be only deleted ones */ + fd = dead_f->dents; + + dead_f->dents = fd->next; + + if (fd->ino) { + printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", + dead_f->inocache->ino, fd->name, fd->ino); + } else { + D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", + fd->name, dead_f->inocache->ino)); + } + jffs2_mark_node_obsolete(c, fd->raw); + jffs2_free_full_dirent(fd); } - jffs2_mark_node_obsolete(c, fd->raw); - jffs2_free_full_dirent(fd); } dead_f->inocache->nlink--; diff -Nur linux-2.6.9-intc1/fs/jffs2/writev.c linux-2.6.9-intc1-mih1/fs/jffs2/writev.c --- linux-2.6.9-intc1/fs/jffs2/writev.c 2004-10-18 17:54:30.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/jffs2/writev.c 2005-03-21 21:28:41.000000000 -0500 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: writev.c,v 1.6 2004/11/16 20:36:12 dwmw2 Exp $ * */ diff -Nur linux-2.6.9-intc1/fs/Kconfig linux-2.6.9-intc1-mih1/fs/Kconfig --- linux-2.6.9-intc1/fs/Kconfig 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/fs/Kconfig 2005-03-21 21:28:41.000000000 -0500 @@ -1107,7 +1107,7 @@ config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" - select CRC32 + select CRC32 depends on MTD help JFFS2 is the second generation of the Journalling Flash File System @@ -1135,21 +1135,21 @@ If reporting bugs, please try to have available a full dump of the messages at debug level 1 while the misbehaviour was occurring. -config JFFS2_FS_NAND - bool "JFFS2 support for NAND flash" +config JFFS2_FS_WRITEBUFFER + bool "JFFS2 write-buffering support" depends on JFFS2_FS - default n + default y help - This enables the support for NAND flash in JFFS2. NAND is a newer - type of flash chip design than the traditional NOR flash, with - higher density but a handful of characteristics which make it more - interesting for the file system to use. + This enables the write-buffering support in JFFS2. - Say 'N' unless you have NAND flash. + This functionality is required to support JFFS2 on the following + types of flash devices: + - NAND flash + - NOR flash with transparent ECC + - DataFlash config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" - depends on JFFS2_FS default n help Enabling this option allows you to explicitly choose which diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/audio.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/audio.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/audio.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/audio.h 2005-04-02 00:56:19.000000000 -0500 @@ -0,0 +1,16 @@ +#ifndef __ASM_ARCH_AUDIO_H__ +#define __ASM_ARCH_AUDIO_H__ + +#include +#include +#include + +typedef struct { + int (*startup)(snd_pcm_substream_t *, void *); + void (*shutdown)(snd_pcm_substream_t *, void *); + void (*suspend)(void *); + void (*resume)(void *); + void *priv; +} pxa2xx_audio_ops_t; + +#endif diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/glencoe.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/glencoe.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/glencoe.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/glencoe.h 2005-04-21 20:18:25.000000000 -0400 @@ -0,0 +1,72 @@ +/* + * linux/include/asm-arm/arch-pxa/glencoe.h + * Author: Matthias Ihmig + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ASM_ARCH_GLENCOE_H +#define ASM_ARCH_GLENCOE_H + +/* Glencoe specific GPIO functions and assignments*/ + +#define GLN_GPIO_GPIO0 0 /* IN: General purpose input */ +#define GLN_GPIO_SLEEPWAKE 1 /* IN: Sleep/Wake button */ + +#define GLN_GPIO_nGEM_WAKE 9 /* OUT: GEM module wake up output */ +#define GLN_GPIO_nGEM_SLEEP 10 /* IN: GEM module sleep input, 0=module asleep, 1=module awake*/ +#define GLN_GPIO_nGEM_RST 11 /* OUT: GEM module reset, 0=in reset, 1=out of reset */ +#define GLN_GPIO_nGEM_PWR_DWN 12 /* OUT: GEM module power, 0=power off, 1=power on */ + +#define GLN_GPIO_KP_DKIN7 13 /* IN: Keypad Direct Key input 7*/ +#define GLN_GPIO_LCD_BRIGHTNESS 16 /* OUT: LCD Brightness output - PWM control */ + +#define GLN_GPIO_nUSIM_CD_CPU 17 /* IN: USIM card detect for when CPU is connected to SIM card, 0=card inserted, 1=no SIM card inserted */ +#define GLN_GPIO_MMC_PWR_ON 22 /* OUT: MMC/SD card power, 0=off, 1=on */ +#define GLN_GPIO_nMMC_CARD_DET 27 /* IN: MMC/SD card detect input, 0=card inserted, 1=card not inserted */ +#define GLN_GPIO_nGREEN_LED 33 /* OUT: Green general purpose LED, 0=on, 1=off */ +#define GLN_GPIO_nBLUE_LED 80 /* OUT: Blue general purpose LED, 0=on, 1=off */ +#define GLN_GPIO_BL_PWR_ON 86 /* OUT: Blacklight power control, 0=off, 1=on */ +#define GLN_GPIO_LCD_PWR_ON 87 /* OUT: LCD power control, 0=off, 1=on */ + +#define GLN_GPIO_nCF1_IRQ 100 /* IN: Compact Flash card 1 interrupt, 0=pending, 1=no int pending */ +#define GLN_GPIO_CF1_RST 101 /* OUT: Compact Flash card 1 reset, 0=card not in reset, 1=card in reset */ +#define GLN_GPIO_CF_PWR_ON 102 /* OUT: Compact Flash power, 0=power off, 1=power on */ +#define GLN_GPIO_nCF1_CARD_DET 103 /* IN: Compact Flash card 1 detect, 0=card inserted, 1=no card inserted */ + +#define GLN_GPIO_nCF2_IRQ 104 /* IN: Compact Flash card 2 interrupt, 0=pending, 1=no int pending */ +#define GLN_GPIO_CF2_RST 105 /* OUT: Compact Flash card 2 reset, 0=card not in reset, 1=card in reset */ +#define GLN_GPIO_nCF2_CARD_DET 106 /* IN: Compact Flash card 2 detect, 0=card inserted, 1=no card inserted */ + +#define GLN_GPIO_nAC97_INT 107 /* IN: AC97 codec interrupt, 0=pending, 1=no int pending */ +#define GLN_GPIO_nAUDIO_PWR_DWN 108 /* OUT: Audio subsection power down, 0=powered on, 1=power off */ + +// PWM registers for backlight +#define GLN_BACKLIGHT_PWM_PERVAL PWM_PERVAL0 +#define GLN_BACKLIGHT_PWM_PWDUTY PWM_PWDUTY0 +#define GLN_BACKLIGHT_PWM_CTRL PWM_CTRL0 +#define GLN_BACKLIGHT_PWM_MD GPIO16_PWM0_MD +#define GLN_BACKLIGHT_PWM_CKEN CKEN0_PWM0 + + +/* + * LED macros + */ + +#define GLN_SW_GREEN_LED_OFF (GPIO_set(GLN_GPIO_nGREEN_LED)) +#define GLN_SW_GREEN_LED_ON (GPIO_clr(GLN_GPIO_nGREEN_LED)) +#define GLN_SW_BLUE_LED_OFF (GPIO_set(GLN_GPIO_nBLUE_LED)) +#define GLN_SW_BLUE_LED_ON (GPIO_clr(GLN_GPIO_nBLUE_LED)) +#define GLN_SW_BL_PWR_ON (GPIO_set(GLN_GPIO_BL_PWR_ON)) +#define GLN_SW_BL_PWR_OFF (GPIO_clr(GLN_GPIO_BL_PWR_ON)) +#define GLN_SW_LCD_PWR_ON (GPIO_set(GLN_GPIO_BL_PWR_ON)) +#define GLN_SW_LCD_PWR_OFF (GPIO_clr(GLN_GPIO_BL_PWR_ON)) + +#define HEARTBEAT_LED_ON GLN_SW_GREEN_LED_ON +#define HEARTBEAT_LED_OFF GLN_SW_GREEN_LED_OFF +#define SYS_BUSY_LED_ON GLN_SW_BLUE_LED_ON +#define SYS_BUSY_LED_OFF GLN_SW_BLUE_LED_OFF + +#endif diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/hardware.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/hardware.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/hardware.h 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/hardware.h 2005-02-27 12:52:01.000000000 -0500 @@ -101,4 +101,8 @@ #include "mainstone.h" #endif +#ifdef CONFIG_MACH_GLENCOE +#include "glencoe.h" +#endif + #endif /* _ASM_ARCH_HARDWARE_H */ diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/irqs.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/irqs.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/irqs.h 2004-10-18 17:53:21.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/irqs.h 2005-02-21 22:31:55.000000000 -0500 @@ -67,10 +67,14 @@ #define GPIO_2_x_TO_IRQ(x) \ PXA_IRQ((x) - 2 + PXA_INTERNAL_IRQS) + +// x=GPIO-Pin, return IRQ-number assigned to that GPIO pin #define IRQ_GPIO(x) (((x) < 2) ? (IRQ_GPIO0 + (x)) : GPIO_2_x_TO_IRQ(x)) #define IRQ_TO_GPIO_2_x(i) \ ((i) - IRQ_GPIO(2) + 2) + +// IRQ i occurred, return the corresponding GPIO pin #define IRQ_TO_GPIO(i) (((i) < IRQ_GPIO(2)) ? ((i) - IRQ_GPIO0) : IRQ_TO_GPIO_2_x(i)) #if defined(CONFIG_PXA25x) @@ -185,3 +189,11 @@ #define MAINSTONE_S1_STSCHG_IRQ MAINSTONE_IRQ(14) #define MAINSTONE_S1_IRQ MAINSTONE_IRQ(15) +// nAC97_IRQ = PEN_DOWN ! +#define GLENCOE_nAC97_IRQ IRQ_GPIO(GLN_GPIO_nAC97_INT) +#define GLENCOE_USIM_IRQ IRQ_GPIO(GLN_GPIO_nUSIM_CD_CPU) +#define GLENCOE_MMC_IRQ IRQ_GPIO(GLN_GPIO_nMMC_CARD_DET) +#define GLENCOE_S0_CD_IRQ IRQ_GPIO(GLN_GPIO_nCF1_CARD_DET) +#define GLENCOE_S0_IRQ IRQ_GPIO(GLN_GPIO_nCF1_IRQ) +#define GLENCOE_S1_CD_IRQ IRQ_GPIO(GLN_GPIO_nCF2_CARD_DET) +#define GLENCOE_S1_IRQ IRQ_GPIO(GLN_GPIO_nCF2_IRQ) diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/pxa-regs.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/pxa-regs.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/pxa-regs.h 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/pxa-regs.h 2005-03-16 20:49:04.000000000 -0500 @@ -172,12 +172,16 @@ #define DRCMRTXPCDR DRCMR12 #define DRCMRRXSSDR DRCMR13 #define DRCMRTXSSDR DRCMR14 +#define DRCMRRXSS2DR DRCMR15 +#define DRCMRTXSS2DR DRCMR16 #define DRCMRRXICDR DRCMR17 #define DRCMRTXICDR DRCMR18 #define DRCMRRXSTRBR DRCMR19 #define DRCMRTXSTTHR DRCMR20 #define DRCMRRXMMC DRCMR21 #define DRCMRTXMMC DRCMR22 +#define DRCMRRXSS3DR DRCMR66 +#define DRCMRTXSS3DR DRCMR67 #define DRCMRUDC(x) DRCMR((x) + 24) #define DRCMR_MAPVLD (1 << 7) /* Map Valid (read / write) */ @@ -442,6 +446,43 @@ #define ISR_SAD (1 << 9) /* slave address detected */ #define ISR_BED (1 << 10) /* bus error no ACK/NAK */ +#define SACR0_RFTH(x) (x << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_TFTH(x) (x << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */ +#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */ +#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */ +#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */ +#define SACR0_ENB (1 << 0) /* Enable I2S Link */ + +#define SACR1_ENLBF (1 << 5) /* Enable Loopback */ +#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */ +#define SACR1_DREC (1 << 3) /* Disable Recording Function */ +#define SACR1_AMSL (1 << 1) /* Specify Alternate Mode */ + +#define SASR0_I2SOFF (1 << 7) /* Controller Status */ +#define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */ +#define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */ +#define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */ +#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */ +#define SASR0_BSY (1 << 2) /* I2S Busy */ +#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */ +#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */ + +#define SADIV_3_058M 0x0c /* Serial Clock Divider 3.058MHz */ +#define SADIV_2_836M 0x0d /* 2.836 MHz */ +#define SADIV_1_405M 0x1a /* 1.405 MHz */ +#define SADIV_1_026M 0x24 /* 1.026 MHz */ +#define SADIV_702K 0x34 /* 702 kHz */ +#define SADIV_513K 0x48 /* 513 kHz */ + +#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */ +#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */ + +#define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */ +#define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */ +#define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */ +#define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */ + /* * Serial Audio Controller @@ -458,6 +499,43 @@ #define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */ #endif +#define SACR0_RFTH(x) (x << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_TFTH(x) (x << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */ +#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */ +#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */ +#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */ +#define SACR0_ENB (1 << 0) /* Enable I2S Link */ + +#define SACR1_ENLBF (1 << 5) /* Enable Loopback */ +#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */ +#define SACR1_DREC (1 << 3) /* Disable Recording Function */ +#define SACR1_AMSL (1 << 1) /* Specify Alternate Mode */ + +#define SASR0_I2SOFF (1 << 7) /* Controller Status */ +#define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */ +#define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */ +#define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */ +#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */ +#define SASR0_BSY (1 << 2) /* I2S Busy */ +#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */ +#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */ + +#define SADIV_3_058M 0x0c /* Serial Clock Divider 3.058MHz */ +#define SADIV_2_836M 0x0d /* 2.836 MHz */ +#define SADIV_1_405M 0x1a /* 1.405 MHz */ +#define SADIV_1_026M 0x24 /* 1.026 MHz */ +#define SADIV_702K 0x34 /* 702 kHz */ +#define SADIV_513K 0x48 /* 513 kHz */ + +#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */ +#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */ + +#define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */ +#define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */ +#define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */ +#define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */ + /* * AC97 Controller registers @@ -465,14 +543,18 @@ #define POCR __REG(0x40500000) /* PCM Out Control Register */ #define POCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */ +#define POCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */ #define PICR __REG(0x40500004) /* PCM In Control Register */ #define PICR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */ +#define PICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */ #define MCCR __REG(0x40500008) /* Mic In Control Register */ #define MCCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */ +#define MCCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */ #define GCR __REG(0x4050000C) /* Global Control Register */ +#define GCR_nDMAEN (1 << 24) /* non DMA Enable */ #define GCR_CDONE_IE (1 << 19) /* Command Done Interrupt Enable */ #define GCR_SDONE_IE (1 << 18) /* Status Done Interrupt Enable */ #define GCR_SECRDY_IEN (1 << 9) /* Secondary Ready Interrupt Enable */ @@ -486,12 +568,17 @@ #define POSR __REG(0x40500010) /* PCM Out Status Register */ #define POSR_FIFOE (1 << 4) /* FIFO error */ +#define POSR_FSR (1 << 2) /* FIFO Service Request */ #define PISR __REG(0x40500014) /* PCM In Status Register */ #define PISR_FIFOE (1 << 4) /* FIFO error */ +#define PISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */ +#define PISR_FSR (1 << 2) /* FIFO Service Request */ #define MCSR __REG(0x40500018) /* Mic In Status Register */ #define MCSR_FIFOE (1 << 4) /* FIFO error */ +#define MCSR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */ +#define MCSR_FSR (1 << 2) /* FIFO Service Request */ #define GSR __REG(0x4050001C) /* Global Status Register */ #define GSR_CDONE (1 << 19) /* Command Done */ @@ -504,9 +591,10 @@ #define GSR_PRIRES (1 << 10) /* Primary Resume Interrupt */ #define GSR_SCR (1 << 9) /* Secondary Codec Ready */ #define GSR_PCR (1 << 8) /* Primary Codec Ready */ -#define GSR_MINT (1 << 7) /* Mic In Interrupt */ +#define GSR_MCINT (1 << 7) /* Mic In Interrupt */ #define GSR_POINT (1 << 6) /* PCM Out Interrupt */ #define GSR_PIINT (1 << 5) /* PCM In Interrupt */ +#define GSR_ACOFFD (1 << 3) /* AC-link Shut Off Done */ #define GSR_MOINT (1 << 2) /* Modem Out Interrupt */ #define GSR_MIINT (1 << 1) /* Modem In Interrupt */ #define GSR_GSCI (1 << 0) /* Codec GPI Status Change Interrupt */ @@ -519,15 +607,20 @@ #define MOCR __REG(0x40500100) /* Modem Out Control Register */ #define MOCR_FEIE (1 << 3) /* FIFO Error */ +#define MOCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */ #define MICR __REG(0x40500108) /* Modem In Control Register */ #define MICR_FEIE (1 << 3) /* FIFO Error */ +#define MICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */ #define MOSR __REG(0x40500110) /* Modem Out Status Register */ #define MOSR_FIFOE (1 << 4) /* FIFO error */ +#define MOSR_FSR (1 << 2) /* FIFO Service Request */ #define MISR __REG(0x40500118) /* Modem In Status Register */ #define MISR_FIFOE (1 << 4) /* FIFO error */ +#define MISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */ +#define MISR_FSR (1 << 2) /* FIFO Service Request */ #define MODR __REG(0x40500140) /* Modem FIFO Data Register */ @@ -1107,6 +1200,13 @@ #define GPIO_bit(x) (1 << ((x) & 0x1f)) +/* get current bitlevel of GPIO Input pin, 0=pin low, !=0: pin high */ +#define GPIO_lev(x) (GPLR(x) & GPIO_bit(x)) +/* clear GPIO Output pin */ +#define GPIO_clr(x) (GPCR(x) = GPIO_bit(x)) +/* set GPIO Output pin */ +#define GPIO_set(x) (GPSR(x) = GPIO_bit(x)) + #ifdef CONFIG_PXA27x /* Interrupt Controller */ @@ -1177,6 +1277,7 @@ #define GPIO30_SDATA_OUT 30 /* AC97/I2S Sdata_out */ #define GPIO31_SYNC 31 /* AC97/I2S sync */ #define GPIO32_SDATA_IN1 32 /* AC97 Sdata_in1 */ +#define GPIO32_SYSCLK 32 /* I2S System Clock */ #define GPIO32_MMCCLK 32 /* MMC Clock (PXA270) */ #define GPIO33_nCS_5 33 /* chip select 5 */ #define GPIO34_FFRXD 34 /* FFUART receive */ @@ -1193,6 +1294,7 @@ #define GPIO43_BTTXD 43 /* BTUART transmit data */ #define GPIO44_BTCTS 44 /* BTUART clear to send */ #define GPIO45_BTRTS 45 /* BTUART request to send */ +#define GPIO45_AC97_SYSCLK 45 /* AC97 System Clock */ #define GPIO46_ICPRXD 46 /* ICP receive data */ #define GPIO46_STRXD 46 /* STD_UART receive data */ #define GPIO47_ICPTXD 47 /* ICP transmit data */ @@ -1241,6 +1343,10 @@ #define GPIO78_nCS_2 78 /* chip select 2 */ #define GPIO79_nCS_3 79 /* chip select 3 */ #define GPIO80_nCS_4 80 /* chip select 4 */ +#define GPIO81_NSCLK 81 /* NSSP clock */ +#define GPIO82_NSFRM 82 /* NSSP Frame */ +#define GPIO83_NSTXD 83 /* NSSP transmit */ +#define GPIO84_NSRXD 84 /* NSSP receive */ #define GPIO85_nPCE_1 85 /* Card Enable for Card Space (PXA27x) */ #define GPIO92_MMCDAT0 92 /* MMC DAT0 (PXA27x) */ #define GPIO109_MMCDAT1 109 /* MMC DAT1 (PXA27x) */ @@ -1250,6 +1356,8 @@ #define GPIO111_MMCCS1 111 /* MMC Chip Select 1 (PXA27x) */ #define GPIO112_MMCCMD 112 /* MMC CMD (PXA27x) */ #define GPIO113_AC97_RESET_N 113 /* AC97 NRESET on (PXA27x) */ +#define GPIO117_SCL 117 /* I2C Clock PXA27x */ +#define GPIO118_SDA 118 /* I2C Data PXA27x */ /* GPIO alternate function mode & direction */ @@ -1264,6 +1372,8 @@ #define GPIO_MD_MASK_NR 0x07f #define GPIO_MD_MASK_DIR 0x080 #define GPIO_MD_MASK_FN 0x300 +#define GPIO_DFLT_LOW 0x400 +#define GPIO_DFLT_HIGH 0x800 #define GPIO1_RTS_MD ( 1 | GPIO_ALT_FN_1_IN) #define GPIO6_MMCCLK_MD ( 6 | GPIO_ALT_FN_1_OUT) @@ -1287,14 +1397,16 @@ #define GPIO26_SRXD_MD (26 | GPIO_ALT_FN_1_IN) #define GPIO27_SEXTCLK_MD (27 | GPIO_ALT_FN_1_IN) #define GPIO28_BITCLK_AC97_MD (28 | GPIO_ALT_FN_1_IN) -#define GPIO28_BITCLK_I2S_MD (28 | GPIO_ALT_FN_2_IN) +#define GPIO28_BITCLK_IN_I2S_MD (28 | GPIO_ALT_FN_2_IN) +#define GPIO28_BITCLK_OUT_I2S_MD (28 | GPIO_ALT_FN_1_OUT) #define GPIO29_SDATA_IN_AC97_MD (29 | GPIO_ALT_FN_1_IN) #define GPIO29_SDATA_IN_I2S_MD (29 | GPIO_ALT_FN_2_IN) #define GPIO30_SDATA_OUT_AC97_MD (30 | GPIO_ALT_FN_2_OUT) #define GPIO30_SDATA_OUT_I2S_MD (30 | GPIO_ALT_FN_1_OUT) -#define GPIO31_SYNC_AC97_MD (31 | GPIO_ALT_FN_2_OUT) #define GPIO31_SYNC_I2S_MD (31 | GPIO_ALT_FN_1_OUT) +#define GPIO31_SYNC_AC97_MD (31 | GPIO_ALT_FN_2_OUT) #define GPIO32_SDATA_IN1_AC97_MD (32 | GPIO_ALT_FN_1_IN) +#define GPIO32_SYSCLK_I2S_MD (32 | GPIO_ALT_FN_1_OUT) #define GPIO32_MMCCLK_MD ( 32 | GPIO_ALT_FN_2_OUT) #define GPIO33_nCS_5_MD (33 | GPIO_ALT_FN_2_OUT) #define GPIO34_FFRXD_MD (34 | GPIO_ALT_FN_1_IN) @@ -1311,7 +1423,7 @@ #define GPIO43_BTTXD_MD (43 | GPIO_ALT_FN_2_OUT) #define GPIO44_BTCTS_MD (44 | GPIO_ALT_FN_1_IN) #define GPIO45_BTRTS_MD (45 | GPIO_ALT_FN_2_OUT) -#define GPIO45_SYSCLK_AC97_MD (45 | GPIO_ALT_FN_1_OUT) +#define GPIO45_SYSCLK_AC97_MD (45 | GPIO_ALT_FN_1_OUT) #define GPIO46_ICPRXD_MD (46 | GPIO_ALT_FN_1_IN) #define GPIO46_STRXD_MD (46 | GPIO_ALT_FN_2_IN) #define GPIO47_ICPTXD_MD (47 | GPIO_ALT_FN_2_OUT) @@ -1361,6 +1473,14 @@ #define GPIO79_nCS_3_MD (79 | GPIO_ALT_FN_2_OUT) #define GPIO79_pSKTSEL_MD (79 | GPIO_ALT_FN_1_OUT) #define GPIO80_nCS_4_MD (80 | GPIO_ALT_FN_2_OUT) +#define GPIO81_NSSP_CLK_OUT (81 | GPIO_ALT_FN_1_OUT) +#define GPIO81_NSSP_CLK_IN (81 | GPIO_ALT_FN_1_IN) +#define GPIO82_NSSP_FRM_OUT (82 | GPIO_ALT_FN_1_OUT) +#define GPIO82_NSSP_FRM_IN (82 | GPIO_ALT_FN_1_IN) +#define GPIO83_NSSP_TX (83 | GPIO_ALT_FN_1_OUT) +#define GPIO83_NSSP_RX (83 | GPIO_ALT_FN_2_IN) +#define GPIO84_NSSP_TX (84 | GPIO_ALT_FN_1_OUT) +#define GPIO84_NSSP_RX (84 | GPIO_ALT_FN_2_IN) #define GPIO85_nPCE_1_MD (85 | GPIO_ALT_FN_1_OUT) #define GPIO88_USBH1_PWR_MD (88 | GPIO_ALT_FN_1_IN) #define GPIO89_USBH1_PEN_MD (89 | GPIO_ALT_FN_2_OUT) @@ -1372,9 +1492,10 @@ #define GPIO110_MMCCS1_MD (111 | GPIO_ALT_FN_1_OUT) #define GPIO112_MMCCMD_MD (112 | GPIO_ALT_FN_1_OUT) #define GPIO113_AC97_RESET_N_MD (113 | GPIO_ALT_FN_2_OUT) -#define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_OUT) -#define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN) - +#define GPIO117_SCL_MD (117 | GPIO_ALT_FN_1_OUT) /* I2C Clock PXA27x Board */ +#define GPIO118_SDA_MD (118 | GPIO_ALT_FN_1_OUT) /* I2C Data PXA27x Board */ +#define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_OUT) /* for WM97xx driver */ +#define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN) /* * Power Manager @@ -1468,17 +1589,32 @@ #define RCSR_WDR (1 << 1) /* Watchdog Reset */ #define RCSR_HWR (1 << 0) /* Hardware Reset */ +#define PWER_GPIO(Nb) (1 << Nb) /* GPIO [0..15] wake-up enable */ +#define PWER_GPIO0 PWER_GPIO (0) /* GPIO [0] wake-up enable */ +#define PWER_GPIO1 PWER_GPIO (1) /* GPIO [1] wake-up enable */ +#define PWER_GPIO2 PWER_GPIO (2) /* GPIO [2] wake-up enable */ +#define PWER_GPIO3 PWER_GPIO (3) /* GPIO [3] wake-up enable */ +#define PWER_GPIO4 PWER_GPIO (4) /* GPIO [4] wake-up enable */ +#define PWER_GPIO5 PWER_GPIO (5) /* GPIO [5] wake-up enable */ +#define PWER_GPIO6 PWER_GPIO (6) /* GPIO [6] wake-up enable */ +#define PWER_GPIO7 PWER_GPIO (7) /* GPIO [7] wake-up enable */ +#define PWER_GPIO8 PWER_GPIO (8) /* GPIO [8] wake-up enable */ +#define PWER_GPIO9 PWER_GPIO (9) /* GPIO [9] wake-up enable */ +#define PWER_GPIO10 PWER_GPIO (10) /* GPIO [10] wake-up enable */ +#define PWER_GPIO11 PWER_GPIO (11) /* GPIO [11] wake-up enable */ +#define PWER_GPIO12 PWER_GPIO (12) /* GPIO [12] wake-up enable */ +#define PWER_GPIO13 PWER_GPIO (13) /* GPIO [13] wake-up enable */ +#define PWER_GPIO14 PWER_GPIO (14) /* GPIO [14] wake-up enable */ +#define PWER_GPIO15 PWER_GPIO (15) /* GPIO [15] wake-up enable */ +#define PWER_RTC 0x80000000 /* RTC alarm wake-up enable */ /* * SSP Serial Port Registers + * PXA250, PXA255, PXA26x and PXA27x SSP controllers are all slightly different. + * PXA255, PXA26x and PXA27x have extra ports, registers and bits. */ - -#define SSCR0 __REG(0x41000000) /* SSP Control Register 0 */ -#define SSCR1 __REG(0x41000004) /* SSP Control Register 1 */ -#define SSSR __REG(0x41000008) /* SSP Status Register */ -#define SSITR __REG(0x4100000C) /* SSP Interrupt Test Register */ -#define SSDR __REG(0x41000010) /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ - + + /* Common PXA2xx bits first */ #define SSCR0_DSS (0x0000000f) /* Data Size Select (mask) */ #define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */ #define SSCR0_FRF (0x00000030) /* FRame Format (mask) */ @@ -1508,6 +1644,201 @@ #define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ #define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ +#define SSCR0_TIM (1 << 23) /* Transmit FIFO Under Run Interrupt Mask */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO Over Run interrupt Mask */ +#define SSCR0_NCS (1 << 21) /* Network Clock Select */ +#define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */ + +/* extra bits in PXA255, PXA26x and PXA27x SSP ports */ +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */ +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */ +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */ +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */ +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */ +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */ +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */ +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */ +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */ +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */ +#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interupt Enable */ +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */ +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */ + +#define SSSR_BCE (1 << 23) /* Bit Count Error */ +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */ +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */ +#define SSSR_EOC (1 << 20) /* End Of Chain */ +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */ +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */ + +#define SSPSP_DMYSTOP(x) (x << 23) /* Dummy Stop */ +#define SSPSP_SFRMWDTH(x) (x << 16) /* Serial Frame Width */ +#define SSPSP_SFRMDLY(x) (x << 9) /* Serial Frame Delay */ +#define SSPSP_DMYSTRT(x) (x << 7) /* Dummy Start */ +#define SSPSP_STRTDLY(x) (x << 4) /* Start Delay */ +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */ +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_SCMODE(x) (x << 0) /* Serial Bit Rate Clock Mode */ + +#define SSCR0_TIM (1 << 23) /* Transmit FIFO Under Run Interrupt Mask */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO Over Run interrupt Mask */ +#define SSCR0_NCS (1 << 21) /* Network Clock Select */ +#define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */ + +/* extra bits in PXA255, PXA26x and PXA27x SSP ports */ +#define SSCR0_PSP (3 << 4) /* PSP - Programmable Serial Protocol */ +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */ +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */ +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */ +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */ +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */ +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */ +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */ +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */ +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */ +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */ +#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interupt Enable */ +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */ +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */ + +#define SSSR_BCE (1 << 23) /* Bit Count Error */ +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */ +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */ +#define SSSR_EOC (1 << 20) /* End Of Chain */ +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */ +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */ + +#define SSPSP_DMYSTOP(x) (x << 23) /* Dummy Stop */ +#define SSPSP_SFRMWDTH(x) (x << 16) /* Serial Frame Width */ +#define SSPSP_SFRMDLY(x) (x << 9) /* Serial Frame Delay */ +#define SSPSP_DMYSTRT(x) (x << 7) /* Dummy Start */ +#define SSPSP_STRTDLY(x) (x << 4) /* Start Delay */ +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */ +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_SCMODE(x) (x << 0) /* Serial Bit Rate Clock Mode */ + + +#define SSCR0_P1 __REG(0x41000000) /* SSP Port 1 Control Register 0 */ +#define SSCR1_P1 __REG(0x41000004) /* SSP Port 1 Control Register 1 */ +#define SSSR_P1 __REG(0x41000008) /* SSP Port 1 Status Register */ +#define SSITR_P1 __REG(0x4100000C) /* SSP Port 1 Interrupt Test Register */ +#define SSDR_P1 __REG(0x41000010) /* (Write / Read) SSP Port 1 Data Write Register/SSP Data Read Register */ + +/* Support existing PXA25x drivers */ +#define SSCR0 SSCR0_P1 /* SSP Control Register 0 */ +#define SSCR1 SSCR1_P1 /* SSP Control Register 1 */ +#define SSSR SSSR_P1 /* SSP Status Register */ +#define SSITR SSITR_P1 /* SSP Interrupt Test Register */ +#define SSDR SSDR_P1 /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ + +/* PXA27x ports */ +#if defined (CONFIG_PXA27x) +#define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ +#define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSCR0_P2 __REG(0x41700000) /* SSP Port 2 Control Register 0 */ +#define SSCR1_P2 __REG(0x41700004) /* SSP Port 2 Control Register 1 */ +#define SSSR_P2 __REG(0x41700008) /* SSP Port 2 Status Register */ +#define SSITR_P2 __REG(0x4170000C) /* SSP Port 2 Interrupt Test Register */ +#define SSDR_P2 __REG(0x41700010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ +#define SSTO_P2 __REG(0x41700028) /* SSP Port 2 Time Out Register */ +#define SSPSP_P2 __REG(0x4170002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSCR0_P3 __REG(0x41900000) /* SSP Port 3 Control Register 0 */ +#define SSCR1_P3 __REG(0x41900004) /* SSP Port 3 Control Register 1 */ +#define SSSR_P3 __REG(0x41900008) /* SSP Port 3 Status Register */ +#define SSITR_P3 __REG(0x4190000C) /* SSP Port 3 Interrupt Test Register */ +#define SSDR_P3 __REG(0x41900010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ +#define SSTO_P3 __REG(0x41900028) /* SSP Port 3 Time Out Register */ +#define SSPSP_P3 __REG(0x4190002C) /* SSP Port 3 Programmable Serial Protocol */ +#else /* PXA255 (only port 2) and PXA26x ports*/ +#define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ +#define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSCR0_P2 __REG(0x41400000) /* SSP Port 2 Control Register 0 */ +#define SSCR1_P2 __REG(0x41400004) /* SSP Port 2 Control Register 1 */ +#define SSSR_P2 __REG(0x41400008) /* SSP Port 2 Status Register */ +#define SSITR_P2 __REG(0x4140000C) /* SSP Port 2 Interrupt Test Register */ +#define SSDR_P2 __REG(0x41400010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ +#define SSTO_P2 __REG(0x41400028) /* SSP Port 2 Time Out Register */ +#define SSPSP_P2 __REG(0x4140002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSCR0_P3 __REG(0x41500000) /* SSP Port 3 Control Register 0 */ +#define SSCR1_P3 __REG(0x41500004) /* SSP Port 3 Control Register 1 */ +#define SSSR_P3 __REG(0x41500008) /* SSP Port 3 Status Register */ +#define SSITR_P3 __REG(0x4150000C) /* SSP Port 3 Interrupt Test Register */ +#define SSDR_P3 __REG(0x41500010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ +#define SSTO_P3 __REG(0x41500028) /* SSP Port 3 Time Out Register */ +#define SSPSP_P3 __REG(0x4150002C) /* SSP Port 3 Programmable Serial Protocol */ +#endif + +#define SSCR0_P(x) (*(((x) == 1) ? &SSCR0_P1 : ((x) == 2) ? &SSCR0_P2 : ((x) == 3) ? &SSCR0_P3 : NULL)) +#define SSCR1_P(x) (*(((x) == 1) ? &SSCR1_P1 : ((x) == 2) ? &SSCR1_P2 : ((x) == 3) ? &SSCR1_P3 : NULL)) +#define SSSR_P(x) (*(((x) == 1) ? &SSSR_P1 : ((x) == 2) ? &SSSR_P2 : ((x) == 3) ? &SSSR_P3 : NULL)) +#define SSITR_P(x) (*(((x) == 1) ? &SSITR_P1 : ((x) == 2) ? &SSITR_P2 : ((x) == 3) ? &SSITR_P3 : NULL)) +#define SSDR_P(x) (*(((x) == 1) ? &SSDR_P1 : ((x) == 2) ? &SSDR_P2 : ((x) == 3) ? &SSDR_P3 : NULL)) +#define SSTO_P(x) (*(((x) == 1) ? &SSTO_P1 : ((x) == 2) ? &SSTO_P2 : ((x) == 3) ? &SSTO_P3 : NULL)) +#define SSPSP_P(x) (*(((x) == 1) ? &SSPSP_P1 : ((x) == 2) ? &SSPSP_P2 : ((x) == 3) ? &SSPSP_P3 : NULL)) + +#define SSCR0_P1 __REG(0x41000000) /* SSP Port 1 Control Register 0 */ +#define SSCR1_P1 __REG(0x41000004) /* SSP Port 1 Control Register 1 */ +#define SSSR_P1 __REG(0x41000008) /* SSP Port 1 Status Register */ +#define SSITR_P1 __REG(0x4100000C) /* SSP Port 1 Interrupt Test Register */ +#define SSDR_P1 __REG(0x41000010) /* (Write / Read) SSP Port 1 Data Write Register/SSP Data Read Register */ + +/* Support existing PXA25x drivers */ +#define SSCR0 SSCR0_P1 /* SSP Control Register 0 */ +#define SSCR1 SSCR1_P1 /* SSP Control Register 1 */ +#define SSSR SSSR_P1 /* SSP Status Register */ +#define SSITR SSITR_P1 /* SSP Interrupt Test Register */ +#define SSDR SSDR_P1 /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ + +/* PXA27x ports */ +#if defined (CONFIG_PXA27x) +#define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ +#define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSCR0_P2 __REG(0x41700000) /* SSP Port 2 Control Register 0 */ +#define SSCR1_P2 __REG(0x41700004) /* SSP Port 2 Control Register 1 */ +#define SSSR_P2 __REG(0x41700008) /* SSP Port 2 Status Register */ +#define SSITR_P2 __REG(0x4170000C) /* SSP Port 2 Interrupt Test Register */ +#define SSDR_P2 __REG(0x41700010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ +#define SSTO_P2 __REG(0x41700028) /* SSP Port 2 Time Out Register */ +#define SSPSP_P2 __REG(0x4170002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSCR0_P3 __REG(0x41900000) /* SSP Port 3 Control Register 0 */ +#define SSCR1_P3 __REG(0x41900004) /* SSP Port 3 Control Register 1 */ +#define SSSR_P3 __REG(0x41900008) /* SSP Port 3 Status Register */ +#define SSITR_P3 __REG(0x4190000C) /* SSP Port 3 Interrupt Test Register */ +#define SSDR_P3 __REG(0x41900010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ +#define SSTO_P3 __REG(0x41900028) /* SSP Port 3 Time Out Register */ +#define SSPSP_P3 __REG(0x4190002C) /* SSP Port 3 Programmable Serial Protocol */ +#else /* PXA255 (only port 2) and PXA26x ports*/ +#define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ +#define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSCR0_P2 __REG(0x41400000) /* SSP Port 2 Control Register 0 */ +#define SSCR1_P2 __REG(0x41400004) /* SSP Port 2 Control Register 1 */ +#define SSSR_P2 __REG(0x41400008) /* SSP Port 2 Status Register */ +#define SSITR_P2 __REG(0x4140000C) /* SSP Port 2 Interrupt Test Register */ +#define SSDR_P2 __REG(0x41400010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ +#define SSTO_P2 __REG(0x41400028) /* SSP Port 2 Time Out Register */ +#define SSPSP_P2 __REG(0x4140002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSCR0_P3 __REG(0x41500000) /* SSP Port 3 Control Register 0 */ +#define SSCR1_P3 __REG(0x41500004) /* SSP Port 3 Control Register 1 */ +#define SSSR_P3 __REG(0x41500008) /* SSP Port 3 Status Register */ +#define SSITR_P3 __REG(0x4150000C) /* SSP Port 3 Interrupt Test Register */ +#define SSDR_P3 __REG(0x41500010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ +#define SSTO_P3 __REG(0x41500028) /* SSP Port 3 Time Out Register */ +#define SSPSP_P3 __REG(0x4150002C) /* SSP Port 3 Programmable Serial Protocol */ +#endif + +#define SSCR0_P(x) (*(((x) == 1) ? &SSCR0_P1 : ((x) == 2) ? &SSCR0_P2 : ((x) == 3) ? &SSCR0_P3 : NULL)) +#define SSCR1_P(x) (*(((x) == 1) ? &SSCR1_P1 : ((x) == 2) ? &SSCR1_P2 : ((x) == 3) ? &SSCR1_P3 : NULL)) +#define SSSR_P(x) (*(((x) == 1) ? &SSSR_P1 : ((x) == 2) ? &SSSR_P2 : ((x) == 3) ? &SSSR_P3 : NULL)) +#define SSITR_P(x) (*(((x) == 1) ? &SSITR_P1 : ((x) == 2) ? &SSITR_P2 : ((x) == 3) ? &SSITR_P3 : NULL)) +#define SSDR_P(x) (*(((x) == 1) ? &SSDR_P1 : ((x) == 2) ? &SSDR_P2 : ((x) == 3) ? &SSDR_P3 : NULL)) +#define SSTO_P(x) (*(((x) == 1) ? &SSTO_P1 : ((x) == 2) ? &SSTO_P2 : ((x) == 3) ? &SSTO_P3 : NULL)) +#define SSPSP_P(x) (*(((x) == 1) ? &SSPSP_P1 : ((x) == 2) ? &SSPSP_P2 : ((x) == 3) ? &SSPSP_P3 : NULL)) /* * MultiMediaCard (MMC) controller @@ -1561,8 +1892,10 @@ #define CKEN13_FICP (1 << 13) /* FICP Unit Clock Enable */ #define CKEN12_MMC (1 << 12) /* MMC Unit Clock Enable */ #define CKEN11_USB (1 << 11) /* USB Unit Clock Enable */ +#define CKEN10_ASSP (1 << 10) /* ASSP (SSP3) Clock Enable */ #define CKEN10_USBHOST (1 << 10) /* USB Host Unit Clock Enable */ #define CKEN9_OSTIMER (1 << 9) /* OS Timer Unit Clock Enable */ +#define CKEN9_NSSP (1 << 9) /* NSSP (SSP2) Clock Enable */ #define CKEN8_I2S (1 << 8) /* I2S Unit Clock Enable */ #define CKEN7_BTUART (1 << 7) /* BTUART Unit Clock Enable */ #define CKEN6_FFUART (1 << 6) /* FFUART Unit Clock Enable */ @@ -2053,6 +2386,9 @@ #define UHCHIT __REG(0x4C00006C) /* UHC Interrupt Test register */ /* Camera Interface */ +#define CI_REG_SIZE 0x40 /* 0x5000_0000 --- 0x5000_0038 * 64K */ +#define CI_REGS_PHYS 0x50000000 /* Start phyical address of CI registers */ + #define CICR0 __REG(0x50000000) #define CICR1 __REG(0x50000004) #define CICR2 __REG(0x50000008) diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/timex.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/timex.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/timex.h 2004-10-18 17:53:22.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/timex.h 2005-05-01 02:17:46.000000000 -0400 @@ -14,12 +14,14 @@ #if defined(CONFIG_PXA25x) /* PXA250/210 timer base */ -#define CLOCK_TICK_RATE 3686400 + #define CLOCK_TICK_RATE 3686400 #elif defined(CONFIG_PXA27x) /* PXA27x timer base */ -#ifdef CONFIG_MACH_MAINSTONE -#define CLOCK_TICK_RATE 3249600 -#else -#define CLOCK_TICK_RATE 3250000 -#endif + #ifdef CONFIG_MACH_MAINSTONE + #define CLOCK_TICK_RATE 3249600 + #elif defined(CONFIG_MACH_GLENCOE) + #define CLOCK_TICK_RATE 3249600 + #else + #define CLOCK_TICK_RATE 3250000 + #endif #endif diff -Nur linux-2.6.9-intc1/include/asm-arm/arch-pxa/uncompress.h linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/uncompress.h --- linux-2.6.9-intc1/include/asm-arm/arch-pxa/uncompress.h 2004-10-18 17:53:10.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/asm-arm/arch-pxa/uncompress.h 2005-05-02 18:21:29.000000000 -0400 @@ -13,8 +13,15 @@ #define BTUART ((volatile unsigned long *)0x40200000) #define STUART ((volatile unsigned long *)0x40700000) -#define UART FFUART - +#if defined (CONFIG_INITIAL_UART1) + #define UART FFUART +#elif defined (CONFIG_INITIAL_UART2) + #define UART BTUART +#elif defined (CONFIG_INITIAL_UART3) + #define UART STUART +#else + #error No serial port selected! +#endif static __inline__ void putc(char c) { diff -Nur linux-2.6.9-intc1/include/asm-arm/dma-mapping.h linux-2.6.9-intc1-mih1/include/asm-arm/dma-mapping.h --- linux-2.6.9-intc1/include/asm-arm/dma-mapping.h 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/asm-arm/dma-mapping.h 2005-03-24 15:47:36.000000000 -0500 @@ -87,6 +87,22 @@ dma_addr_t handle); /** + * dma_mmap_coherent - map a coherent DMA allocation into user space + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @vma: vm_area_struct describing requested user mapping + * @cpu_addr: kernel CPU-view address returned from dma_alloc_coherent + * @handle: device-view address returned from dma_alloc_coherent + * @size: size of memory originally requested in dma_alloc_coherent + * + * Map a coherent DMA buffer previously allocated by dma_alloc_coherent + * into user space. The coherent DMA buffer must not be freed by the + * driver until the user space mapping has been released. + */ +int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t handle, size_t size); + + +/** * dma_alloc_writecombine - allocate writecombining memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size @@ -103,6 +119,9 @@ #define dma_free_writecombine(dev,size,cpu_addr,handle) \ dma_free_coherent(dev,size,cpu_addr,handle) +int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t handle, size_t size); + /** * dma_map_single - map a single buffer for streaming DMA diff -Nur linux-2.6.9-intc1/include/linux/ac97_codec.h linux-2.6.9-intc1-mih1/include/linux/ac97_codec.h --- linux-2.6.9-intc1/include/linux/ac97_codec.h 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/ac97_codec.h 2005-02-21 22:46:00.000000000 -0500 @@ -290,7 +290,7 @@ #define AC97_DELUDED_MODEM 1 /* Audio codec reports its a modem */ #define AC97_NO_PCM_VOLUME 2 /* Volume control is missing */ -#define AC97_DEFAULT_POWER_OFF 4 /* Needs warm reset to power up */ +#define AC97_DEFAULT_POWER_OFF 4 /* Needs warm reset to power up */ }; extern int ac97_read_proc (char *page_out, char **start, off_t off, @@ -300,7 +300,9 @@ extern unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate); extern int ac97_save_state(struct ac97_codec *codec); extern int ac97_restore_state(struct ac97_codec *codec); - +extern int ac97_suspend(struct ac97_codec *codec, int state); +extern void ac97_resume(struct ac97_codec *codec); + extern struct ac97_codec *ac97_alloc_codec(void); extern void ac97_release_codec(struct ac97_codec *codec); @@ -311,9 +313,33 @@ u32 codec_mask; int (*probe) (struct ac97_codec *codec, struct ac97_driver *driver); void (*remove) (struct ac97_codec *codec, struct ac97_driver *driver); + int (*suspend) (struct ac97_codec *codec, int state); + void (*resume) (struct ac97_codec *codec); }; extern int ac97_register_driver(struct ac97_driver *driver); extern void ac97_unregister_driver(struct ac97_driver *driver); + +/* quirk types */ +enum { + AC97_TUNE_DEFAULT = -1, /* use default from quirk list (not valid in list) */ + AC97_TUNE_NONE = 0, /* nothing extra to do */ + AC97_TUNE_HP_ONLY, /* headphone (true line-out) control as master only */ + AC97_TUNE_SWAP_HP, /* swap headphone and master controls */ + AC97_TUNE_SWAP_SURROUND, /* swap master and surround controls */ + AC97_TUNE_AD_SHARING, /* for AD1985, turn on OMS bit and use headphone */ + AC97_TUNE_ALC_JACK, /* for Realtek, enable JACK detection */ +}; + +struct ac97_quirk { + unsigned short vendor; /* PCI vendor id */ + unsigned short device; /* PCI device id */ + unsigned short mask; /* device id bit mask, 0 = accept all */ + const char *name; /* name shown as info */ + int type; /* quirk type above */ +}; + +struct pci_dev; +extern int ac97_tune_hardware(struct pci_dev *pdev, struct ac97_quirk *quirk, int override); #endif /* _AC97_CODEC_H_ */ diff -Nur linux-2.6.9-intc1/include/linux/delay.h linux-2.6.9-intc1-mih1/include/linux/delay.h --- linux-2.6.9-intc1/include/linux/delay.h 2004-10-18 17:53:44.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/delay.h 2005-04-21 20:50:16.000000000 -0400 @@ -38,6 +38,7 @@ #define ndelay(x) udelay(((x)+999)/1000) #endif +void calibrate_delay(void); void msleep(unsigned int msecs); unsigned long msleep_interruptible(unsigned int msecs); diff -Nur linux-2.6.9-intc1/include/linux/jffs2_fs_i.h linux-2.6.9-intc1-mih1/include/linux/jffs2_fs_i.h --- linux-2.6.9-intc1/include/linux/jffs2_fs_i.h 2004-10-18 17:54:39.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/jffs2_fs_i.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,10 +1,11 @@ -/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */ +/* $Id: jffs2_fs_i.h,v 1.17 2004/11/11 23:51:27 dwmw2 Exp $ */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I #include #include +#include struct jffs2_inode_info { /* We need an internal semaphore similar to inode->i_sem. diff -Nur linux-2.6.9-intc1/include/linux/jffs2_fs_sb.h linux-2.6.9-intc1-mih1/include/linux/jffs2_fs_sb.h --- linux-2.6.9-intc1/include/linux/jffs2_fs_sb.h 2004-10-18 17:53:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/jffs2_fs_sb.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.51 2005/02/28 08:21:06 dedekind Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -11,9 +11,11 @@ #include #include #include +#include #define JFFS2_SB_FLAG_RO 1 -#define JFFS2_SB_FLAG_MOUNTING 2 +#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ +#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ struct jffs2_inodirty; @@ -35,9 +37,7 @@ struct semaphore alloc_sem; /* Used to protect all the following fields, and also to protect against - out-of-order writing of nodes. - And GC. - */ + out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER (i.e. zero for OOB CLEANMARKER */ @@ -95,7 +95,7 @@ to an obsoleted node. I don't like this. Alternatives welcomed. */ struct semaphore erase_free_sem; -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* Write-behind buffer for NAND flash */ unsigned char *wbuf; uint32_t wbuf_ofs; @@ -103,6 +103,8 @@ uint32_t wbuf_pagesize; struct jffs2_inodirty *wbuf_inodes; + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + /* Information about out-of-band area usage... */ struct nand_oobinfo *oobinfo; uint32_t badblock_pos; diff -Nur linux-2.6.9-intc1/include/linux/jffs2.h linux-2.6.9-intc1-mih1/include/linux/jffs2.h --- linux-2.6.9-intc1/include/linux/jffs2.h 2004-10-18 17:55:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/jffs2.h 2005-03-21 21:28:40.000000000 -0500 @@ -3,12 +3,12 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $ + * $Id: jffs2.h,v 1.34 2004/11/16 20:36:14 dwmw2 Exp $ * */ diff -Nur linux-2.6.9-intc1/include/linux/mmc/card.h linux-2.6.9-intc1-mih1/include/linux/mmc/card.h --- linux-2.6.9-intc1/include/linux/mmc/card.h 2004-10-18 17:54:55.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mmc/card.h 2005-02-26 18:19:12.000000000 -0500 @@ -47,6 +47,8 @@ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_DEAD (1<<1) /* device no longer in stack */ #define MMC_STATE_BAD (1<<2) /* unrecognised device */ +#define MMC_STATE_IS_SD (1<<3) /* card specific */ + u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ struct mmc_cid cid; /* card identification */ diff -Nur linux-2.6.9-intc1/include/linux/mmc/host.h linux-2.6.9-intc1-mih1/include/linux/mmc/host.h --- linux-2.6.9-intc1/include/linux/mmc/host.h 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mmc/host.h 2005-02-26 18:19:23.000000000 -0500 @@ -68,6 +68,8 @@ unsigned int f_max; u32 ocr_avail; char host_name[8]; +#define HOST_CAP_SD_4_BIT (1 << 0) + unsigned long flags; /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ diff -Nur linux-2.6.9-intc1/include/linux/mmc/mmc.h linux-2.6.9-intc1-mih1/include/linux/mmc/mmc.h --- linux-2.6.9-intc1/include/linux/mmc/mmc.h 2004-10-18 17:54:27.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mmc/mmc.h 2005-05-01 02:21:53.000000000 -0400 @@ -23,10 +23,12 @@ unsigned int flags; /* expected response type */ #define MMC_RSP_NONE (0 << 0) #define MMC_RSP_SHORT (1 << 0) -#define MMC_RSP_LONG (2 << 0) -#define MMC_RSP_MASK (3 << 0) +#define MMC_RSP_LONG (1 << 1) +#define MMC_RSP_SD (1 << 2) #define MMC_RSP_CRC (1 << 3) /* expect valid crc */ #define MMC_RSP_BUSY (1 << 4) /* card may send busy */ +#define MMC_FLAG_ACMD (1 << 5) +#define MMC_RSP_MASK 0x1f /* * These are the response types, and correspond to valid bit @@ -37,6 +39,7 @@ #define MMC_RSP_R1B (MMC_RSP_SHORT|MMC_RSP_CRC|MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_LONG|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_SHORT) +#define MMC_RSP_R6 (MMC_RSP_R1|MMC_RSP_SD) unsigned int retries; /* max number of retries */ unsigned int error; /* command error */ @@ -57,7 +60,6 @@ unsigned int timeout_clks; /* data timeout (in clocks) */ unsigned int blksz_bits; /* data block size */ unsigned int blocks; /* number of blocks */ - struct request *req; /* request structure */ unsigned int error; /* data error */ unsigned int flags; @@ -69,6 +71,9 @@ struct mmc_command *stop; /* stop command */ struct mmc_request *mrq; /* assoicated request */ + + unsigned int sg_len; /* size of scatter list */ + struct scatterlist *sg; /* I/O scatter list */ }; struct mmc_request { diff -Nur linux-2.6.9-intc1/include/linux/mmc/protocol.h linux-2.6.9-intc1-mih1/include/linux/mmc/protocol.h --- linux-2.6.9-intc1/include/linux/mmc/protocol.h 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mmc/protocol.h 2005-02-26 18:19:43.000000000 -0500 @@ -31,6 +31,7 @@ #define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ #define MMC_ALL_SEND_CID 2 /* bcr R2 */ #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SEND_RELATIVE_ADDR 3 /* ac sd cards use different CMD3 to MMC */ #define MMC_SET_DSR 4 /* bc [31:16] RCA */ #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ #define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ @@ -76,6 +77,10 @@ #define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1b */ +/* Advanced Command (used by sd card) */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ + /* MMC status in R1 Type diff -Nur linux-2.6.9-intc1/include/linux/mm.h linux-2.6.9-intc1-mih1/include/linux/mm.h --- linux-2.6.9-intc1/include/linux/mm.h 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mm.h 2005-03-24 17:27:28.000000000 -0500 @@ -761,6 +761,9 @@ extern int remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_t prot); +int remap_pfn_range(struct vm_area_struct *, unsigned long, + unsigned long, unsigned long, pgprot_t); + #ifdef CONFIG_PROC_FS void __vm_stat_account(struct mm_struct *, unsigned long, struct file *, long); #else diff -Nur linux-2.6.9-intc1/include/linux/mtd/cfi.h linux-2.6.9-intc1-mih1/include/linux/mtd/cfi.h --- linux-2.6.9-intc1/include/linux/mtd/cfi.h 2004-10-18 17:53:51.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/cfi.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.45 2004/07/20 02:44:27 dwmw2 Exp $ + * $Id: cfi.h,v 1.53 2005/03/15 19:03:13 gleixner Exp $ */ #ifndef __MTD_CFI_H__ @@ -145,6 +145,32 @@ uint16_t ProtRegAddr; uint8_t FactProtRegSize; uint8_t UserProtRegSize; + uint8_t extra[0]; +} __attribute__((packed)); + +struct cfi_intelext_otpinfo { + uint32_t ProtRegAddr; + uint16_t FactGroups; + uint8_t FactProtRegSize; + uint16_t UserGroups; + uint8_t UserProtRegSize; +} __attribute__((packed)); + +struct cfi_intelext_blockinfo { + uint16_t NumIdentBlocks; + uint16_t BlockSize; + uint16_t MinBlockEraseCycles; + uint8_t BitsPerCell; + uint8_t BlockCap; +} __attribute__((packed)); + +struct cfi_intelext_regioninfo { + uint16_t NumIdentPartitions; + uint8_t NumOpAllowed; + uint8_t NumOpAllowedSimProgMode; + uint8_t NumOpAllowedSimEraMode; + uint8_t NumBlockTypes; + struct cfi_intelext_blockinfo BlockTypes[1]; } __attribute__((packed)); /* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */ @@ -177,16 +203,19 @@ uint32_t ConfField[1]; /* Not host ordered */ } __attribute__((packed)); -#define P_ID_NONE 0 -#define P_ID_INTEL_EXT 1 -#define P_ID_AMD_STD 2 -#define P_ID_INTEL_STD 3 -#define P_ID_AMD_EXT 4 -#define P_ID_ST_ADV 32 -#define P_ID_MITSUBISHI_STD 256 -#define P_ID_MITSUBISHI_EXT 257 -#define P_ID_SST_PAGE 258 -#define P_ID_RESERVED 65535 +#define P_ID_NONE 0x0000 +#define P_ID_INTEL_EXT 0x0001 +#define P_ID_AMD_STD 0x0002 +#define P_ID_INTEL_STD 0x0003 +#define P_ID_AMD_EXT 0x0004 +#define P_ID_WINBOND 0x0006 +#define P_ID_ST_ADV 0x0020 +#define P_ID_MITSUBISHI_STD 0x0100 +#define P_ID_MITSUBISHI_EXT 0x0101 +#define P_ID_SST_PAGE 0x0102 +#define P_ID_INTEL_PERFORMANCE 0x0200 +#define P_ID_INTEL_DATA 0x0210 +#define P_ID_RESERVED 0xffff #define CFI_MODE_CFI 1 @@ -223,7 +252,7 @@ * It looks too long to be inline, but in the common case it should almost all * get optimised away. */ -static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) +static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi) { map_word val = { {0} }; int wordwidth, words_per_bus, chip_mode, chips_per_word; @@ -286,6 +315,69 @@ } #define CMD(x) cfi_build_cmd((x), map, cfi) + +static inline unsigned char cfi_merge_status(map_word val, struct map_info *map, + struct cfi_private *cfi) +{ + int wordwidth, words_per_bus, chip_mode, chips_per_word; + unsigned long onestat, res = 0; + int i; + + /* We do it this way to give the compiler a fighting chance + of optimising away all the crap for 'bankwidth' larger than + an unsigned long, in the common case where that support is + disabled */ + if (map_bankwidth_is_large(map)) { + wordwidth = sizeof(unsigned long); + words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1 + } else { + wordwidth = map_bankwidth(map); + words_per_bus = 1; + } + + chip_mode = map_bankwidth(map) / cfi_interleave(cfi); + chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map); + + onestat = val.x[0]; + /* Or all status words together */ + for (i=1; i < words_per_bus; i++) { + onestat |= val.x[i]; + } + + res = onestat; + switch(chips_per_word) { + default: BUG(); +#if BITS_PER_LONG >= 64 + case 8: + res |= (onestat >> (chip_mode * 32)); +#endif + case 4: + res |= (onestat >> (chip_mode * 16)); + case 2: + res |= (onestat >> (chip_mode * 8)); + case 1: + ; + } + + /* Last, determine what the bit-pattern should be for a single + device, according to chip mode and endianness... */ + switch (chip_mode) { + case 1: + break; + case 2: + res = cfi16_to_cpu(res); + break; + case 4: + res = cfi32_to_cpu(res); + break; + default: BUG(); + } + return res; +} + +#define MERGESTATUS(x) cfi_merge_status((x), map, cfi) + + /* * Sends a CFI command to a bank of flash for the given geometry. * @@ -328,14 +420,12 @@ static inline void cfi_udelay(int us) { - unsigned long t = us * HZ / 1000000; - if (t) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(t); - return; + if (us >= 1000) { + msleep((us+999)/1000); + } else { + udelay(us); + cond_resched(); } - udelay(us); - cond_resched(); } static inline void cfi_spin_lock(spinlock_t *mutex) @@ -350,17 +440,26 @@ struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size, const char* name); - struct cfi_fixup { uint16_t mfr; uint16_t id; - void (*fixup)(struct map_info *map, void* param); + void (*fixup)(struct mtd_info *mtd, void* param); void* param; }; #define CFI_MFR_ANY 0xffff #define CFI_ID_ANY 0xffff -void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups); +#define CFI_MFR_AMD 0x0001 +#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ + +void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); + +typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk); + +int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, + loff_t ofs, size_t len, void *thunk); + #endif /* __MTD_CFI_H__ */ diff -Nur linux-2.6.9-intc1/include/linux/mtd/compatmac.h linux-2.6.9-intc1-mih1/include/linux/mtd/compatmac.h --- linux-2.6.9-intc1/include/linux/mtd/compatmac.h 2004-10-18 17:55:06.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/compatmac.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,10 +1,227 @@ +/* + * $Id: compatmac.h,v 1.72 2005/03/18 22:42:24 gleixner Exp $ + * + * Extensions and omissions from the normal 'linux/compatmac.h' + * files. hopefully this will end up empty as the 'real' one + * becomes fully-featured. + */ #ifndef __LINUX_MTD_COMPATMAC_H__ #define __LINUX_MTD_COMPATMAC_H__ -/* Nothing to see here. We write 2.5-compatible code and this - file makes it all OK in older kernels, but it's empty in _current_ - kernels. Include guard just to make GCC ignore it in future inclusions - anyway... */ +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +#error "This kernel is too old: not supported by this file" +#endif + + /* O(1) scheduler stuff. */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__) +#include +static inline void __recalc_sigpending(void) +{ + recalc_sigpending(current); +} +#undef recalc_sigpending +#define recalc_sigpending() __recalc_sigpending () + +#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) +#endif + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) + +#ifndef yield +#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) +#endif + +#ifndef minor +#define major(d) (MAJOR(to_kdev_t(d))) +#define minor(d) (MINOR(to_kdev_t(d))) +#endif + +#ifndef mk_kdev +#define mk_kdev(ma,mi) MKDEV(ma,mi) +#define kdev_t_to_nr(x) (x) +#endif + +#define need_resched() (current->need_resched) +#define cond_resched() do { if need_resched() { yield(); } } while(0) + +#endif /* < 2.4.20 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) +#define iminor(i) minor((i)->i_rdev) +#define imajor(i) major((i)->i_rdev) +#define old_encode_dev(d) ( (major(d)<<8) | minor(d) ) +#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff))) +#define old_valid_dev(d) (1) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + +#include + +#ifdef __rh_config_h__ +#define sigmask_lock sighand->siglock +#define sig sighand +#endif + +static inline void __daemonize_modvers(void) +{ + daemonize(); + + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); +} +#undef daemonize +#define daemonize(fmt, ...) do { \ + snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \ + __daemonize_modvers(); \ + } while(0) + +static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) +{ + unsigned long flags; + unsigned long ret; + + spin_lock_irqsave(¤t->sigmask_lock, flags); + ret = dequeue_signal(mask, info); + spin_unlock_irqrestore(¤t->sigmask_lock, flags); + + return ret; +} + +static inline int allow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + spin_lock_irq(¤t->sigmask_lock); + sigdelset(¤t->blocked, sig); + recalc_sigpending(); + /* Make sure the kernel neither eats it now converts to SIGKILL */ + current->sig->action[sig-1].sa.sa_handler = (void *)2; + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} +static inline int disallow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + spin_lock_irq(¤t->sigmask_lock); + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + + current->sig->action[sig-1].sa.sa_handler = SIG_DFL; + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} + +#define PF_FREEZE 0 +#define refrigerator(x) do { ; } while(0) +#endif + + /* Module bits */ + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60) +#define try_module_get(m) try_inc_mod_count(m) +#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0) +#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0) +#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0) +#endif + + + /* Random filesystem stuff, only for JFFS2 really */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) +#define parent_ino(d) ((d)->d_parent->d_inode->i_ino) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12) +#define PageUptodate(x) Page_Uptodate(x) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) +#define get_seconds() CURRENT_TIME +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53) +#define generic_file_readonly_mmap generic_file_mmap +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70) + +#include +#include + +static inline char *strlcpy(char *dest, const char *src, int len) +{ + dest[len-1] = 0; + return strncpy(dest, src, len-1); +} + +static inline int do_old_request_module(const char *mod) +{ + return request_module(mod); +} +#undef request_module +#define request_module(fmt, ...) \ + ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); }) + +#endif /* 2.5.70 */ + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7) +#define kvec iovec +#define __user +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28) +#define msleep(x) \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x)*(HZ/1000)); +#endif + +#ifndef __iomem +#define __iomem +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) +#define CURRENT_TIME_SEC ((struct timespec) { xtime.tv_sec, 0 }) +#endif + +#ifndef list_for_each_entry_safe +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif + +#ifndef DEFINE_SPINLOCK +#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED +#endif +#ifndef DEFINE_RWLOCK +#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED +#endif #endif /* __LINUX_MTD_COMPATMAC_H__ */ diff -Nur linux-2.6.9-intc1/include/linux/mtd/doc2000.h linux-2.6.9-intc1-mih1/include/linux/mtd/doc2000.h --- linux-2.6.9-intc1/include/linux/mtd/doc2000.h 2004-10-18 17:54:31.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/doc2000.h 2005-03-21 21:28:40.000000000 -0500 @@ -6,7 +6,7 @@ * Copyright (C) 2002-2003 Greg Ungerer * Copyright (C) 2002-2003 SnapGear Inc * - * $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $ + * $Id: doc2000.h,v 1.24 2005/01/05 12:40:38 dwmw2 Exp $ * * Released under GPL */ @@ -89,8 +89,8 @@ #define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) #define DOC_IOREMAP_LEN 0x4000 #else -#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg)) -#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + (reg)) +#define ReadDOC_(adr, reg) readb((void __iomem *)(adr) + (reg)) +#define WriteDOC_(d, adr, reg) writeb(d, (void __iomem *)(adr) + (reg)) #define DOC_IOREMAP_LEN 0x2000 #endif @@ -168,7 +168,7 @@ struct DiskOnChip { unsigned long physadr; - unsigned long virtadr; + void __iomem *virtadr; unsigned long totlen; unsigned char ChipID; /* Type of DiskOnChip */ int ioreg; diff -Nur linux-2.6.9-intc1/include/linux/mtd/flashchip.h linux-2.6.9-intc1-mih1/include/linux/mtd/flashchip.h --- linux-2.6.9-intc1/include/linux/mtd/flashchip.h 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/flashchip.h 2005-03-21 21:28:40.000000000 -0500 @@ -6,7 +6,7 @@ * * (C) 2000 Red Hat. GPLd. * - * $Id: flashchip.h,v 1.14 2004/06/15 16:44:59 nico Exp $ + * $Id: flashchip.h,v 1.17 2005/03/14 18:27:15 bjd Exp $ * */ @@ -29,6 +29,7 @@ FL_ERASE_SUSPENDED, FL_WRITING, FL_WRITING_TO_BUFFER, + FL_OTP_WRITE, FL_WRITE_SUSPENDING, FL_WRITE_SUSPENDED, FL_PM_SUSPENDED, @@ -37,6 +38,8 @@ FL_LOCKING, FL_UNLOCKING, FL_POINT, + FL_XIP_WHILE_ERASING, + FL_XIP_WHILE_WRITING, FL_UNKNOWN } flstate_t; @@ -60,8 +63,8 @@ flstate_t state; flstate_t oldstate; - int write_suspended:1; - int erase_suspended:1; + unsigned int write_suspended:1; + unsigned int erase_suspended:1; unsigned long in_progress_block_addr; spinlock_t *mutex; diff -Nur linux-2.6.9-intc1/include/linux/mtd/gen_probe.h linux-2.6.9-intc1-mih1/include/linux/mtd/gen_probe.h --- linux-2.6.9-intc1/include/linux/mtd/gen_probe.h 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/gen_probe.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,7 +1,7 @@ /* * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $ + * $Id: gen_probe.h,v 1.3 2004/10/20 22:10:33 dwmw2 Exp $ */ #ifndef __LINUX_MTD_GEN_PROBE_H__ @@ -10,7 +10,7 @@ #include #include #include -#include +#include struct chip_probe { char *name; diff -Nur linux-2.6.9-intc1/include/linux/mtd/map.h linux-2.6.9-intc1-mih1/include/linux/mtd/map.h --- linux-2.6.9-intc1/include/linux/mtd/map.h 2004-10-18 17:53:51.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/map.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,6 +1,6 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.43 2004/07/14 13:30:27 dwmw2 Exp $ */ +/* $Id: map.h,v 1.48 2005/02/16 15:54:59 nico Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,11 @@ #define map_bankwidth_is_4(map) (0) #endif +/* ensure we never evaluate anything shorted than an unsigned long + * to zero, and ensure we'll never miss the end of an comparison (bjd) */ + +#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long)) + #ifdef CONFIG_MTD_MAP_BANK_WIDTH_8 # ifdef map_bankwidth # undef map_bankwidth @@ -63,12 +69,12 @@ # undef map_bankwidth_is_large # define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) # undef map_words -# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# define map_words(map) map_calc_words(map) # endif # else # define map_bankwidth(map) 8 # define map_bankwidth_is_large(map) (BITS_PER_LONG < 64) -# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# define map_words(map) map_calc_words(map) # endif #define map_bankwidth_is_8(map) (map_bankwidth(map) == 8) #undef MAX_MAP_BANKWIDTH @@ -84,11 +90,11 @@ # undef map_bankwidth_is_large # define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) # undef map_words -# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# define map_words(map) map_calc_words(map) # else # define map_bankwidth(map) 16 # define map_bankwidth_is_large(map) (1) -# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# define map_words(map) map_calc_words(map) # endif #define map_bankwidth_is_16(map) (map_bankwidth(map) == 16) #undef MAX_MAP_BANKWIDTH @@ -104,11 +110,11 @@ # undef map_bankwidth_is_large # define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) # undef map_words -# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# define map_words(map) map_calc_words(map) # else # define map_bankwidth(map) 32 # define map_bankwidth_is_large(map) (1) -# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# define map_words(map) map_calc_words(map) # endif #define map_bankwidth_is_32(map) (map_bankwidth(map) == 32) #undef MAX_MAP_BANKWIDTH @@ -257,6 +263,17 @@ return r; } +static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2) +{ + map_word r; + int i; + + for (i=0; ibankwidth)) -#define map_is_linear(map) (1) +#define map_is_linear(map) ({ (void)(map); 1; }) #endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */ diff -Nur linux-2.6.9-intc1/include/linux/mtd/mtd.h linux-2.6.9-intc1-mih1/include/linux/mtd/mtd.h --- linux-2.6.9-intc1/include/linux/mtd/mtd.h 2004-10-18 17:53:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/mtd.h 2005-03-31 21:33:44.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin Exp $ + * $Id: mtd.h,v 1.58 2005/04/01 01:59:54 nico Exp $ * * Copyright (C) 1999-2003 David Woodhouse et al. * @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -113,12 +114,12 @@ * flash devices. The user data is one time programmable but the * factory data is read only. */ - int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); - + int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); - - /* This function is not yet implemented */ + int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); + int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); /* kvec-based read/write methods. We need these especially for NAND flash, with its limited number of write cycles per erase. @@ -147,6 +148,8 @@ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); + struct notifier_block reboot_notifier; /* default mode before reboot */ + void *priv; struct module *owner; diff -Nur linux-2.6.9-intc1/include/linux/mtd/nand.h linux-2.6.9-intc1-mih1/include/linux/mtd/nand.h --- linux-2.6.9-intc1/include/linux/mtd/nand.h 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/nand.h 2005-03-21 21:28:40.000000000 -0500 @@ -5,7 +5,7 @@ * Steven J. Hill * Thomas Gleixner * - * $Id: nand.h,v 1.63 2004/07/07 16:29:43 gleixner Exp $ + * $Id: nand.h,v 1.71 2005/02/09 12:12:59 gleixner Exp $ * * 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 @@ -48,6 +48,10 @@ * 02-08-2004 tglx added option field to nand structure for chip anomalities * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id * update of nand_chip structure description + * 01-17-2005 dmarlin added extended commands for AG-AND device and added option + * for BBT_AUTO_REFRESH. + * 01-20-2005 dmarlin added optional pointer to hardware specific callback for + * extra error status checks. */ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H @@ -115,6 +119,25 @@ #define NAND_CMD_READSTART 0x30 #define NAND_CMD_CACHEDPROG 0x15 +/* Extended commands for AG-AND device */ +/* + * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but + * there is no way to distinguish that from NAND_CMD_READ0 + * until the remaining sequence of commands has been completed + * so add a high order bit and mask it off in the command. + */ +#define NAND_CMD_DEPLETE1 0x100 +#define NAND_CMD_DEPLETE2 0x38 +#define NAND_CMD_STATUS_MULTI 0x71 +#define NAND_CMD_STATUS_ERROR 0x72 +/* multi-bank error status (banks 0-3) */ +#define NAND_CMD_STATUS_ERROR0 0x73 +#define NAND_CMD_STATUS_ERROR1 0x74 +#define NAND_CMD_STATUS_ERROR2 0x75 +#define NAND_CMD_STATUS_ERROR3 0x76 +#define NAND_CMD_STATUS_RESET 0x7f +#define NAND_CMD_STATUS_CLEAR 0xff + /* Status bits */ #define NAND_STATUS_FAIL 0x01 #define NAND_STATUS_FAIL_N1 0x02 @@ -138,10 +161,12 @@ #define NAND_ECC_HW6_512 4 /* Hardware ECC 8 byte ECC per 512 Byte data */ #define NAND_ECC_HW8_512 6 +/* Hardware ECC 12 byte ECC per 2048 Byte data */ +#define NAND_ECC_HW12_2048 7 /* * Constants for Hardware ECC -*/ + */ /* Reset Hardware ECC for read */ #define NAND_ECC_READ 0 /* Reset Hardware ECC for write */ @@ -149,6 +174,10 @@ /* Enable Hardware ECC before syndrom is read back from flash */ #define NAND_ECC_READSYN 2 +/* Bit mask for flags passed to do_nand_read_ecc */ +#define NAND_GET_DEVICE 0x80 + + /* Option constants for bizarre disfunctionality and real * features */ @@ -168,6 +197,10 @@ /* Chip has a array of 4 pages which can be read without * additional ready /busy waits */ #define NAND_4PAGE_ARRAY 0x00000040 +/* Chip requires that BBT is periodically rewritten to prevent + * bits from adjacent blocks from 'leaking' in altering data. + * This happens with the Renesas AG-AND chips, possibly others. */ +#define BBT_AUTO_REFRESH 0x00000080 /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ @@ -190,7 +223,8 @@ * This can only work if we have the ecc bytes directly behind the * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ #define NAND_HWECC_SYNDROME 0x00020000 - +/* This option skips the bbt scan during initialization. */ +#define NAND_SKIP_BBTSCAN 0x00040000 /* Options set by nand scan */ /* Nand scan has allocated oob_buf */ @@ -212,6 +246,18 @@ FL_CACHEDPRG, } nand_state_t; +/* Keep gcc happy */ +struct nand_chip; + +/** + * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices + * @lock: protection lock + * @active: the mtd device which holds the controller currently + */ +struct nand_hw_control { + spinlock_t lock; + struct nand_chip *active; +}; /** * struct nand_chip - NAND Private Flash Chip Data @@ -241,6 +287,7 @@ * @scan_bbt: [REPLACEABLE] function to scan bad block table * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines * @eccsize: [INTERN] databytes used per ecc-calculation + * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step * @eccsteps: [INTERN] number of ecc calculation steps per page * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip @@ -265,12 +312,16 @@ * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_md: [REPLACEABLE] bad block table mirror descriptor + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan + * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices * @priv: [OPTIONAL] pointer to private chip date + * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks + * (determine if errors are correctable) */ struct nand_chip { - unsigned long IO_ADDR_R; - unsigned long IO_ADDR_W; + void __iomem *IO_ADDR_R; + void __iomem *IO_ADDR_W; u_char (*read_byte)(struct mtd_info *mtd); void (*write_byte)(struct mtd_info *mtd, u_char byte); @@ -294,6 +345,7 @@ int (*scan_bbt)(struct mtd_info *mtd); int eccmode; int eccsize; + int eccbytes; int eccsteps; int chip_delay; spinlock_t chip_lock; @@ -317,7 +369,10 @@ uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; + struct nand_hw_control *controller; void *priv; + int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); }; /* @@ -439,6 +494,9 @@ extern int nand_default_bbt (struct mtd_info *mtd); extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); +extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags); /* * Constants for oob configuration diff -Nur linux-2.6.9-intc1/include/linux/mtd/partitions.h linux-2.6.9-intc1-mih1/include/linux/mtd/partitions.h --- linux-2.6.9-intc1/include/linux/mtd/partitions.h 2004-10-18 17:55:18.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/partitions.h 2005-03-21 21:28:40.000000000 -0500 @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $ + * $Id: partitions.h,v 1.16 2004/11/16 18:34:40 dwmw2 Exp $ */ #ifndef MTD_PARTITIONS_H @@ -64,7 +64,6 @@ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long); }; -extern struct mtd_part_parser *get_partition_parser(const char *name); extern int register_mtd_parser(struct mtd_part_parser *parser); extern int deregister_mtd_parser(struct mtd_part_parser *parser); extern int parse_mtd_partitions(struct mtd_info *master, const char **types, diff -Nur linux-2.6.9-intc1/include/linux/mtd/plat-ram.h linux-2.6.9-intc1-mih1/include/linux/mtd/plat-ram.h --- linux-2.6.9-intc1/include/linux/mtd/plat-ram.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/plat-ram.h 2005-03-21 21:28:40.000000000 -0500 @@ -0,0 +1,35 @@ +/* linux/include/mtd/plat-ram.h + * + * (c) 2004 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks + * + * Generic platform device based RAM map + * + * $Id: plat-ram.h,v 1.2 2005/01/24 00:37:40 bjd Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __LINUX_MTD_PLATRAM_H +#define __LINUX_MTD_PLATRAM_H __FILE__ + +#define PLATRAM_RO (0) +#define PLATRAM_RW (1) + +struct platdata_mtd_ram { + char *mapname; + char **probes; + struct mtd_partition *partitions; + int nr_partitions; + int bankwidth; + + /* control callbacks */ + + void (*set_rw)(struct device *dev, int to); +}; + +#endif /* __LINUX_MTD_PLATRAM_H */ diff -Nur linux-2.6.9-intc1/include/linux/mtd/xip.h linux-2.6.9-intc1-mih1/include/linux/mtd/xip.h --- linux-2.6.9-intc1/include/linux/mtd/xip.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/include/linux/mtd/xip.h 2005-03-21 21:28:40.000000000 -0500 @@ -0,0 +1,107 @@ +/* + * MTD primitives for XIP support + * + * Author: Nicolas Pitre + * Created: Nov 2, 2004 + * Copyright: (C) 2004 MontaVista Software, Inc. + * + * This XIP support for MTD has been loosely inspired + * by an earlier patch authored by David Woodhouse. + * + * 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. + * + * $Id: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $ + */ + +#ifndef __LINUX_MTD_XIP_H__ +#define __LINUX_MTD_XIP_H__ + +#include + +#ifdef CONFIG_MTD_XIP + +/* + * Function that are modifying the flash state away from array mode must + * obviously not be running from flash. The __xipram is therefore marking + * those functions so they get relocated to ram. + */ +#define __xipram __attribute__ ((__section__ (".data"))) + +/* + * We really don't want gcc to guess anything. + * We absolutely _need_ proper inlining. + */ +#include + +/* + * Each architecture has to provide the following macros. They must access + * the hardware directly and not rely on any other (XIP) functions since they + * won't be available when used (flash not in array mode). + * + * xip_irqpending() + * + * return non zero when any hardware interrupt is pending. + * + * xip_currtime() + * + * return a platform specific time reference to be used with + * xip_elapsed_since(). + * + * xip_elapsed_since(x) + * + * return in usecs the elapsed timebetween now and the reference x as + * returned by xip_currtime(). + * + * note 1: convertion to usec can be approximated, as long as the + * returned value is <= the real elapsed time. + * note 2: this should be able to cope with a few seconds without + * overflowing. + */ + +#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA) + +#include +#ifdef CONFIG_ARCH_PXA +#include +#endif + +#define xip_irqpending() (ICIP & ICMR) + +/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */ +#define xip_currtime() (OSCR) +#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4) + +#else + +#warning "missing IRQ and timer primitives for XIP MTD support" +#warning "some of the XIP MTD support code will be disabled" +#warning "your system will therefore be unresponsive when writing or erasing flash" + +#define xip_irqpending() (0) +#define xip_currtime() (0) +#define xip_elapsed_since(x) (0) + +#endif + +/* + * xip_cpu_idle() is used when waiting for a delay equal or larger than + * the system timer tick period. This should put the CPU into idle mode + * to save power and to be woken up only when some interrupts are pending. + * As above, this should not rely upon standard kernel code. + */ + +#if defined(CONFIG_CPU_XSCALE) +#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1)) +#else +#define xip_cpu_idle() do { } while (0) +#endif + +#else + +#define __xipram + +#endif /* CONFIG_MTD_XIP */ + +#endif /* __LINUX_MTD_XIP_H__ */ diff -Nur linux-2.6.9-intc1/include/linux/netdevice.h linux-2.6.9-intc1-mih1/include/linux/netdevice.h --- linux-2.6.9-intc1/include/linux/netdevice.h 2004-10-18 17:55:27.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/netdevice.h 2005-03-14 20:11:32.000000000 -0500 @@ -309,7 +309,9 @@ /* List of functions to handle Wireless Extensions (instead of ioctl). * See for details. Jean II */ - struct iw_handler_def * wireless_handlers; + const struct iw_handler_def * wireless_handlers; + /* Instance data managed by the core of Wireless Extensions. */ + struct iw_public_data * wireless_data; struct ethtool_ops *ethtool_ops; diff -Nur linux-2.6.9-intc1/include/linux/serio.h linux-2.6.9-intc1-mih1/include/linux/serio.h --- linux-2.6.9-intc1/include/linux/serio.h 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/serio.h 2005-02-21 22:44:28.000000000 -0500 @@ -167,6 +167,7 @@ #define SERIO_SNES232 0x26 #define SERIO_SEMTECH 0x27 #define SERIO_LKKBD 0x28 +#define SERIO_FASTAP 0x29 #define SERIO_ID 0xff00UL #define SERIO_EXTRA 0xff0000UL diff -Nur linux-2.6.9-intc1/include/linux/spinlock.h linux-2.6.9-intc1-mih1/include/linux/spinlock.h --- linux-2.6.9-intc1/include/linux/spinlock.h 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/spinlock.h 2005-03-24 16:25:20.000000000 -0500 @@ -531,4 +531,13 @@ #endif } +#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED +#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED + +/** + * spin_can_lock - would spin_trylock() succeed? + * @lock: the spinlock in question. + */ +#define spin_can_lock(lock) (!spin_is_locked(lock)) + #endif /* __LINUX_SPINLOCK_H */ diff -Nur linux-2.6.9-intc1/include/linux/wireless.h linux-2.6.9-intc1-mih1/include/linux/wireless.h --- linux-2.6.9-intc1/include/linux/wireless.h 2004-10-18 17:54:38.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/linux/wireless.h 2005-03-14 20:11:32.000000000 -0500 @@ -1,10 +1,10 @@ /* * This file define a set of standard wireless extensions * - * Version : 16 2.4.03 + * Version : 17 21.6.04 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. */ #ifndef _LINUX_WIRELESS_H @@ -47,12 +47,12 @@ * # include/net/iw_handler.h * * Note as well that /proc/net/wireless implementation has now moved in : - * # include/linux/wireless.c + * # net/core/wireless.c * * Wireless Events (2002 -> onward) : * -------------------------------- * Events are defined at the end of this file, and implemented in : - * # include/linux/wireless.c + * # net/core/wireless.c * * Other comments : * -------------- @@ -82,7 +82,7 @@ * (there is some stuff that will be added in the future...) * I just plan to increment with each new version. */ -#define WIRELESS_EXT 16 +#define WIRELESS_EXT 17 /* * Changes : @@ -175,6 +175,13 @@ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) */ /**************************** CONSTANTS ****************************/ @@ -251,7 +258,7 @@ /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ -/* These 16 ioctl are wireless device private. +/* These 32 ioctl are wireless device private, for 16 commands. * Each driver is free to use them for whatever purpose it chooses, * however the driver *must* export the description of those ioctls * with SIOCGIWPRIV and *must* use arguments as defined below. @@ -266,8 +273,8 @@ * We now have 32 commands, so a bit more space ;-). * Also, all 'odd' commands are only usable by root and don't return the * content of ifr/iwr to user (but you are not obliged to use the set/get - * convention, just use every other two command). - * And I repeat : you are not obliged to use them with iwspy, but you + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you * must be compliant with it. */ @@ -352,6 +359,18 @@ #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ #define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x1 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x2 +#define IW_QUAL_NOISE_UPDATED 0x4 +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + /* Maximum number of size of encoding token available * they are listed in the range structure */ #define IW_MAX_ENCODING_SIZES 8 @@ -390,6 +409,7 @@ #define IW_TXPOW_TYPE 0x00FF /* Type of value */ #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ #define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ /* Retry limits and lifetime flags available */ @@ -418,6 +438,25 @@ /* Max number of char in custom event - use multiple of them if needed */ #define IW_CUSTOM_MAX 256 /* In bytes */ +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + /****************************** TYPES ******************************/ /* --------------------------- SUBTYPES --------------------------- */ @@ -456,7 +495,7 @@ __s32 m; /* Mantissa */ __s16 e; /* Exponent */ __u8 i; /* List index (when in range struct) */ - __u8 pad; /* Unused - just for alignement */ + __u8 flags; /* Flags (fixed/auto) */ }; /* @@ -610,11 +649,12 @@ /* Old Frequency (backward compat - moved lower ) */ __u16 old_num_channels; __u8 old_num_frequency; - /* Filler to keep "version" at the same offset */ - __s32 old_freq[6]; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; /* signal level threshold range */ - __s32 sensitivity; + __s32 sensitivity; /* Quality of link & SNR stuff */ /* Quality range (link, level, noise) diff -Nur linux-2.6.9-intc1/include/mtd/mtd-abi.h linux-2.6.9-intc1-mih1/include/mtd/mtd-abi.h --- linux-2.6.9-intc1/include/mtd/mtd-abi.h 2004-10-18 17:54:32.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/mtd/mtd-abi.h 2005-03-21 21:28:40.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: mtd-abi.h,v 1.5 2004/06/22 09:29:35 gleixner Exp $ + * $Id: mtd-abi.h,v 1.10 2005/02/09 09:17:42 pavlov Exp $ * * Portions of MTD ABI definition which are shared by kernel and user space */ @@ -7,6 +7,11 @@ #ifndef __MTD_ABI_H__ #define __MTD_ABI_H__ +#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into + separate files was to avoid #ifdef __KERNEL__ */ +#define __user +#endif + struct erase_info_user { uint32_t start; uint32_t length; @@ -24,6 +29,7 @@ #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 #define MTD_PEROM 5 +#define MTD_DATAFLASH 6 #define MTD_OTHER 14 #define MTD_UNKNOWN 15 @@ -35,6 +41,7 @@ #define MTD_XIP 32 // eXecute-In-Place possible #define MTD_OOB 64 // Out-of-band data (NAND flash) #define MTD_ECC 128 // Device capable of automatic ECC +#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 @@ -55,6 +62,11 @@ #define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme #define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) +/* OTP mode selection */ +#define MTD_OTP_OFF 0 +#define MTD_OTP_FACTORY 1 +#define MTD_OTP_USER 2 + struct mtd_info_user { uint8_t type; uint32_t flags; @@ -74,6 +86,12 @@ uint32_t regionindex; }; +struct otp_info { + uint32_t start; + uint32_t length; + uint32_t locked; +}; + #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) #define MEMERASE _IOW('M', 2, struct erase_info_user) #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) @@ -86,6 +104,10 @@ #define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) #define MEMGETBADBLOCK _IOW('M', 11, loff_t) #define MEMSETBADBLOCK _IOW('M', 12, loff_t) +#define OTPSELECT _IOR('M', 13, int) +#define OTPGETREGIONCOUNT _IOW('M', 14, int) +#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) +#define OTPLOCK _IOR('M', 16, struct otp_info) struct nand_oobinfo { uint32_t useecc; diff -Nur linux-2.6.9-intc1/include/net/iw_handler.h linux-2.6.9-intc1-mih1/include/net/iw_handler.h --- linux-2.6.9-intc1/include/net/iw_handler.h 2004-10-18 17:55:36.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/net/iw_handler.h 2005-03-14 20:11:32.000000000 -0500 @@ -1,10 +1,10 @@ /* * This file define the new driver API for Wireless Extensions * - * Version : 5 4.12.02 + * Version : 6 21.6.04 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 2001-2004 Jean Tourrilhes, All Rights Reserved. */ #ifndef _IW_HANDLER_H @@ -206,7 +206,7 @@ * will be needed... * I just plan to increment with each new version. */ -#define IW_HANDLER_VERSION 5 +#define IW_HANDLER_VERSION 6 /* * Changes : @@ -224,11 +224,18 @@ * V4 to V5 * -------- * - Add new spy support : struct iw_spy_data & prototypes + * + * V5 to V6 + * -------- + * - Change the way we get to spy_data method for added safety + * - Remove spy #ifdef, they are always on -> cleaner code + * - Add IW_DESCR_FLAG_NOMAX flag for very large requests + * - Start migrating get_wireless_stats to struct iw_handler_def */ /**************************** CONSTANTS ****************************/ -/* Enable enhanced spy support. Disable to reduce footprint */ +/* Enhanced spy support available */ #define IW_WIRELESS_SPY #define IW_WIRELESS_THRSPY @@ -258,6 +265,7 @@ #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ #define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ /* SET : Omit payload from generated iwevent */ +#define IW_DESCR_FLAG_NOMAX 0x0008 /* GET : no limit on request size */ /* Driver level flags */ #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ @@ -311,23 +319,25 @@ /* Array of handlers for standard ioctls * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] */ - iw_handler * standard; + const iw_handler * standard; /* Array of handlers for private ioctls * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] */ - iw_handler * private; + const iw_handler * private; /* Arguments of private handler. This one is just a list, so you * can put it in any order you want and should not leave holes... * We will automatically export that to user space... */ - struct iw_priv_args * private_args; + const struct iw_priv_args * private_args; - /* Driver enhanced spy support */ - long spy_offset; /* Spy data offset */ + /* This field will be *removed* in the next version of WE */ + long spy_offset; /* DO NOT USE */ - /* In the long term, get_wireless_stats will move from - * 'struct net_device' to here, to minimise bloat. */ + /* New location of get_wireless_stats, to de-bloat struct net_device. + * The old pointer in struct net_device will be gradually phased + * out, and drivers are encouraged to use this one... */ + struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); }; /* ---------------------- IOCTL DESCRIPTION ---------------------- */ @@ -374,18 +384,29 @@ */ struct iw_spy_data { -#ifdef IW_WIRELESS_SPY /* --- Standard spy support --- */ int spy_number; u_char spy_address[IW_MAX_SPY][ETH_ALEN]; struct iw_quality spy_stat[IW_MAX_SPY]; -#ifdef IW_WIRELESS_THRSPY /* --- Enhanced spy support (event) */ struct iw_quality spy_thr_low; /* Low threshold */ struct iw_quality spy_thr_high; /* High threshold */ u_char spy_thr_under[IW_MAX_SPY]; -#endif /* IW_WIRELESS_THRSPY */ -#endif /* IW_WIRELESS_SPY */ +}; + +/* --------------------- DEVICE WIRELESS DATA --------------------- */ +/* + * This is all the wireless data specific to a device instance that + * is managed by the core of Wireless Extensions. + * We only keep pointer to those structures, so that a driver is free + * to share them between instances. + * This structure should be initialised before registering the device. + * Access to this data follow the same rules as any other struct net_device + * data (i.e. valid as long as struct net_device exist, same locking rules). + */ +struct iw_public_data { + /* Driver enhanced spy support */ + struct iw_spy_data * spy_data; }; /**************************** PROTOTYPES ****************************/ @@ -394,6 +415,9 @@ * Those may be called only within the kernel. */ +/* Data needed by fs/compat_ioctl.c for 32->64 bit conversion */ +extern const char iw_priv_type_size[]; + /* First : function strictly used inside the kernel */ /* Handle /proc/net/wireless, called in net/code/dev.c */ diff -Nur linux-2.6.9-intc1/include/sound/ac97_codec.h linux-2.6.9-intc1-mih1/include/sound/ac97_codec.h --- linux-2.6.9-intc1/include/sound/ac97_codec.h 2004-10-18 17:54:31.000000000 -0400 +++ linux-2.6.9-intc1-mih1/include/sound/ac97_codec.h 2005-04-04 23:23:40.000000000 -0400 @@ -26,6 +26,7 @@ */ #include +#include #include "control.h" #include "info.h" @@ -491,6 +492,7 @@ } ad18xx; unsigned int dev_flags; /* device specific */ } spec; + struct device dev; }; /* conditions */ @@ -518,6 +520,7 @@ /* functions */ int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops, void *private_data, ac97_bus_t **rbus); /* create new AC97 bus */ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97); /* create mixer controls */ +const char *snd_ac97_get_short_name(ac97_t *ac97); void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value); unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg); @@ -558,4 +561,7 @@ enum ac97_pcm_cfg cfg, unsigned short slots); int snd_ac97_pcm_close(struct ac97_pcm *pcm); +/* for ad hoc drivers... */ +extern struct bus_type ac97_bus_type; + #endif /* __SOUND_AC97_CODEC_H */ diff -Nur linux-2.6.9-intc1/MAINTAINERS linux-2.6.9-intc1-mih1/MAINTAINERS --- linux-2.6.9-intc1/MAINTAINERS 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/MAINTAINERS 2005-03-10 18:39:05.000000000 -0500 @@ -934,6 +934,13 @@ L: iss_storagedev@hp.com S: Supported +HOST AP DRIVER +P: Jouni Malinen +M: jkmaline@cc.hut.fi +L: hostap@shmoo.com +W: http://hostap.epitest.fi/ +S: Maintained + HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series P: Jaroslav Kysela M: perex@suse.cz diff -Nur linux-2.6.9-intc1/Makefile linux-2.6.9-intc1-mih1/Makefile --- linux-2.6.9-intc1/Makefile 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/Makefile 2005-04-21 15:55:39.000000000 -0400 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 9 -EXTRAVERSION = -intc1 +EXTRAVERSION = -intc1-mih1 NAME=Zonked Quokka # *DOCUMENTATION* diff -Nur linux-2.6.9-intc1/mm/memory.c linux-2.6.9-intc1-mih1/mm/memory.c --- linux-2.6.9-intc1/mm/memory.c 2004-10-18 17:54:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/mm/memory.c 2005-03-24 17:30:30.000000000 -0500 @@ -990,6 +990,15 @@ EXPORT_SYMBOL(remap_page_range); +inline int remap_pfn_range(struct vm_area_struct *vma, + unsigned long uvaddr, + unsigned long paddr, + unsigned long size, pgprot_t prot) +{ + return remap_page_range(vma, uvaddr, paddr << PAGE_SHIFT, size, prot); +} +EXPORT_SYMBOL(remap_pfn_range); + /* * Do pte_mkwrite, but only if the vma says VM_WRITE. We do this when * servicing faults for write access. In the normal case, do always want diff -Nur linux-2.6.9-intc1/net/core/dev.c linux-2.6.9-intc1-mih1/net/core/dev.c --- linux-2.6.9-intc1/net/core/dev.c 2004-10-18 17:54:08.000000000 -0400 +++ linux-2.6.9-intc1-mih1/net/core/dev.c 2005-03-14 20:11:32.000000000 -0500 @@ -2745,7 +2745,7 @@ /* Follow me in net/core/wireless.c */ ret = wireless_process_ioctl(&ifr, cmd); rtnl_unlock(); - if (!ret && IW_IS_GET(cmd) && + if (IW_IS_GET(cmd) && copy_to_user(arg, &ifr, sizeof(struct ifreq))) ret = -EFAULT; diff -Nur linux-2.6.9-intc1/net/core/wireless.c linux-2.6.9-intc1-mih1/net/core/wireless.c --- linux-2.6.9-intc1/net/core/wireless.c 2004-10-18 17:55:43.000000000 -0400 +++ linux-2.6.9-intc1-mih1/net/core/wireless.c 2005-03-14 20:11:32.000000000 -0500 @@ -2,7 +2,7 @@ * This file implement the Wireless Extensions APIs. * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) */ @@ -48,6 +48,16 @@ * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() * o Add enhanced spy support : iw_handler_set_thrspy() and event. * o Add WIRELESS_EXT version display in /proc/net/wireless + * + * v6 - 18.06.04 - Jean II + * o Change get_spydata() method for added safety + * o Remove spy #ifdef, they are always on -> cleaner code + * o Allow any size GET request if user specifies length > max + * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV + * o Start migrating get_wireless_stats to struct iw_handler_def + * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus + * Based on patch from Pavel Roskin : + * o Fix kernel data leak to user space in private handler handling */ /***************************** INCLUDES *****************************/ @@ -69,10 +79,6 @@ /**************************** CONSTANTS ****************************/ -/* Enough lenience, let's make sure things are proper... */ -#define WE_STRICT_WRITE /* Check write buffer size */ -/* I'll probably drop both the define and kernel message in the next version */ - /* Debugging stuff */ #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ @@ -186,6 +192,7 @@ .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, + .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWSCAN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, @@ -194,6 +201,7 @@ .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, + .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWESSID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, @@ -296,7 +304,7 @@ sizeof(struct iw_ioctl_description)); /* Size (in bytes) of the various private data types */ -static const char priv_type_size[] = { +const char iw_priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */ @@ -363,12 +371,15 @@ */ static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) { + /* New location */ + if((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); + + /* Old location, will be phased out in next WE */ return (dev->get_wireless_stats ? dev->get_wireless_stats(dev) : (struct iw_statistics *) NULL); - /* In the future, get_wireless_stats may move from 'struct net_device' - * to 'struct iw_handler_def', to de-bloat struct net_device. - * Definitely worse a thought... */ } /* ---------------------------------------------------------------- */ @@ -403,14 +414,32 @@ /* ---------------------------------------------------------------- */ /* - * Number of private arguments + * Calculate size of private arguments */ static inline int get_priv_size(__u16 args) { int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; - return num * priv_type_size[type]; + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Re-calculate the size of private arguments + */ +static inline int adjust_priv_size(__u16 args, + union iwreq_data * wrqu) +{ + int num = wrqu->data.length; + int max = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + /* Make sure the driver doesn't goof up */ + if (max < num) + num = max; + + return num * iw_priv_type_size[type]; } @@ -440,11 +469,14 @@ seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " "%6d %6d %6d\n", dev->name, stats->status, stats->qual.qual, - stats->qual.updated & 1 ? '.' : ' ', + stats->qual.updated & IW_QUAL_QUAL_UPDATED + ? '.' : ' ', ((__u8) stats->qual.level), - stats->qual.updated & 2 ? '.' : ' ', + stats->qual.updated & IW_QUAL_LEVEL_UPDATED + ? '.' : ' ', ((__u8) stats->qual.noise), - stats->qual.updated & 4 ? '.' : ' ', + stats->qual.updated & IW_QUAL_NOISE_UPDATED + ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); @@ -555,13 +587,15 @@ /* Check NULL pointer */ if(iwr->u.data.pointer == NULL) return -EFAULT; -#ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { - printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); + /* User space can't know in advance how large the buffer + * needs to be. Give it a hint, so that we can support + * any size buffer we want somewhat efficiently... */ + iwr->u.data.length = dev->wireless_handlers->num_private_args; return -E2BIG; } -#endif /* WE_STRICT_WRITE */ /* Set the number of available ioctls. */ iwr->u.data.length = dev->wireless_handlers->num_private_args; @@ -590,7 +624,6 @@ const struct iw_ioctl_description * descr; struct iw_request_info info; int ret = -EINVAL; - int user_size = 0; /* Get the description of the IOCTL */ if((cmd - SIOCIWFIRST) >= standard_ioctl_num) @@ -621,8 +654,14 @@ #endif /* WE_SET_EVENT */ } else { char * extra; + int extra_size; + int user_length = 0; int err; + /* Calculate space needed by arguments. Always allocate + * for max space. Easier, and won't last long... */ + extra_size = descr->max_tokens * descr->token_size; + /* Check what user space is giving us */ if(IW_IS_SET(cmd)) { /* Check NULL pointer */ @@ -639,18 +678,33 @@ if(iwr->u.data.pointer == NULL) return -EFAULT; /* Save user space buffer size for checking */ - user_size = iwr->u.data.length; + user_length = iwr->u.data.length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. */ + + /* Support for very large requests */ + if((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. */ + extra_size = user_length * descr->token_size; + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... */ + } } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", - dev->name, descr->max_tokens * descr->token_size); + dev->name, extra_size); #endif /* WE_IOCTL_DEBUG */ - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(descr->max_tokens * descr->token_size, - GFP_KERNEL); + /* Create the kernel buffer */ + extra = kmalloc(extra_size, GFP_KERNEL); if (extra == NULL) { return -ENOMEM; } @@ -676,14 +730,11 @@ /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { -#ifdef WE_STRICT_WRITE /* Check if there is enough buffer up there */ - if(user_size < iwr->u.data.length) { - printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); + if(user_length < iwr->u.data.length) { kfree(extra); return -E2BIG; } -#endif /* WE_STRICT_WRITE */ err = copy_to_user(iwr->u.data.pointer, extra, iwr->u.data.length * @@ -746,7 +797,7 @@ iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; - struct iw_priv_args * descr = NULL; + const struct iw_priv_args * descr = NULL; struct iw_request_info info; int extra_size = 0; int i; @@ -786,7 +837,7 @@ ((extra_size + offset) <= IFNAMSIZ)) extra_size = 0; } else { - /* Size of set arguments */ + /* Size of get arguments */ extra_size = get_priv_size(descr->get_args); /* Does it fits in iwr ? */ @@ -856,6 +907,14 @@ /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { + + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { + extra_size = adjust_priv_size(descr->get_args, + &(iwr->u)); + } + err = copy_to_user(iwr->u.data.pointer, extra, extra_size); if (err) @@ -1127,9 +1186,25 @@ * One of the main advantage of centralising spy support here is that * it becomes much easier to improve and extend it without having to touch * the drivers. One example is the addition of the Spy-Threshold events. - * Note : IW_WIRELESS_SPY is defined in iw_handler.h */ +/* ---------------------------------------------------------------- */ +/* + * Return the pointer to the spy data in the driver. + * Because this is called on the Rx path via wireless_spy_update(), + * we want it to be efficient... + */ +static inline struct iw_spy_data * get_spydata(struct net_device *dev) +{ + /* This is the new way */ + if(dev->wireless_data) + return(dev->wireless_data->spy_data); + + /* This is the old way. Doesn't work for multi-headed drivers. + * It will be removed in the next version of WE. */ + return (dev->priv + dev->wireless_handlers->spy_offset); +} + /*------------------------------------------------------------------*/ /* * Standard Wireless Handler : set Spy List @@ -1139,16 +1214,30 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; + if(!dev->wireless_data) + /* Help user know that driver needs updating */ + printk(KERN_DEBUG "%s (WE) : Driver using old/buggy spy support, please fix driver !\n", + dev->name); + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Disable spy collection while we copy the addresses. - * As we don't disable interrupts, we need to do this to avoid races. - * As we are the only writer, this is good enough. */ + * While we copy addresses, any call to wireless_spy_update() + * will NOP. This is OK, as anyway the addresses are changing. */ spydata->spy_number = 0; + /* We want to operate without locking, because wireless_spy_update() + * most likely will happen in the interrupt handler, and therefore + * have its own locking constraints and needs performance. + * The rtnl_lock() make sure we don't race with the other iw_handlers. + * This make sure wireless_spy_update() "see" that the spy list + * is temporarily disabled. */ + wmb(); + /* Are there are addresses to copy? */ if(wrqu->data.length > 0) { int i; @@ -1174,13 +1263,14 @@ spydata->spy_address[i][5]); #endif /* WE_SPY_DEBUG */ } + + /* Make sure above is updated before re-enabling */ + wmb(); + /* Enable addresses */ spydata->spy_number = wrqu->data.length; return 0; -#else /* IW_WIRELESS_SPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_SPY */ } /*------------------------------------------------------------------*/ @@ -1192,12 +1282,14 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; int i; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + wrqu->data.length = spydata->spy_number; /* Copy addresses. */ @@ -1214,9 +1306,6 @@ for(i = 0; i < spydata->spy_number; i++) spydata->spy_stat[i].updated = 0; return 0; -#else /* IW_WIRELESS_SPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_SPY */ } /*------------------------------------------------------------------*/ @@ -1228,11 +1317,13 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_THRSPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Just do it */ memcpy(&(spydata->spy_thr_low), &(threshold->low), 2 * sizeof(struct iw_quality)); @@ -1245,9 +1336,6 @@ #endif /* WE_SPY_DEBUG */ return 0; -#else /* IW_WIRELESS_THRSPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_THRSPY */ } /*------------------------------------------------------------------*/ @@ -1259,22 +1347,20 @@ union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_THRSPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Just do it */ memcpy(&(threshold->low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality)); return 0; -#else /* IW_WIRELESS_THRSPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_THRSPY */ } -#ifdef IW_WIRELESS_THRSPY /*------------------------------------------------------------------*/ /* * Prepare and send a Spy Threshold event @@ -1312,7 +1398,6 @@ /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); } -#endif /* IW_WIRELESS_THRSPY */ /* ---------------------------------------------------------------- */ /* @@ -1325,12 +1410,14 @@ unsigned char * address, struct iw_quality * wstats) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); int i; int match = -1; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return; + #ifdef WE_SPY_DEBUG printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); #endif /* WE_SPY_DEBUG */ @@ -1342,7 +1429,7 @@ sizeof(struct iw_quality)); match = i; } -#ifdef IW_WIRELESS_THRSPY + /* Generate an event if we cross the spy threshold. * To avoid event storms, we have a simple hysteresis : we generate * event only when we go under the low threshold or above the @@ -1362,8 +1449,6 @@ } } } -#endif /* IW_WIRELESS_THRSPY */ -#endif /* IW_WIRELESS_SPY */ } EXPORT_SYMBOL(iw_handler_get_spy); diff -Nur linux-2.6.9-intc1/sound/arm/Kconfig linux-2.6.9-intc1-mih1/sound/arm/Kconfig --- linux-2.6.9-intc1/sound/arm/Kconfig 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/arm/Kconfig 2005-04-04 12:36:13.000000000 -0400 @@ -11,5 +11,20 @@ Say Y or M if you have a Compaq iPaq H3x00 handheld computer and want to use its Philips UDA 1341 audio chip. +config SND_PXA2XX_AC97 + tristate "AC97 driver for the Intel PXA2xx chip" + depends on ARCH_PXA && SND + select SND_PXA2XX_PCM + select SND_AC97_CODEC + help + Say Y or M if you want to support any AC97 codec attached to + the PXA2xx AC97 interface. + +config SND_PXA2XX_PCM + tristate + select SND_PCM + help + Generic PCM module for PXA2xx + endmenu diff -Nur linux-2.6.9-intc1/sound/arm/Makefile linux-2.6.9-intc1-mih1/sound/arm/Makefile --- linux-2.6.9-intc1/sound/arm/Makefile 2004-10-18 17:55:07.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/arm/Makefile 2005-04-04 12:37:06.000000000 -0400 @@ -3,6 +3,10 @@ # snd-sa11xx-uda1341-objs := sa11xx-uda1341.o +snd-pxa2xx-pcm-objs := pxa2xx-pcm.o +snd-pxa2xx-ac97-objs := pxa2xx-ac97.o # Toplevel Module Dependency obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o +obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o +obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o diff -Nur linux-2.6.9-intc1/sound/arm/pxa2xx-ac97.c linux-2.6.9-intc1-mih1/sound/arm/pxa2xx-ac97.c --- linux-2.6.9-intc1/sound/arm/pxa2xx-ac97.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/arm/pxa2xx-ac97.c 2005-04-02 01:36:56.000000000 -0500 @@ -0,0 +1,410 @@ +/* + * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. + * + * Author: Nicolas Pitre + * Created: Dec 02, 2004 + * Copyright: MontaVista Software Inc. + * + * 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 +#include + +#include "pxa2xx-pcm.h" + + +static DECLARE_MUTEX(car_mutex); +static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); +static volatile long gsr_bits; + +static unsigned short pxa2xx_ac97_read(ac97_t *ac97, unsigned short reg) +{ + unsigned short val = -1; + volatile u32 *reg_addr; + + down(&car_mutex); + if (CAR & CAR_CAIP) { + printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__); + goto out; + } + + /* set up primary or secondary codec space */ + reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr += (reg >> 1); + + /* start read access across the ac97 link */ + gsr_bits = 0; + val = *reg_addr; + if (reg == AC97_GPIO_STATUS) + goto out; + wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1); + if (!gsr_bits & GSR_SDONE) { + printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", + __FUNCTION__, reg, gsr_bits); + val = -1; + goto out; + } + + /* valid data now */ + gsr_bits = 0; + val = *reg_addr; + /* but we've just started another cycle... */ + wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1); + +out: up(&car_mutex); + return val; +} + +static void pxa2xx_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + volatile u32 *reg_addr; + + down(&car_mutex); + + if (CAR & CAR_CAIP) { + printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__); + goto out; + } + + /* set up primary or secondary codec space */ + reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr += (reg >> 1); + gsr_bits = 0; + *reg_addr = val; + wait_event_timeout(gsr_wq, gsr_bits & GSR_CDONE, 1); + if (!gsr_bits & GSR_SDONE) + printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", + __FUNCTION__, reg, gsr_bits); + +out: up(&car_mutex); +} + +static void pxa2xx_ac97_reset(ac97_t *ac97) +{ + /* First, try cold reset */ + GCR &= GCR_COLD_RST; /* clear everything but nCRST */ + GCR &= ~GCR_COLD_RST; /* then assert nCRST */ + + gsr_bits = 0; +#ifdef CONFIG_PXA27x + /* PXA27x Developers Manual section 13.5.2.2.1 */ + pxa_set_cken(1 << 31, 1); + udelay(5); + pxa_set_cken(1 << 31, 0); + GCR = GCR_COLD_RST; + udelay(50); +#else + GCR = GCR_COLD_RST; + GCR |= GCR_CDONE_IE|GCR_SDONE_IE; + wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); +#endif + + if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) { + printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", + __FUNCTION__, gsr_bits); + + /* let's try warm reset */ + gsr_bits = 0; +#ifdef CONFIG_PXA27x + /* warm reset broken on Bulverde, + so manually keep AC97 reset high */ + pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); + udelay(10); + GCR |= GCR_WARM_RST; + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); + udelay(50); +#else + GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;; + wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); +#endif + + if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) + printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", + __FUNCTION__, gsr_bits); + } + + GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); + GCR |= GCR_SDONE_IE|GCR_CDONE_IE; +} + +static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + long status; + + status = GSR; + if (status) { + GSR = status; + gsr_bits |= status; + wake_up(&gsr_wq); + +#ifdef CONFIG_PXA27x + /* Although we don't use those we still need to clear them + since they tend to spuriously trigger when MMC is used + (hardware bug? go figure)... */ + MISR = MISR_EOC; + PISR = PISR_EOC; + MCSR = MCSR_EOC; +#endif + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static ac97_bus_ops_t pxa2xx_ac97_ops = { + .read = pxa2xx_ac97_read, + .write = pxa2xx_ac97_write, + .reset = pxa2xx_ac97_reset, +}; + +static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_out = { + .name = "AC97 PCM out", + .dev_addr = __PREG(PCDR), + .drcmr = &DRCMRTXPCDR, + .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_in = { + .name = "AC97 PCM in", + .dev_addr = __PREG(PCDR), + .drcmr = &DRCMRRXPCDR, + .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static snd_pcm_t *pxa2xx_ac97_pcm; +static ac97_t *pxa2xx_ac97_ac97; + +static int pxa2xx_ac97_pcm_startup(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + pxa2xx_audio_ops_t *platform_ops; + int r; + + runtime->hw.channels_min = 2; + runtime->hw.channels_max = 2; + + r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_RATES_FRONT_DAC : AC97_RATES_ADC; + runtime->hw.rates = pxa2xx_ac97_ac97->rates[r]; + snd_pcm_limit_hw_rates(runtime); + + platform_ops = substream->pcm->card->dev->platform_data; + if (platform_ops && platform_ops->startup) + return platform_ops->startup(substream, platform_ops->priv); + else + return 0; +} + +static void pxa2xx_ac97_pcm_shutdown(snd_pcm_substream_t *substream) +{ + pxa2xx_audio_ops_t *platform_ops; + + platform_ops = substream->pcm->card->dev->platform_data; + if (platform_ops && platform_ops->shutdown) + platform_ops->shutdown(substream, platform_ops->priv); +} + +static int pxa2xx_ac97_pcm_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; + return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); +} + +static pxa2xx_pcm_client_t pxa2xx_ac97_pcm_client = { + .playback_params = &pxa2xx_ac97_pcm_out, + .capture_params = &pxa2xx_ac97_pcm_in, + .startup = pxa2xx_ac97_pcm_startup, + .shutdown = pxa2xx_ac97_pcm_shutdown, + .prepare = pxa2xx_ac97_pcm_prepare, +}; + +#ifdef CONFIG_PM + +static int pxa2xx_ac97_do_suspend(snd_card_t *card, unsigned int state) +{ + if (card->power_state != SNDRV_CTL_POWER_D3cold) { + pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; + snd_pcm_suspend_all(pxa2xx_ac97_pcm); + snd_ac97_suspend(pxa2xx_ac97_ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + if (platform_ops && platform_ops->suspend) + platform_ops->suspend(platform_ops->priv); + GCR |= GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + } + + return 0; +} + +static int pxa2xx_ac97_do_resume(snd_card_t *card, unsigned int state) +{ + if (card->power_state != SNDRV_CTL_POWER_D0) { + pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; + pxa_set_cken(CKEN2_AC97, 1); + if (platform_ops && platform_ops->resume) + platform_ops->resume(platform_ops->priv); + snd_ac97_resume(pxa2xx_ac97_ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + } + + return 0; +} + +static int pxa2xx_ac97_suspend(struct device *_dev, u32 state, u32 level) +{ + snd_card_t *card = dev_get_drvdata(_dev); + int ret = 0; + + if (card && level == SUSPEND_DISABLE) + ret = pxa2xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold); + + return ret; +} + +static int pxa2xx_ac97_resume(struct device *_dev, u32 level) +{ + snd_card_t *card = dev_get_drvdata(_dev); + int ret = 0; + + if (card && level == RESUME_ENABLE) + ret = pxa2xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0); + + return ret; +} + +#else +#define pxa2xx_ac97_suspend NULL +#define pxa2xx_ac97_resume NULL +#endif + +static int pxa2xx_ac97_probe(struct device *dev) +{ + snd_card_t *card; + ac97_bus_t *ac97_bus; + ac97_template_t ac97_template; + int ret; + + ret = -ENOMEM; + card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0); + if (!card) + goto err; + + card->dev = dev; + strncpy(card->driver, dev->driver->name, sizeof(card->driver)); + + ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); + if (ret) + goto err; + + ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL); + if (ret < 0) + goto err; + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); +#ifdef CONFIG_PXA27x + /* Use GPIO 113 as AC97 Reset on Bulverde */ + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +#endif + pxa_set_cken(CKEN2_AC97, 1); + + ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus); + if (ret) + goto err; + memset(&ac97_template, 0, sizeof(ac97_template)); + ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97); + if (ret) + goto err; + + snprintf(card->shortname, sizeof(card->shortname), + "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97)); + snprintf(card->longname, sizeof(card->longname), + "%s (%s)", dev->driver->name, card->mixername); + + snd_card_set_pm_callback(card, pxa2xx_ac97_do_suspend, + pxa2xx_ac97_do_resume, NULL); + ret = snd_card_register(card); + if (ret == 0) { + dev_set_drvdata(dev, card); + return 0; + } + + err: + if (card) + snd_card_free(card); + if (CKEN & CKEN2_AC97) { + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN2_AC97, 0); + } + return ret; +} + +static int pxa2xx_ac97_remove(struct device *dev) +{ + snd_card_t *card = dev_get_drvdata(dev); + + if (card) { + snd_card_free(card); + dev_set_drvdata(dev, NULL); + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN2_AC97, 0); + } + + return 0; +} + +static struct device_driver pxa2xx_ac97_driver = { + .name = "pxa2xx-ac97", + .bus = &platform_bus_type, + .probe = pxa2xx_ac97_probe, + .remove = pxa2xx_ac97_remove, + .suspend = pxa2xx_ac97_suspend, + .resume = pxa2xx_ac97_resume, +}; + +static int __init pxa2xx_ac97_init(void) +{ + return driver_register(&pxa2xx_ac97_driver); +} + +static void __exit pxa2xx_ac97_exit(void) +{ + driver_unregister(&pxa2xx_ac97_driver); +} + +module_init(pxa2xx_ac97_init); +module_exit(pxa2xx_ac97_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/sound/arm/pxa2xx-pcm.c linux-2.6.9-intc1-mih1/sound/arm/pxa2xx-pcm.c --- linux-2.6.9-intc1/sound/arm/pxa2xx-pcm.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/arm/pxa2xx-pcm.c 2005-04-02 01:36:56.000000000 -0500 @@ -0,0 +1,367 @@ +/* + * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: (C) 2004 MontaVista Software, Inc. + * + * 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 "pxa2xx-pcm.h" + + +static const snd_pcm_hardware_t pxa2xx_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = 32, + .period_bytes_max = 8192 - 32, + .periods_min = 1, + .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), + .buffer_bytes_max = 128 * 1024, + .fifo_size = 32, +}; + +struct pxa2xx_runtime_data { + int dma_ch; + pxa2xx_pcm_dma_params_t *params; + pxa_dma_desc *dma_desc_array; + dma_addr_t dma_desc_array_phys; +}; + +static int pxa2xx_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + struct pxa2xx_runtime_data *rtd = runtime->private_data; + size_t totsize = params_buffer_bytes(params); + size_t period = params_period_bytes(params); + pxa_dma_desc *dma_desc; + dma_addr_t dma_buff_phys, next_desc_phys; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = totsize; + + dma_desc = rtd->dma_desc_array; + next_desc_phys = rtd->dma_desc_array_phys; + dma_buff_phys = runtime->dma_addr; + do { + next_desc_phys += sizeof(pxa_dma_desc); + dma_desc->ddadr = next_desc_phys; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_desc->dsadr = dma_buff_phys; + dma_desc->dtadr = rtd->params->dev_addr; + } else { + dma_desc->dsadr = rtd->params->dev_addr; + dma_desc->dtadr = dma_buff_phys; + } + if (period > totsize) + period = totsize; + dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc++; + dma_buff_phys += period; + } while (totsize -= period); + dma_desc[-1].ddadr = rtd->dma_desc_array_phys; + + return 0; +} + +static int pxa2xx_pcm_hw_free(snd_pcm_substream_t *substream) +{ + struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; + + *rtd->params->drcmr = 0; + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +} + +static int pxa2xx_pcm_prepare(snd_pcm_substream_t *substream) +{ + pxa2xx_pcm_client_t *client = substream->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + struct pxa2xx_runtime_data *rtd = runtime->private_data; + + DCSR(rtd->dma_ch) &= ~DCSR_RUN; + DCSR(rtd->dma_ch) = 0; + DCMD(rtd->dma_ch) = 0; + *rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD; + + return client->prepare(substream); +} + +static int pxa2xx_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys; + DCSR(rtd->dma_ch) = DCSR_RUN; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + DCSR(rtd->dma_ch) &= ~DCSR_RUN; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + DCSR(rtd->dma_ch) |= DCSR_RUN; + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id, struct pt_regs *regs) +{ + snd_pcm_substream_t *substream = dev_id; + struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; + int dcsr; + + dcsr = DCSR(dma_ch); + DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; + + if (dcsr & DCSR_ENDINTR) { + snd_pcm_period_elapsed(substream); + } else { + printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", + rtd->params->name, dma_ch, dcsr ); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + } +} + +static snd_pcm_uframes_t pxa2xx_pcm_pointer(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + struct pxa2xx_runtime_data *rtd = runtime->private_data; + dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch); + snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); + if (x == runtime->buffer_size) + x = 0; + return x; +} + +static int +pxa2xx_pcm_hw_rule_mult32(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *i = hw_param_interval(params, rule->var); + int changed = 0; + + if (i->min & 31) { + i->min = (i->min & ~31) + 32; + i->openmin = 0; + changed = 1; + } + + if (i->max & 31) { + i->max &= ~31; + i->openmax = 0; + changed = 1; + } + + return changed; +} + +static int pxa2xx_pcm_open(snd_pcm_substream_t *substream) +{ + pxa2xx_pcm_client_t *client = substream->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + struct pxa2xx_runtime_data *rtd; + int ret; + + runtime->hw = pxa2xx_pcm_hardware; + + /* + * For mysterious reasons (and despite what the manual says) + * playback samples are lost if the DMA count is not a multiple + * of the DMA burst size. Let's add a rule to enforce that. + */ + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + pxa2xx_pcm_hw_rule_mult32, NULL, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1); + if (ret) + goto out; + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + pxa2xx_pcm_hw_rule_mult32, NULL, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); + if (ret) + goto out; + + ret = -ENOMEM; + rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); + if (!rtd) + goto out; + rtd->dma_desc_array = + dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, + &rtd->dma_desc_array_phys, GFP_KERNEL); + if (!rtd->dma_desc_array) + goto err1; + + rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + client->playback_params : client->capture_params; + ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + pxa2xx_pcm_dma_irq, substream); + if (ret < 0) + goto err2; + rtd->dma_ch = ret; + + runtime->private_data = rtd; + ret = client->startup(substream); + if (!ret) + goto out; + + pxa_free_dma(rtd->dma_ch); + err2: + dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, + rtd->dma_desc_array, rtd->dma_desc_array_phys); + err1: + kfree(rtd); + out: + return ret; +} + +static int pxa2xx_pcm_close(snd_pcm_substream_t *substream) +{ + pxa2xx_pcm_client_t *client = substream->private_data; + struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; + + pxa_free_dma(rtd->dma_ch); + client->shutdown(substream); + dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, + rtd->dma_desc_array, rtd->dma_desc_array_phys); + kfree(rtd); + return 0; +} + +static int +pxa2xx_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static snd_pcm_ops_t pxa2xx_pcm_ops = { + .open = pxa2xx_pcm_open, + .close = pxa2xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pxa2xx_pcm_hw_params, + .hw_free = pxa2xx_pcm_hw_free, + .prepare = pxa2xx_pcm_prepare, + .trigger = pxa2xx_pcm_trigger, + .pointer = pxa2xx_pcm_pointer, + .mmap = pxa2xx_pcm_mmap, +}; + +static int pxa2xx_pcm_preallocate_dma_buffer(snd_pcm_t *pcm, int stream) +{ + snd_pcm_substream_t *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + return 0; +} + +static void pxa2xx_pcm_free_dma_buffers(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + buf = &substream->dma_buffer; + if (!buf->area) + continue; + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 pxa2xx_pcm_dmamask = 0xffffffff; + +int pxa2xx_pcm_new(snd_card_t *card, pxa2xx_pcm_client_t *client, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int play = client->playback_params ? 1 : 0; + int capt = client->capture_params ? 1 : 0; + int ret; + + ret = snd_pcm_new(card, "PXA2xx-PCM", 0, play, capt, &pcm); + if (ret) + goto out; + + pcm->private_data = client; + pcm->private_free = pxa2xx_pcm_free_dma_buffers; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &pxa2xx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (play) { + int stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops); + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); + if (ret) + goto out; + } + if (capt) { + int stream = SNDRV_PCM_STREAM_CAPTURE; + snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops); + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); + if (ret) + goto out; + } + + if (rpcm) + *rpcm = pcm; + ret = 0; + + out: + return ret; +} + +EXPORT_SYMBOL(pxa2xx_pcm_new); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/sound/arm/pxa2xx-pcm.h linux-2.6.9-intc1-mih1/sound/arm/pxa2xx-pcm.h --- linux-2.6.9-intc1/sound/arm/pxa2xx-pcm.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/arm/pxa2xx-pcm.h 2005-04-02 01:36:56.000000000 -0500 @@ -0,0 +1,29 @@ +/* + * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: MontaVista Software, Inc. + * + * 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. + */ + +typedef struct { + char *name; /* stream identifier */ + u32 dcmd; /* DMA descriptor dcmd field */ + volatile u32 *drcmr; /* the DMA request channel to use */ + u32 dev_addr; /* device physical address for DMA */ +} pxa2xx_pcm_dma_params_t; + +typedef struct { + pxa2xx_pcm_dma_params_t *playback_params; + pxa2xx_pcm_dma_params_t *capture_params; + int (*startup)(snd_pcm_substream_t *); + void (*shutdown)(snd_pcm_substream_t *); + int (*prepare)(snd_pcm_substream_t *); +} pxa2xx_pcm_client_t; + +extern int pxa2xx_pcm_new(snd_card_t *, pxa2xx_pcm_client_t *, snd_pcm_t **); + diff -Nur linux-2.6.9-intc1/sound/oss/ac97_codec.c linux-2.6.9-intc1-mih1/sound/oss/ac97_codec.c --- linux-2.6.9-intc1/sound/oss/ac97_codec.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/ac97_codec.c 2005-05-01 02:25:14.000000000 -0400 @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -133,6 +134,9 @@ {0x41445348, "Analog Devices AD1881A", &null_ops}, {0x41445360, "Analog Devices AD1885", &default_ops}, {0x41445361, "Analog Devices AD1886", &ad1886_ops}, + {0x41445370, "Analog Devices AD1981", &null_ops}, + {0x41445372, "Analog Devices AD1981A", &null_ops}, + {0x41445374, "Analog Devices AD1981B", &null_ops}, {0x41445460, "Analog Devices AD1885", &default_ops}, {0x41445461, "Analog Devices AD1886", &ad1886_ops}, {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, @@ -466,6 +470,13 @@ { unsigned int left,right; +#ifdef CONFIG_SOUND_NO_SPEAKER_INIT + if (oss_mixer == SOUND_MIXER_SPEAKER) { + printk ("ac97_codec:set_mixer: Not setting channel %d.\n",oss_mixer); + return; + } +#endif + /* cleanse input a little */ right = ((val >> 8) & 0xff) ; left = (val & 0xff) ; @@ -826,6 +837,7 @@ /* will the codec power down if register reset ? */ id1 = codec->codec_read(codec, AC97_VENDOR_ID1); id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + codec->name = NULL; codec->codec_ops = &null_ops; for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { @@ -1333,7 +1345,7 @@ unsigned int new_rate = rate; u32 dacp; u32 mast_vol, phone_vol, mono_vol, pcm_vol; - u32 mute_vol = 0x8000; /* The mute volume? */ + u32 mute_vol = AC97_MUTE; if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) { @@ -1423,6 +1435,33 @@ EXPORT_SYMBOL(ac97_restore_state); +int ac97_suspend(struct ac97_codec *codec, int state) +{ + struct ac97_driver *driver; + int ret = 0; + + down(&codec_sem); + driver = codec->driver; + if (driver != NULL && driver->suspend) + ret = driver->suspend(codec, state); + up(&codec_sem); + + return ret; +} +EXPORT_SYMBOL(ac97_suspend); + +void ac97_resume(struct ac97_codec *codec) +{ + struct ac97_driver *driver; + + down(&codec_sem); + driver = codec->driver; + if (driver != NULL && driver->resume) + driver->resume(codec); + up(&codec_sem); +} +EXPORT_SYMBOL(ac97_resume); + /** * ac97_register_driver - register a codec helper * @driver: Driver handler @@ -1486,4 +1525,91 @@ EXPORT_SYMBOL_GPL(ac97_unregister_driver); +static int swap_headphone(int remove_master) +{ + struct list_head *l; + struct ac97_codec *c; + + if (remove_master) { + down(&codec_sem); + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if (supported_mixer(c, SOUND_MIXER_PHONEOUT)) + c->supported_mixers &= ~SOUND_MASK_PHONEOUT; + } + up(&codec_sem); + } else + ac97_hw[SOUND_MIXER_PHONEOUT].offset = AC97_MASTER_VOL_STEREO; + + /* Scale values already match */ + ac97_hw[SOUND_MIXER_VOLUME].offset = AC97_MASTER_VOL_MONO; + return 0; +} + +static int apply_quirk(int quirk) +{ + switch (quirk) { + case AC97_TUNE_NONE: + return 0; + case AC97_TUNE_HP_ONLY: + return swap_headphone(1); + case AC97_TUNE_SWAP_HP: + return swap_headphone(0); + case AC97_TUNE_SWAP_SURROUND: + return -ENOSYS; /* not yet implemented */ + case AC97_TUNE_AD_SHARING: + return -ENOSYS; /* not yet implemented */ + case AC97_TUNE_ALC_JACK: + return -ENOSYS; /* not yet implemented */ + } + return -EINVAL; +} + +/** + * ac97_tune_hardware - tune up the hardware + * @pdev: pci_dev pointer + * @quirk: quirk list + * @override: explicit quirk value (overrides if not AC97_TUNE_DEFAULT) + * + * Do some workaround for each pci device, such as renaming of the + * headphone (true line-out) control as "Master". + * The quirk-list must be terminated with a zero-filled entry. + * + * Returns zero if successful, or a negative error code on failure. + */ + +int ac97_tune_hardware(struct pci_dev *pdev, struct ac97_quirk *quirk, int override) +{ + int result; + + if (!quirk) + return -EINVAL; + + if (override != AC97_TUNE_DEFAULT) { + result = apply_quirk(override); + if (result < 0) + printk(KERN_ERR "applying quirk type %d failed (%d)\n", override, result); + return result; + } + + for (; quirk->vendor; quirk++) { + if (quirk->vendor != pdev->subsystem_vendor) + continue; + if ((! quirk->mask && quirk->device == pdev->subsystem_device) || + quirk->device == (quirk->mask & pdev->subsystem_device)) { +#ifdef DEBUG + printk("ac97 quirk for %s (%04x:%04x)\n", quirk->name, pdev->subsystem_vendor, pdev->subsystem_device); +#endif + result = apply_quirk(quirk->type); + if (result < 0) + printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result); + return result; + } + } + return 0; +} + +EXPORT_SYMBOL_GPL(ac97_tune_hardware); + MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/sound/oss/ac97_plugin_wm97xx.c linux-2.6.9-intc1-mih1/sound/oss/ac97_plugin_wm97xx.c --- linux-2.6.9-intc1/sound/oss/ac97_plugin_wm97xx.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/oss/ac97_plugin_wm97xx.c 2005-02-09 14:32:18.000000000 -0500 @@ -0,0 +1,2246 @@ +/* + * ac97_plugin_wm97xx.c -- Touch screen driver for Wolfson WM9705 and WM9712 + * AC97 Codecs. + * + * Copyright 2003, 2004 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * 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. + * + * Notes: + * + * Features: + * - supports WM9705, WM9712, WM9713 + * - polling mode + * - coordinate polling + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - 4 and 5 wire touchscreens (5 wire is WM9712 only) + * - pen down detection + * - battery monitor + * - sample AUX adc's + * - power management + * - codec GPIO + * - codec event notification + * + * Revision history + * 7th May 2003 Initial version. + * 6th June 2003 Added non module support and AC97 registration. + * 18th June 2003 Added AUX adc sampling. + * 23rd June 2003 Did some minimal reformatting, fixed a couple of + * locking bugs and noted a race to fix. + * 24th June 2003 Added power management and fixed race condition. + * 10th July 2003 Changed to a misc device. + * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h + * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() + * because some ac97_read/ac_97_write call schedule() + * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com + * 13th Nov 2003 Removed h3600 touch interface, added interrupt based + * pen down notification and implemented continous mode + * on XScale arch. + * 16th Nov 2003 Ian Molton + * Modified so that it suits the new 2.6 driver model. + * 25th Jan 2004 Andrew Zabolotny + * Implemented IRQ-driven pen down detection, implemented + * the private API meant to be exposed to platform-specific + * drivers, reorganized the driver so that it supports + * an arbitrary number of devices. + * 1st Feb 2004 Moved continuous mode handling to a separate + * architecture-dependent file. For now only PXA + * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). + * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both + * digitizer registers instead of reading them every time. + * A reorganization of the whole code for better + * error handling. + * 17th Apr 2004 Added BMON support. + * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual + * GPIOs) and 2.6 power management. + * 29th Nov 2004 Added WM9713 support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" + +#define TS_NAME "wm97xx" +#define WM_TS_VERSION "0.24" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Debug + */ +#if 1 +#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) +#endif +#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) + +/* + * Module parameters + */ + +static int useless; + +/* + * Set the codec sample mode. + * + * The WM9712 can sample touchscreen data in 3 different operating + * modes. i.e. polling, coordinate and continous. + * + * Polling: The driver polls the codec and issues 3 seperate commands + * over the AC97 link to read X,Y and pressure. + * + * Coordinate: The driver polls the codec and only issues 1 command over + * the AC97 link to read X,Y and pressure. This mode has + * strict timing requirements and may drop samples if + * interrupted. However, it is less demanding on the AC97 + * link. Note: this mode requires a larger delay than polling + * mode. + * + * Continuous: The codec automatically samples X,Y and pressure and then + * sends the data over the AC97 link in slots. This is the + * same method used by the codec when recording audio. + * This mode is not selectable by this parameter because + * it needs separate architecture-dependent support. The + * following drivers will add continuouse mode support: + * + * pxa-wm97xx-touch.c - for Intel PXA built-in AC97 controller. + */ +static int mode; +module_param (mode, int, 0); +MODULE_PARM_DESC(mode, "WM97XX operation mode (0:polling 1:coordinate)"); + +/* + * WM9712 - Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int rpu; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + +/* + * WM9705 - Pen detect comparator threshold. + * + * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold + * i.e. 1 = Vmid/15 threshold + * 15 = Vmid/1 threshold + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int pdd = 8; +module_param(pdd, int, 0); +MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil = 0; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * WM9712 - Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +static int five_wire; +module_param(five_wire, int, 0); +MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Coordinate inversion control. + * + * On some hardware the coordinate system of the touchscreen and the + * coordinate system of the graphics controller are different. A well-written + * calibration tool won't depend on that; however for compatibility with + * some old apps sometimes it is required to bring both coordinate + * systems together. That's what this parameter is for. + * + * Bit 0: if 1, the X coordinate is inverted + * Bit 1: if 1, the Y coordinate is inverted + */ +static int invert = 0; +module_param(invert, int, 0); +MODULE_PARM_DESC(invert, "Set bit 0 to invert X coordinate, set bit 1 to invert Y coordinate"); + +/* + * Touchscreen absolute values + * + * These parameters are used to help the input layer discard out of + * range readings and reduce jitter etc. + * + * o min, max:- indicate the min and max values your touch screen returns + * o fuzz:- use a higher number to reduce jitter + * + * The default values correspond to Mainstone II in QVGA mode + * + * Please read + * Documentation/input/input-programming.txt for more details. + */ + +static int abs_x[3] = {350,3900,5}; +module_param_array(abs_x, int, useless, 0); +MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); + +static int abs_y[3] = {320,3750,40}; +module_param_array(abs_y, int, useless, 0); +MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); + +static int abs_p[3] = {0,150,4}; +module_param_array(abs_p, int, useless, 0); +MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); + +/* + * Codec AUX ADC inputs. + */ +static u16 wm97xx_adcsel [3][4] = +{ + /* WM9705 */ + { WM9705_ADCSEL_BMON, WM9705_ADCSEL_AUX, WM9705_ADCSEL_PHONE, WM9705_ADCSEL_PCBEEP }, + /* WM9712 */ + { WM9712_ADCSEL_COMP1, WM9712_ADCSEL_COMP2, WM9712_ADCSEL_BMON, WM9712_ADCSEL_WIPER }, + /* WM9713 */ + {WM9713_ADCSEL_AUX1, WM9713_ADCSEL_AUX2, WM9713_ADCSEL_AUX3, WM9713_ADCSEL_AUX4 }, +}; + +/* AUX ADC conversion */ +typedef struct { + unsigned adcsel; /* ADC select mask; !=0 -> enabled */ + unsigned next_jiffies; /* Next time mark to collect data at */ + unsigned period; /* Interval in jiffies to collect data */ + wm97xx_ac_prepare func; /* Before/after conversion callback */ + int value; /* Last value sampled (-1 = no data yet) */ +} wm97xx_auxconv_data_t; + +/* Codec data */ +typedef struct { + u16 dig1, dig2, dig3; /* Cached digitizer registers */ + spinlock_t lock; /* Hardware lock */ + struct ac97_codec *codec; /* AC97 codec */ + struct semaphore sem; + struct completion ts_init; + struct completion ts_exit; + volatile struct task_struct *ts_task; + codec_read_t cont_read_sample; /* continuous mode reader */ + wm97xx_auxconv_data_t aux_conv [4]; /* AUX data */ + struct completion aux_exit; + struct completion aux_init; + volatile struct task_struct *aux_task; + int aux_task_ref; + wait_queue_head_t aux_wait; /* AUX pen-up wait queue */ + unsigned int pen_irq; /* Pen IRQ number in use */ + volatile int pen_irq_count; + unsigned int codec_irq; /* Codec IRQ number in use */ + volatile int codec_irq_count; + wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ + int codec_irq_ref_count; /* Number of gpio irq users */ + int adc_errs; /* Sample read back errors */ + unsigned int pen_is_down; /* Pen down time */ + unsigned int use_count; + struct workqueue_struct *pen_irq_workq; + struct work_struct pen_event_work; + struct workqueue_struct *codec_event_workq; + struct work_struct codec_event_work; + codec_event_t codec_event[WM97XX_MAX_GPIO]; /* codec event handlers */ + /* Bitfields grouped here to get some space gain */ + + unsigned is_wm9712:1; /* are we a WM9705/12 */ + + unsigned pen_probably_down:1; /* used in polling and coordinate modes */ + unsigned pen_is_up:1; /* pen is up */ + unsigned invert_x:1; /* invert X coords */ + unsigned invert_y:1; /* invert Y coords */ + unsigned aux_waiting:1; /* aux measurement waiting */ + unsigned ac97_link:1; /* interrupt over AC link */ + unsigned probe:1; /* probe complete */ +#if defined(CONFIG_PM) + unsigned phone_pga:5; /* PGA settings for PM */ + unsigned line_pga:16; + unsigned mic_pga:16; +#endif +} wm97xx_ts_t; + +/* Shortcut since we have a lot of such references */ +#define CODEC ts.codec + +/* + * Codec interface + */ +typedef struct { + int (*poll_sample)(int adcsel, int *sample); /* read 1 sample */ + int (*poll_coord)(wm97xx_data_t *data); /* read X,Y,[P} in coord */ + int (*poll_touch)(wm97xx_data_t *data); /* read X,Y,[P] in poll */ + void (*init_phy)(void); + void (*codec_suspend)(void); + void (*codec_resume)(void); + void (*digitiser_save)(void); + void (*digitiser_restore)(void); + void (*aux_prepare)(void); + void (*set_continuous_mode)(struct wm97xx_device *dev, int enable, int slot, int rate); + void (*digitiser_start)(void); + void (*digitiser_stop)(void); +} wm97xx_codec_t; + +/* + * We only support a single codec, however we may need to provide + * similtanious access to the device e.g. X and detect-stylus (GPE) + */ +static wm97xx_ts_t ts; +static wm97xx_codec_t *wm97xx_codec; +static struct input_dev wm97xx_input; +static LIST_HEAD(wm97xx_drivers); + +/* + * Codec ID registers + */ +u32 wm97xx_id; +EXPORT_SYMBOL_GPL (wm97xx_id); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[16] = { + 21, // 1 AC97 Link frames + 42, // 2 + 84, // 4 + 167, // 8 + 333, // 16 + 667, // 32 + 1000, // 48 + 1333, // 64 + 2000, // 96 + 2667, // 128 + 3333, // 160 + 4000, // 192 + 4667, // 224 + 5333, // 256 + 6000, // 288 + 0 // No delay, switch matrix always on +}; + +static void wm97xx_digitiser_save(void) +{ + ts.dig1 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1); + ts.dig2 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER2); +} + +static void wm9713_digitiser_save(void) +{ + ts.dig1 = CODEC->codec_read(CODEC, AC97_WM9713_DIG1); + ts.dig2 = CODEC->codec_read(CODEC, AC97_WM9713_DIG2); + ts.dig3 = CODEC->codec_read(CODEC, AC97_WM9713_DIG3); +} + +static void wm97xx_digitiser_restore(void) +{ + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); +} + +static void wm9713_digitiser_restore(void) +{ + CODEC->codec_write (CODEC, AC97_WM9713_DIG1, ts.dig1); + CODEC->codec_write (CODEC, AC97_WM9713_DIG2, ts.dig2); + CODEC->codec_write (CODEC, AC97_WM9713_DIG3, ts.dig3); +} + +static void wm97xx_aux_prepare(void) +{ + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER1, 0); + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); +} + +static void wm9713_aux_prepare(void) +{ + CODEC->codec_write (CODEC, AC97_WM9713_DIG1, 0); + CODEC->codec_write (CODEC, AC97_WM9713_DIG2, 0); + CODEC->codec_write (CODEC, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); +} + +static void wm97xx_digitiser_start(void) +{ + u16 dig2 = ts.dig2 | WM97XX_PRP_DET_DIG; + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2 = dig2); + CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); /* dummy read */ +} + +static void wm97xx_digitiser_stop(void) +{ + u16 val = ts.codec->codec_read(ts.codec, AC97_WM97XX_DIGITISER2); + ts.codec->codec_write(ts.codec, AC97_WM97XX_DIGITISER2, val & ~WM97XX_PRP_DET_DIG); +} + +static void wm9713_digitiser_start(void) +{ + u16 val = CODEC->codec_read(CODEC, AC97_EXTENDED_MODEM_ID); + CODEC->codec_write(CODEC, AC97_EXTENDED_MODEM_ID, val & 0x7fff); + val = CODEC->codec_read(CODEC, AC97_WM9713_DIG3); + CODEC->codec_write(CODEC, AC97_WM9713_DIG3, val | WM97XX_PRP_DET_DIG); + CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); /* dummy read */ +} + +static void wm9713_digitiser_stop(void) +{ + u16 val = CODEC->codec_read(CODEC, AC97_WM9713_DIG3); + CODEC->codec_write(CODEC, AC97_WM9713_DIG3, val & ~WM97XX_PRP_DET_DIG); + val = CODEC->codec_read(CODEC, AC97_EXTENDED_MODEM_ID); + CODEC->codec_write(CODEC, AC97_EXTENDED_MODEM_ID, val | 0x8000); +} + + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay (3 * AC97_LINK_FRAME + delay_table [d]); +} + +static inline int is_pden (void) +{ + u16 pden = 0; + + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + pden = ts.dig2 & WM9705_PDEN; + break; + case WM9712_ID2: + pden = ts.dig2 & WM9712_PDEN; + break; + case WM9713_ID2: + pden = ts.dig3 & WM9713_PDEN; + break; + } + + return pden; +} + +/* + * Read a sample from the WM9705/12 adc in polling mode. + */ +static int wm97xx_poll_sample (int adcsel, int *sample) +{ + int timeout = 5 * delay; + + if (!ts.pen_probably_down) { + u16 data = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + ts.pen_probably_down = 1; + } + + /* set up digitiser */ + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay (delay); + + /* wait for POLL to go low */ + while ((CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden()) + ts.pen_probably_down = 0; + else { + ts.adc_errs++; + dbg ("adc sample timeout"); + } + return 0; + } + + *sample = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { + ts.adc_errs++; + dbg ("adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSEL_MASK); + return 0; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + ts.pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + + +/* + * Read a sample from the WM9713 adc in polling mode. + */ +static int wm9713_poll_sample (int adcsel, int *sample) +{ + u16 dig1; + + int timeout = 5 * delay; + + if (!ts.pen_probably_down) { + u16 data = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + ts.pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = ts.codec->codec_read(ts.codec, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + CODEC->codec_write(CODEC, AC97_WM9713_DIG1, dig1 | adcsel | + WM9713_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((CODEC->codec_read(CODEC, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden ()) + ts.pen_probably_down = 0; + else { + ts.adc_errs++; + dbg ("adc sample timeout"); + } + return RC_PENUP; + } + *sample = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSRC_MASK) != generic_ffs(adcsel >> 1) << 12) { + ts.adc_errs++; + dbg ("adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSRC_MASK); + return RC_PENUP; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + ts.pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read WM9705/12 sample data (x,y,[p]) from the adc in coordinate mode. + */ +static int wm97xx_poll_coord_touch(wm97xx_data_t *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!ts.pen_probably_down) { + u16 data = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + ts.pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = (ts.dig1 & ~WM97XX_ADCSEL_MASK) | WM97XX_ADCSEL_PRES; + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, + (ts.dig1 = dig1) | WM97XX_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* read X then wait for 1 AC97 link frame + settling delay */ + data->x = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + udelay (AC97_LINK_FRAME + delay_table[delay]); + + /* read Y */ + data->y = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* wait for POLL to go low and then read pressure */ + while ((CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)&& timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + if (is_pden()) + ts.pen_probably_down = 0; + else { + ts.adc_errs++; + dbg ("adc sample timeout"); + } + return 0; + } + + /* read pressure */ + data->p = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct samples */ + if (((data->x & WM97XX_ADCSRC_MASK) != 0x1000) || + ((data->y & WM97XX_ADCSRC_MASK) != 0x2000) || + ((data->p & WM97XX_ADCSRC_MASK) != 0x3000)) { + ts.adc_errs++; + dbg ("wrong sample order"); + return 0; + } + + if (!(data->x & WM97XX_PEN_DOWN)) { + ts.pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read WM9713 sample data (x,y,[p]) from the adc in coordinate mode. + */ +static int wm9713_poll_coord_touch(wm97xx_data_t *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!ts.pen_probably_down) { + u16 data = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + ts.pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = CODEC->codec_read(CODEC, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + CODEC->codec_write(CODEC, AC97_WM9713_DIG1, dig1 | WM9713_ADCSEL_PRES | + WM9713_POLL | WM9713_COO); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* read X then wait for 1 AC97 link frame + settling delay */ + data->x = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + udelay (AC97_LINK_FRAME + delay_table[delay]); + + /* read Y */ + data->y = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* wait for POLL to go low and then read pressure */ + while ((CODEC->codec_read(CODEC, AC97_WM9713_DIG1) & WM9713_POLL)&& timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + if (is_pden ()) + ts.pen_probably_down = 0; + else { + ts.adc_errs++; + dbg ("adc sample timeout"); + } + return RC_PENUP; + } + + /* read pressure */ + data->p = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct samples */ + if (((data->x & WM97XX_ADCSRC_MASK) != 0x1000) || + ((data->y & WM97XX_ADCSRC_MASK) != 0x2000) || + ((data->p & WM97XX_ADCSRC_MASK) != 0x3000)) { + ts.adc_errs++; + dbg ("wrong sample order"); + return RC_PENUP; + } + + if (!(data->x & WM97XX_PEN_DOWN)) { + ts.pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + + +/* + * Sample the WM9705/WM9712 touchscreen in polling mode + */ +static int wm97xx_poll_touch(wm97xx_data_t *data) +{ + int rc; + + if ((rc = wm97xx_poll_sample(WM97XX_ADCSEL_X, &data->x)) != RC_VALID) + return rc; + if ((rc = wm97xx_poll_sample(WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) + return rc; + if (pil && !five_wire) { + if ((rc = wm97xx_poll_sample(WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Sample the WM9713 touchscreen in polling mode + */ +static int wm9713_poll_touch(wm97xx_data_t *data) +{ + int rc; + + if ((rc = wm9713_poll_sample(WM9713_ADCSEL_X, &data->x)) != RC_VALID) + return rc; + if ((rc = wm9713_poll_sample(WM9713_ADCSEL_Y, &data->y)) != RC_VALID) + return rc; + if (pil && !five_wire) { + if ((rc = wm9713_poll_sample(WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Read AUX adc + */ +static int wm97xx_check_aux (void) +{ + int i, restore_state = 0, min_delta = 0x7fffffff, power_adc = 0; + u16 adcsel, power = 0; + unsigned jiff = jiffies; + + spin_lock(&ts.lock); + if (ts.pen_is_down) { + /* If pen is down, delay auxiliary sampling. The touchscreen + * thread will wake us up when the pen will be released. + */ + ts.aux_waiting = 1; + spin_unlock(&ts.lock); + return 10*HZ; + } + + /* When the touchscreen is not in use, we may have to power up the AUX ADC + * before we can use sample the AUX inputs. + */ + if ((wm97xx_id & 0xffff) == WM9713_ID2 && + (power = CODEC->codec_read(CODEC, AC97_EXTENDED_MODEM_ID)) & 0x8000) { + power_adc = 1; + CODEC->codec_write (CODEC, AC97_EXTENDED_MODEM_ID, power & 0x7fff); + } + + ts.aux_waiting = 0; + for (i = 0; i < ARRAY_SIZE (ts.aux_conv); i++) + if ((adcsel = ts.aux_conv [i].adcsel)) { + int delta = ts.aux_conv [i].next_jiffies - jiff; + + /* Loose the check here to syncronize events that + * have close periods. This allows less timer + * interrupts to be generated, and better performance + * because of less codec writes. + */ + if (delta < (int)(ts.aux_conv [i].period >> 5)) { + int auxval = 0; + + if (!restore_state) { + /* This is the first AUX sample we + * read, prepare the chip. + */ + restore_state = 1; + wm97xx_codec->digitiser_save(); + wm97xx_codec->aux_prepare(); + } + + /* Fool wm97xx_poll_read_adc() */ + ts.pen_probably_down = 1; + + if (ts.aux_conv [i].func) + ts.aux_conv [i].func (i, 0); + wm97xx_codec->poll_sample (adcsel, &auxval); + if (ts.aux_conv [i].func) + ts.aux_conv [i].func (i, 1); + ts.aux_conv [i].value = auxval & 0xfff; + ts.aux_conv [i].next_jiffies = jiff + ts.aux_conv [i].period; + } + + delta = ts.aux_conv [i].next_jiffies - jiff; + if (delta < min_delta) + min_delta = delta; + } + + if (power_adc) + CODEC->codec_write(CODEC, AC97_EXTENDED_MODEM_ID, power | 0x8000); + + if (restore_state) { + /* We know for sure pen is not down */ + ts.pen_probably_down = 0; + /* Restore previous state of digitizer registers */ + wm97xx_codec->digitiser_restore(); + } + spin_unlock(&ts.lock); + + if (min_delta < 1) + min_delta = 1; + + return min_delta; +} + +/* + * Auxiliary ADC reading thread. + */ +static int wm97xx_aux_thread (void *data) +{ + /* set up thread context */ + ts.aux_task = current; + daemonize("kwm97xx_aux"); + set_user_nice (current, 5); + + if(ts.codec == NULL) { + ts.aux_task = NULL; + printk(KERN_ERR "codec is NULL, bailing\n"); + } + complete (&ts.aux_init); + + /* touch reader loop */ + while (ts.aux_task) { + int sleep_time = wm97xx_check_aux (); + + if (ts.aux_waiting) { + wait_event_interruptible_timeout (ts.aux_wait, + ts.aux_waiting == 0, + sleep_time); + } else { + set_task_state (current, TASK_INTERRUPTIBLE); + schedule_timeout (sleep_time); + } + } + complete_and_exit (&ts.aux_exit, 0); +} + +/*---/ Public interface exposed to custom architecture-dependent drivers /---*/ + +int wm97xx_request_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac, + int period, wm97xx_ac_prepare func) +{ + int ret = 0; + + spin_lock(&ts.lock); + if (ts.aux_conv[ac].func) { + spin_unlock(&ts.lock); + return -EBUSY; + } + + ts.aux_conv [ac].value = -1; + ts.aux_conv [ac].period = period; + ts.aux_conv [ac].next_jiffies = jiffies + period; + ts.aux_conv [ac].func = func; + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + ts.aux_conv [ac].adcsel = wm97xx_adcsel[0][ac]; + break; + case WM9712_ID2: + ts.aux_conv [ac].adcsel = wm97xx_adcsel[1][ac]; + break; + case WM9713_ID2: + ts.aux_conv [ac].adcsel = wm97xx_adcsel[2][ac]; + break; + } + ts.aux_task_ref++; + + /* Start the aux thread, if not started already */ + if (ts.aux_task) { + spin_unlock(&ts.lock); + return 0; + } + spin_unlock(&ts.lock); + init_completion (&ts.aux_init); + init_completion (&ts.aux_exit); + ret = kernel_thread (wm97xx_aux_thread, &ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion (&ts.aux_init); + if(ts.aux_task == NULL) + ret = -EINVAL; + } + return ret; +} +EXPORT_SYMBOL_GPL(wm97xx_request_auxconv); + +void wm97xx_free_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac) +{ + spin_lock(&ts.lock); + if (ts.aux_conv == NULL) + goto out; + + ts.aux_conv [ac].value = -1; + ts.aux_conv [ac].func = NULL; + ts.aux_task_ref--; + + if (!ts.aux_task_ref && ts.aux_task) { + spin_unlock(&ts.lock); + /* kill thread */ + ts.aux_task = NULL; + ts.aux_waiting = 0; + wait_for_completion (&ts.aux_exit); + return; + } +out: + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_free_auxconv); + +int wm97xx_get_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac) +{ + return ts.aux_conv [ac].adcsel ? ts.aux_conv [ac].value : -1; +} +EXPORT_SYMBOL_GPL(wm97xx_get_auxconv); + +/* + * Arch specific wm97xx components can register here. This means they are + * probed/removed and can have a PM suspend/resume. + */ +int wm97xx_driver_register (struct wm97xx_device *drv) +{ + if (!drv->probe || !drv->remove) + return -ENODEV; + + INIT_LIST_HEAD(&drv->devices); + spin_lock(&ts.lock); + list_add(&drv->devices, &wm97xx_drivers); + if(ts.probe) { + spin_unlock(&ts.lock); + return drv->probe(); + } + spin_unlock(&ts.lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_driver_register); + +/* + * Arch specific unregister. + */ +void wm97xx_driver_unregister (struct wm97xx_device *drv) +{ + spin_lock(&ts.lock); + list_del_init(&drv->devices); + if (ts.probe){ + spin_unlock(&ts.lock); + drv->remove(); + } else + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_driver_unregister); + +/* + * Get the status of a codec GPIO pin + */ +wm97xx_gpio_status_t wm97xx_get_codec_gpio (struct wm97xx_device *dev, u32 gpio) +{ + u16 status; + + status = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + + if (status & gpio) + return WM97XX_GPIO_HIGH; + else + return WM97XX_GPIO_LOW; +} +EXPORT_SYMBOL_GPL(wm97xx_get_codec_gpio); + +/* + * Set the status of a codec GPIO pin + */ +void wm97xx_set_codec_gpio (struct wm97xx_device *dev, u32 gpio, wm97xx_gpio_status_t status) +{ + u16 reg; + + spin_lock(&ts.lock); + reg = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + if (status & WM97XX_GPIO_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + if ((wm97xx_id & 0xffff) == WM9712_ID2) { + /* small hack for wm9712 */ + CODEC->id = 1; + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, reg << 1); + CODEC->id = 0; + } else + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, reg); + + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_set_codec_gpio); + +/* + * Codec GPIO pin configuration, this set's pin direction, polarity, + * stickyness and wake up. + */ +void wm97xx_config_codec_gpio (struct wm97xx_device *dev, u32 gpio, + wm97xx_gpio_dir_t dir, wm97xx_gpio_pol_t pol, + wm97xx_gpio_sticky_t sticky, wm97xx_gpio_wake_t wake) +{ + u16 reg; + spin_lock(&ts.lock); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_POLARITY); + if (pol == WM97XX_GPIO_POL_HIGH) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_POLARITY, reg); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + if (sticky == WM97XX_GPIO_STICKY) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, reg); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_WAKE_UP); + if (wake == WM97XX_GPIO_WAKE) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_WAKE_UP, reg); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_CONFIG); + if (dir == WM97XX_GPIO_IN) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_CONFIG, reg); + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_config_codec_gpio); + +/* + * Simple and safe codec register IO for external drivers + */ +u16 wm97xx_reg_read (struct wm97xx_device *dev, u16 reg) +{ + return CODEC->codec_read(CODEC, reg); +} +EXPORT_SYMBOL_GPL(wm97xx_reg_read); + +void wm97xx_reg_write (struct wm97xx_device *dev, u16 reg, u16 val) +{ + CODEC->codec_write(CODEC, reg, val); +} +EXPORT_SYMBOL_GPL(wm97xx_reg_write); + +/* + * Allow arch specific driver to use the codec PENDOWN gpio signal + * as a pen down interrupt source. The PENDOWN signal is usually + * routed via a cpu irq/gpio. + * + * NOTE: The wm9712 also allows the pen down signal to be sent over + * the AC97 link or over the codec irq pin. Use wm97xx_enable_codec_irq + * in this case. + */ +int wm97xx_set_pen_irq (struct wm97xx_device *dev, int irq) +{ + spin_lock(&ts.lock); + if (!ts.pen_irq) + ts.pen_irq = irq; + + spin_unlock(&ts.lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_set_pen_irq); + +/* + * Allow arch specific drivers to use the codec AC97 interrupt. This + * can either be over the AC97 link or using the codec irq pin. + * This interrupt can be used to notify arch specific drivers of + * numerous events i.e. battery level, ADC data, pen down, etc + */ +int wm97xx_set_codec_irq (struct wm97xx_device *dev, int irq, int ac97_link) +{ + spin_lock(&ts.lock); + ts.codec_irq_ref_count++; + if (!ts.codec_irq) { + ts.codec_irq = irq; + ts.ac97_link = ac97_link; + } + spin_unlock(&ts.lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_set_codec_irq); + +void wm97xx_clear_pen_irq (struct wm97xx_device *dev) +{ + spin_lock(&ts.lock); + if (ts.pen_irq) + ts.pen_irq = 0; + + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_clear_pen_irq); + +int wm97xx_clear_codec_irq (struct wm97xx_device *dev) +{ + spin_lock(&ts.lock); + if (ts.codec_irq_ref_count == 0) + goto out; + + /* last one ? */ + if (--ts.codec_irq_ref_count == 0) { + ts.codec_irq = 0; + } + +out: + spin_unlock(&ts.lock); + return ts.codec_irq_ref_count; +} +EXPORT_SYMBOL_GPL(wm97xx_clear_codec_irq); + +/* + * Register arch specific codec event handler. One handler per event. + */ +int wm97xx_request_codec_event (struct wm97xx_device *dev, int gpio, codec_event_t work) +{ + u16 reg; + int irq = generic_ffs(gpio) - 1; + + if (irq < 0 || irq > WM97XX_MAX_GPIO) + return -ENODEV; + + spin_lock(&ts.lock); + if ((wm97xx_id & 0xffff) == WM9705_ID2) + goto err; + + /* cant use GPIO2 for irq and gpio at the same time */ + if (!ts.ac97_link && gpio == WM97XX_GPIO_2) + goto err; + + if (ts.codec_event[irq]) + goto err; + + ts.codec_event[irq] = work; + + /* handle pen down internally */ + if (gpio == WM97XX_GPIO_13) + ts.pen_irq = ts.codec_irq; + + /* set pin to GPIO function */ + reg= CODEC->codec_read(CODEC, AC97_MISC_MODEM_STAT); + CODEC->codec_write(CODEC, AC97_MISC_MODEM_STAT, reg | gpio); + spin_unlock(&ts.lock); + wm97xx_config_codec_gpio (dev, gpio, WM97XX_GPIO_IN, WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); + + return 0; + +err: + spin_unlock(&ts.lock); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(wm97xx_request_codec_event); + +/* + * Free codec event handler + */ +void wm97xx_free_codec_event (struct wm97xx_device *dev, int gpio) +{ + int irq = generic_ffs(gpio) - 1; + + spin_lock(&ts.lock); + if ((wm97xx_id & 0xffff) == WM9705_ID2) + goto out; + + if (irq >= 0 && irq < WM97XX_MAX_GPIO) + ts.codec_event[irq] = NULL; + + /* handle pen down internally */ + if (gpio == WM97XX_GPIO_13) + ts.pen_irq = 0; + +out: + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_free_codec_event); + +/* + * Enable WM9705/12 continuous mode, i.e. touch data is streamed across an AC97 slot + */ +static void wm97xx_init_continuous_mode (struct wm97xx_device *dev, int enable, int slot, int rate) +{ + u16 dig1, dig2; + + spin_lock(&ts.lock); + + dig1 = ts.dig1; + dig2 = ts.dig2; + + if (enable) { + /* continous mode */ + dbg("Enabling continous mode"); + ts.cont_read_sample = dev->cont_read; + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY (delay) | + WM97XX_SLT (slot) | + WM97XX_RATE (rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + if ((wm97xx_id & 0xffff) == WM9712_ID2) + dig2 |= WM9712_PDEN; + else + dig2 |= WM9705_PDEN; + } else { + ts.cont_read_sample = NULL; + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + if (mode == 1) + /* coordinate mode */ + dig1 |= WM97XX_COO; + + if ((wm97xx_id & 0xffff) == WM9712_ID2) + dig2 &= ~WM9712_PDEN; + else + dig2 &= ~WM9705_PDEN; + } + + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1 = dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2 = dig2); + + spin_unlock(&ts.lock); +} + +/* + * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot + */ +static void wm9713_init_continuous_mode (struct wm97xx_device *dev, int enable, int slot, int rate) +{ + u16 dig1, dig2, dig3; + + spin_lock(&ts.lock); + + dig1 = ts.dig1; + dig2 = ts.dig2; + dig3 = ts.dig3; + + if (enable) { + /* continous mode */ + dbg("Enabling continous mode"); + dig1 &= ~WM9713_ADCSEL_MASK; + dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; + if (pil) + dig1 |= WM9713_ADCSEL_PRES; + dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); + dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | WM97XX_SLT (slot) | WM97XX_RATE (rate); + dig3 |= WM9713_PDEN; + } else { + dig1 &= ~(WM97XX_CTC | WM97XX_COO); + dig2 &= ~WM97XX_SLEN; + if (mode == 1) + /* coordinate mode */ + dig1 |= WM97XX_COO; + dig3 &= ~WM9713_PDEN; + } + + CODEC->codec_write(CODEC, AC97_WM9713_DIG1, ts.dig1 = dig1); + CODEC->codec_write(CODEC, AC97_WM9713_DIG2, ts.dig2 = dig2); + CODEC->codec_write(CODEC, AC97_WM9713_DIG3, ts.dig3 = dig3); + spin_unlock (&ts.lock); +} + +void wm97xx_set_continuous_mode (struct wm97xx_device *dev, codec_read_t read, int enable, int slot, int rate) +{ + ts.cont_read_sample = read; + wm97xx_codec->set_continuous_mode(dev, enable, slot, rate); +} +EXPORT_SYMBOL_GPL(wm97xx_set_continuous_mode); +/*---------------------------------------------------------------------------*/ + +#if defined(CONFIG_PM) +/* save state of wm9712 PGA's */ +static void wm9712_pga_save(void) +{ + ts.phone_pga = CODEC->codec_read(CODEC, AC97_PHONE_VOL) & 0x001f; + ts.line_pga = CODEC->codec_read(CODEC, AC97_LINEIN_VOL) & 0x1f1f; + ts.mic_pga = CODEC->codec_read(CODEC, AC97_MIC_VOL) & 0x1f1f; +} + +/* restore state of wm9712 PGA's */ +static void wm9712_pga_restore(void) +{ + u16 reg; + + reg = CODEC->codec_read(CODEC, AC97_PHONE_VOL); + CODEC->codec_write(CODEC, AC97_PHONE_VOL, (reg & ~0x1f) | ts.phone_pga); + reg = CODEC->codec_read(CODEC, AC97_LINEIN_VOL); + CODEC->codec_write(CODEC, AC97_LINEIN_VOL, (reg & ~0x1f1f) | ts.line_pga); + reg = CODEC->codec_read(CODEC, AC97_MIC_VOL); + CODEC->codec_write(CODEC, AC97_MIC_VOL, (reg & ~0x1f1f) | ts.mic_pga); +} + +/* + * Power down the codec + */ +static int wm97xx_suspend(struct ac97_codec *codec, int state) +{ + u16 reg; + struct list_head *l; + + /* save digitiser settings */ + ts.dig1 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1); + ts.dig2 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER2); + + /* suspend any arch specific drivers */ + list_for_each(l, &wm97xx_drivers) { + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + if (c->suspend) + c->suspend(state, 0); + } + + /* wm9705 does not have extra PM */ + if (!ts.is_wm9712) + return 0; + + /* save and mute the PGA's */ + wm9712_pga_save(); + + reg = CODEC->codec_read(CODEC, AC97_PHONE_VOL); + CODEC->codec_write(CODEC, AC97_PHONE_VOL, reg | 0x001f); + reg = CODEC->codec_read(CODEC, AC97_MIC_VOL); + CODEC->codec_write(CODEC, AC97_MIC_VOL, reg | 0x1f1f); + reg = CODEC->codec_read(CODEC, AC97_LINEIN_VOL); + CODEC->codec_write(CODEC, AC97_LINEIN_VOL, reg | 0x1f1f); + + /* power down, dont disable the AC link */ + CODEC->codec_write(CODEC, AC97_WM9712_POWER, WM9712_PD(14) | + WM9712_PD(13) | WM9712_PD(12) | WM9712_PD(11) | + WM9712_PD(10) | WM9712_PD(9) | WM9712_PD(8) | + WM9712_PD(7) | WM9712_PD(6) | WM9712_PD(5) | + WM9712_PD(4) | WM9712_PD(3) | WM9712_PD(2) | + WM9712_PD(1) | WM9712_PD(0)); + + return 0; +} + +/* + * Power up the Codec + */ +static void wm97xx_resume(struct ac97_codec* codec) +{ + struct list_head *l; + + /* resume any arch specific drivers */ + list_for_each(l, &wm97xx_drivers) { + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + if (c->resume) + c->resume(0); + } + + /* restore digitiser settings */ + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); + + /* wm9705 does not have extra PM other than muting AUX and VID */ + if (!ts.is_wm9712) { + CODEC->codec_write(CODEC, AC97_AUX_VOL, 0x8000); + CODEC->codec_write(CODEC, AC97_VIDEO_VOL, 0x8000); + return; + } + + /* power up */ + CODEC->codec_write(CODEC, AC97_WM9712_POWER, 0x0); + + /* restore PGA state */ + wm9712_pga_restore(); +} + +#else +#define wm97xx_suspend NULL +#define wm97xx_resume NULL +#endif + +/* + * Handle a pen down interrupt. + */ +static void wm97xx_pen_irq_worker (void *ptr) +{ + u32 status = 0; + /* do we need to enable the touch panel reader */ + if ((wm97xx_id & 0xffff) == WM9705_ID2) { + ts.pen_is_down++; + wake_up_interruptible (&ts.pen_irq_wait); + } else { + status = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + printk(KERN_DEBUG "wm97xx_pen_irq_worker called, codec_GPIO status read: status=%i\n", status); + if (status & WM97XX_GPIO_13) { + ts.pen_is_down++; + printk(KERN_DEBUG "wm97xx_pen_irq_worker: pen is down -> wake_up_interruptible (pen_irq_wait)\n"); + wake_up_interruptible (&ts.pen_irq_wait); + } + } +} + +/* + * Handle a codec event interrupt. + * Can also include pen down depending upon configuration. + */ +static void wm97xx_codec_irq_worker (void *ptr) +{ + int i = WM97XX_MAX_GPIO; + u16 status, sticky = 0; + + status = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + + /* check for pen down as we handle it internally + * MASK pen down the unmask at pen up */ + if (status & WM97XX_GPIO_13) + wm97xx_pen_irq_worker(ptr); + + /* reset codec slot 12 sticky bits and disable active interrupt + * sources by clearing the sticky status. + */ + //spin_lock(&ts.lock); + if ((wm97xx_id & 0xffff) != WM9705_ID2) { + sticky = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + if ((wm97xx_id & 0xffff) == WM9712_ID2) { + /* small hack for wm9712 */ + CODEC->id = 1; + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, status << 1); + CODEC->id = 0; + } else + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, status); + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, status ^ sticky); + } + + /* Iterate through archictecture specific codec workers (if they exist) in + * priority order. + */ + while(--i >= 0) { + if (ts.codec_event[i] && status & 1 << i) + ts.codec_event[i](status); + } + + /* turn sticky bits back on, except for pen down */ + if ((wm97xx_id & 0xffff) != WM9705_ID2) + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, sticky & ~WM97XX_GPIO_13); + enable_irq(ts.codec_irq); + //spin_unlock(&ts.lock); +} + +/* + * Codec PENDOWN edge signal irq handler + */ +static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ts.pen_irq_count++; + queue_work(ts.pen_irq_workq, &ts.pen_event_work); + + return IRQ_HANDLED; +} + +/* + * Codec PENDOWN/Power/GPIO/status signal irq handler + */ +static irqreturn_t wm97xx_codec_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* diasable IRQ so we can reset codec IRQ status */ + disable_irq(irq); + ts.codec_irq_count++; + queue_work(ts.codec_event_workq, &ts.codec_event_work); + + return IRQ_HANDLED; +} + +/* + * initialise codec IRQ handler and workqueue + */ +static int wm97xx_init_codec_irq (void) +{ + u16 reg; + + INIT_WORK(&ts.codec_event_work, wm97xx_codec_irq_worker, &ts); + if ((ts.codec_event_workq = create_singlethread_workqueue("kwm97ev")) == NULL) { + err("could not create codec event work queue"); + ts.codec_irq = 0; + return -1; + } + + ts.codec_irq_count = 0; + if (request_irq(ts.codec_irq, wm97xx_codec_interrupt, SA_SHIRQ, "wm97xx-ac97", &ts)) { + err("could not register codec interrupt"); + destroy_workqueue(ts.codec_event_workq); + ts.codec_irq = 0; + return -1; + } + + /* wm9712/13 can interrupt using it's IRQ pin or over the AC97 link */ + if ((wm97xx_id & 0xffff) != WM9705_ID2) { + if (ts.ac97_link) { /* use link */ + if ((wm97xx_id & 0xffff) == WM9712_ID2) { + reg = CODEC->codec_read(CODEC, AC97_RESERVED_58); + CODEC->codec_write(CODEC, AC97_RESERVED_58, reg |= 0x2); + } else { + reg = CODEC->codec_read(CODEC, 0x5a); + CODEC->codec_write(CODEC, 0x5a, reg |= 0x2); + } + } else { /* use pin */ + reg = CODEC->codec_read(CODEC, AC97_MISC_MODEM_STAT); + CODEC->codec_write(CODEC, AC97_MISC_MODEM_STAT, reg &= 0xfffb); + reg = CODEC->codec_read(CODEC, AC97_GPIO_CONFIG); + CODEC->codec_write(CODEC, AC97_GPIO_CONFIG, reg &= 0xfffb); + } + } + + return 0; +} + +/* + * initialise pen IRQ handler and workqueue + */ +static int wm97xx_init_pen_irq (void) +{ + u16 reg; + + INIT_WORK(&ts.pen_event_work, wm97xx_pen_irq_worker, &ts); + if ((ts.pen_irq_workq = create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + ts.pen_irq = 0; + return -1; + } + + ts.pen_irq_count = 0; + if (request_irq(ts.pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", &ts)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(ts.pen_irq_workq); + ts.pen_irq = 0; + return -1; + } + + /* enable PEN down on wm9712/13 */ + if ((wm97xx_id & 0xffff) != WM9705_ID2) { + reg = CODEC->codec_read(CODEC, AC97_MISC_MODEM_STAT); + CODEC->codec_write(CODEC, AC97_MISC_MODEM_STAT, reg & 0xfff7); + } + + return 0; +} + +/* + * set up the physical settings of the WM9705 + */ +static void init_wm9705_phy(void) +{ + ts.dig1 = 0; + + ts.dig2 = WM97XX_RPR; + + /* + * mute VIDEO and AUX as they share X and Y touchscreen + * inputs on the WM9705 + */ + CODEC->codec_write(CODEC, AC97_AUX_VOL, 0x8000); + CODEC->codec_write(CODEC, AC97_VIDEO_VOL, 0x8000); + + /* touchpanel pressure current*/ + if (pil == 2) { + ts.dig2 |= WM9705_PIL; + dbg("setting pressure measurement current to 400uA."); + } else if (pil) + dbg("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* polling mode sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + dbg("supplied delay out of range."); + delay = 4; + } + } + ts.dig1 &= 0xff0f; + ts.dig1 |= WM97XX_DELAY(delay); + dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); + + /* coordinate mode */ + if (mode == 1) { + ts.dig1 |= WM97XX_COO; + dbg("using coordinate mode"); + } + + /* WM9705 pdd */ + ts.dig2 |= (pdd & 0x000f); + dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); + + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); +} + +/* + * set up the physical settings of the WM9712 + */ +static void init_wm9712_phy(void) +{ + ts.dig1 = 0; + ts.dig2 = WM97XX_RPR | WM9712_RPU(1); + + /* WM9712 rpu */ + if (rpu) { + ts.dig2 &= 0xffc0; + ts.dig2 |= WM9712_RPU(rpu); + dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); + } + + /* touchpanel pressure current*/ + if (pil == 2) { + ts.dig2 |= WM9712_PIL; + dbg("setting pressure measurement current to 400uA."); + } else if (pil) + dbg("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* WM9712 five wire */ + if (five_wire) { + ts.dig2 |= WM9712_45W; + dbg("setting 5-wire touchscreen mode."); + } + + /* polling mode sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + dbg("supplied delay out of range."); + delay = 4; + } + } + ts.dig1 &= 0xff0f; + ts.dig1 |= WM97XX_DELAY(delay); + dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); + + /* coordinate mode */ + if (mode == 1) { + ts.dig1 |= WM97XX_COO; + dbg("using coordinate mode"); + } + + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); +} + +/* + * set up the physical settings of the WM9713 + */ +static void init_wm9713_phy(void) +{ + u16 dig1 = 0, dig2, dig3; + + /* default values */ + dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); + dig3= WM9712_RPU(1); + + /* rpu */ + if (rpu) { + dig3 &= 0xffc0; + dig3 |= WM9712_RPU(rpu); + info("setting pen detect pull-up to %d Ohms",64000 / rpu); + } + + /* touchpanel pressure */ + if (pil == 2) { + dig3 |= WM9712_PIL; + info("setting pressure measurement current to 400uA."); + } else if (pil) + info ("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* five wire */ + if (five_wire) { + dig3 |= WM9712_45W; + info("setting 5-wire touchscreen mode."); + } + + /* sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + info ("supplied delay out of range."); + delay = 4; + info("setting adc sample delay to %d u Secs.", delay_table[delay]); + } + } + dig2 &= 0xff0f; + dig2 |= WM97XX_DELAY(delay); + + /* coordinate mode */ + if (mode == 1) { + dig1 |= WM9713_COO; + info("using coordinate mode"); + } + + CODEC->codec_write(CODEC, AC97_WM9713_DIG1, ts.dig1 = dig1); + CODEC->codec_write(CODEC, AC97_WM9713_DIG2, ts.dig2 = dig2); + CODEC->codec_write(CODEC, AC97_WM9713_DIG3, ts.dig3 = dig3); +} + + +/* Private struct for communication between wm97xx_ts_thread and wm97xx_read_samples */ +struct ts_state { + int sleep_time; + int min_sleep_time; +}; + +/* + * When we use an irq to detect a pen down event, we must manually check + * for a pen up by checking the PDEN bit. If the pen goes up, we have to + * re-enable the codec pen down detection. + */ +static int wm97xx_irq_pen_check(int rc) +{ + u16 pen_status, reg; + + pen_status = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(pen_status & WM97XX_PEN_DOWN)) { + rc = RC_PENUP; + + /* Pen is now going to the up position */ + if (ts.pen_is_down && (ts.pen_irq == ts.codec_irq)) { + reg = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, reg | WM97XX_GPIO_13); + } + } + return rc; +} + +static int wm97xx_read_samples(struct ts_state *state) +{ + wm97xx_data_t data; + int rc = RC_PENUP; + + spin_lock(&ts.lock); + + if (ts.cont_read_sample) { + rc = ts.cont_read_sample (&wm97xx_input, ts.pen_is_down, pressure); + rc = wm97xx_irq_pen_check (rc); + } else { + switch (mode) { + case 0: + rc = wm97xx_codec->poll_touch (&data); + break; + case 1: + rc = wm97xx_codec->poll_coord (&data); + break; + } + } + + if (rc & RC_PENUP) { + if (ts.pen_is_down) { + ts.pen_is_down = 0; + dbg("pen up"); + input_report_abs(&wm97xx_input, ABS_PRESSURE, 0); + input_sync(&wm97xx_input); + if (ts.aux_waiting) { + ts.aux_waiting = 0; + wake_up_interruptible (&ts.aux_wait); + } + } else if (!(rc & RC_AGAIN)) { + /* We need high frequency updates only while pen is down, + * the user never will be able to touch screen faster than + * a few times per second... On the other hand, when the + * user is actively working with the touchscreen we don't + * want to lose the quick response. So we will slowly + * increase sleep time after the pen is up and quicky + * restore it to ~one task switch when pen is down again. + */ + if (state->sleep_time < HZ/10) + state->sleep_time++; + } + } else if (rc & RC_VALID) { + dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", + data.x >> 12, data.x & 0xfff, + data.y >> 12, data.y & 0xfff, + data.p >> 12, data.p & 0xfff); + if (ts.invert_x) + data.x ^= 0xfff; + if (ts.invert_y) + data.y ^= 0xfff; + input_report_abs (&wm97xx_input, ABS_X, data.x & 0xfff); + input_report_abs (&wm97xx_input, ABS_Y, data.y & 0xfff); + input_report_abs (&wm97xx_input, ABS_PRESSURE, data.p & 0xfff); + input_sync (&wm97xx_input); + ts.pen_is_down++; + state->sleep_time = state->min_sleep_time; + } else if (rc & RC_PENDOWN) { + dbg("pen down"); + ts.pen_is_down++; + state->sleep_time = state->min_sleep_time; + } + + spin_unlock(&ts.lock); + return rc; +} + +/* + * The touchscreen sample reader thread. + * Also once in 30 seconds we'll update the battery status. + */ +static int wm97xx_ts_thread(void *data) +{ + int rc; + struct ts_state state; + + /* set up thread context */ + ts.ts_task = current; + daemonize("wm97xx_ts"); + + if(ts.codec == NULL) { + ts.ts_task = NULL; + printk(KERN_ERR "codec is NULL, bailing\n"); + } + complete(&ts.ts_init); + + ts.pen_is_down = 0; + state.min_sleep_time = HZ >= 100 ? HZ/100 : 1; + if (state.min_sleep_time < 1) + state.min_sleep_time = 1; + state.sleep_time = state.min_sleep_time; + + /* touch reader loop */ + while (ts.ts_task) { + do { + rc = wm97xx_read_samples (&state); + } while (rc & RC_AGAIN); + if (!ts.pen_is_down && ts.pen_irq) { + /* Nice, we don't have to poll for pen down event */ + wait_event_interruptible (ts.pen_irq_wait, ts.pen_is_down); + } else { + set_task_state(current, TASK_INTERRUPTIBLE); + schedule_timeout(state.sleep_time); + } + } + complete_and_exit(&ts.ts_exit, 0); +} + +/* + * Start the touchscreen thread and + * the touch digitiser. + */ +static int wm97xx_ts_input_open(struct input_dev *idev) +{ + int ret = 0; + + if (ts.codec == NULL) { + printk("error: ts.codec == NULL\n"); + return -EINVAL; + } + + if (ts.codec->codec_read == NULL) { + printk("error: codec_read == NULL\n"); + return -EINVAL; + } + + if (ts.codec->codec_write == NULL) { + printk("error: codec_write == NULL\n"); + return -EINVAL; + } + + down(&ts.sem); + /* first time opened ? */ + if (ts.use_count++ == 0) { + /* start touchscreen thread */ + init_completion(&ts.ts_init); + init_completion(&ts.ts_exit); + + ret = kernel_thread(wm97xx_ts_thread, &ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&ts.ts_init); + if (ts.ts_task == NULL) + ret = -EINVAL; + } + if (ret < 0) + goto out; + + /* start digitiser */ + spin_lock(&ts.lock); + wm97xx_codec->digitiser_start(); + + /* init pen down/up irq handling */ + if (ts.codec_irq || ts.pen_irq) { + if (ts.codec_irq == ts.pen_irq) { + u16 reg = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, reg | WM97XX_GPIO_13); + wm97xx_init_codec_irq(); + } else { + wm97xx_init_pen_irq(); + } + } + spin_unlock(&ts.lock); + } + +out: + up(&ts.sem); + return ret; +} + +/* + * Kill the touchscreen thread and stop + * the touch digitiser. + */ +static void wm97xx_ts_input_close(struct input_dev *idev) +{ + down(&ts.sem); + if (--ts.use_count == 0) { + /* destroy workqueue and free irq's */ + if (ts.codec_irq || ts.pen_irq) { + if (ts.pen_irq == ts.codec_irq) { + free_irq(ts.codec_irq, &ts); + destroy_workqueue(ts.codec_event_workq); + } else { + free_irq(ts.pen_irq, &ts); + destroy_workqueue(ts.pen_irq_workq); + } + } + + /* kill thread */ + ts.ts_task = NULL; + ts.pen_is_down = 1; + wake_up_interruptible (&ts.pen_irq_wait); + wait_for_completion(&ts.ts_exit); + ts.pen_is_down = 0; + + /* stop digitiser */ + wm97xx_codec->digitiser_stop(); + + } + up(&ts.sem); +} + +/* + * Supported WM97xx Codecs + */ +static wm97xx_codec_t wm9705_codec = { + .poll_sample = wm97xx_poll_sample, + .poll_coord = wm97xx_poll_coord_touch, + .poll_touch = wm97xx_poll_touch, + .init_phy = init_wm9705_phy, + .codec_suspend = wm97xx_suspend, + .codec_resume = wm97xx_resume, + .digitiser_save = wm97xx_digitiser_save, + .digitiser_restore = wm97xx_digitiser_restore, + .aux_prepare = wm97xx_aux_prepare, + .set_continuous_mode = wm97xx_init_continuous_mode, + .digitiser_start = wm97xx_digitiser_start, + .digitiser_stop = wm97xx_digitiser_stop, +}; + +static wm97xx_codec_t wm9712_codec = { + .poll_sample = wm97xx_poll_sample, + .poll_coord = wm97xx_poll_coord_touch, + .poll_touch = wm97xx_poll_touch, + .init_phy = init_wm9712_phy, + .codec_suspend = wm97xx_suspend, + .codec_resume = wm97xx_resume, + .digitiser_save = wm97xx_digitiser_save, + .digitiser_restore = wm97xx_digitiser_restore, + .aux_prepare = wm97xx_aux_prepare, + .set_continuous_mode = wm97xx_init_continuous_mode, + .digitiser_start = wm97xx_digitiser_start, + .digitiser_stop = wm97xx_digitiser_stop, +}; + +static wm97xx_codec_t wm9713_codec = { + .poll_sample = wm9713_poll_sample, + .poll_coord = wm9713_poll_coord_touch, + .poll_touch = wm9713_poll_touch, + .init_phy = init_wm9713_phy, + .codec_suspend = wm97xx_suspend, + .codec_resume = wm97xx_resume, + .digitiser_save = wm9713_digitiser_save, + .digitiser_restore = wm9713_digitiser_restore, + .aux_prepare = wm9713_aux_prepare, + .set_continuous_mode = wm9713_init_continuous_mode, + .digitiser_start = wm9713_digitiser_start, + .digitiser_stop = wm9713_digitiser_stop, +}; + + +/* + * Called by the audio codec initialisation to register + * the touchscreen driver. + */ +static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + u16 id1, id2; + struct list_head *l; + + spin_lock(&ts.lock); + /* stop multiple probes */ + if (ts.probe) { + spin_unlock(&ts.lock); + return 0; + } + + /* set up AC97 codec interface */ + CODEC = codec; + codec->driver_private = &ts; + + /* + * We can only use a WM9705/12/13 that has been *first* initialised + * by the AC97 audio driver. This is because we have to use the audio + * drivers codec read() and write() functions to sample the touchscreen + * + * If an initialsed WM97xx is found then get the codec read and write + * functions. + */ + + /* test for a WM9705/12/13 */ + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + if (id1 == WM97XX_ID1) { + switch (id2) { + case WM9705_ID2: + info("Detected a WM9705 codec"); + wm97xx_codec = &wm9705_codec; + break; + case WM9712_ID2: + info("Detected a WM9712 codec"); + wm97xx_codec = &wm9712_codec; + break; + case WM9713_ID2: + info("Detected a WM9713 codec"); + wm97xx_codec = &wm9713_codec; + break; + default: + goto no_codec; + } + } + wm97xx_id = (id1 << 16) | id2; + + /* tell input system what we events we accept and register */ + memset (&wm97xx_input, 0, sizeof (struct input_dev)); + wm97xx_input.name = "wm97xx touchscreen"; + wm97xx_input.open = wm97xx_ts_input_open; + wm97xx_input.close = wm97xx_ts_input_close; + set_bit(EV_ABS, wm97xx_input.evbit); + set_bit(ABS_X, wm97xx_input.absbit); + set_bit(ABS_Y, wm97xx_input.absbit); + set_bit(ABS_PRESSURE, wm97xx_input.absbit); + wm97xx_input.absmax[ABS_X] = abs_x[1]; + wm97xx_input.absmax[ABS_Y] = abs_y[1]; + wm97xx_input.absmax[ABS_PRESSURE] = abs_p[1]; + wm97xx_input.absmin[ABS_X] = abs_x[0]; + wm97xx_input.absmin[ABS_Y] = abs_y[0]; + wm97xx_input.absmin[ABS_PRESSURE] = abs_p[0]; + wm97xx_input.absfuzz[ABS_X] = abs_x[2]; + wm97xx_input.absfuzz[ABS_Y] = abs_y[2]; + wm97xx_input.absfuzz[ABS_PRESSURE] = abs_p[2]; + wm97xx_input.private = &ts; + input_register_device(&wm97xx_input); + + /* set up physical characteristics */ + wm97xx_codec->init_phy(); + + /* probe any arch specific drivers */ + list_for_each(l, &wm97xx_drivers){ + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + c->probe(); + } + + ts.probe = 1; + spin_unlock(&ts.lock); + return 0; + +no_codec: + err("could not find a WM9705/12/13 codec. Found a 0x%4x:0x%4x instead", id1, id2); + spin_unlock(&ts.lock); + return -ENODEV; +} + +/* + * Called by ac97_codec when it is unloaded. + */ +static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver) +{ + struct list_head *l; + + dbg("Unloading AC97 codec %d", codec->id); + + input_unregister_device(&wm97xx_input); + + /* remove any arch specific drivers */ + list_for_each(l, &wm97xx_drivers) { + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + c->remove(); + } + + /* Stop touch read thread */ + if (ts.ts_task) { + ts.ts_task = NULL; + ts.pen_is_down = 1; + wake_up_interruptible (&ts.pen_irq_wait); + wait_for_completion(&ts.ts_exit); + } + + /* Stop AUX sampling thread */ + if (ts.aux_task) { + ts.aux_task = NULL; + ts.aux_waiting = 0; + wake_up_interruptible (&ts.aux_wait); + wait_for_completion (&ts.aux_exit); + } + + spin_lock(&ts.lock); + codec->driver_private = NULL; + wm97xx_codec = NULL; + ts.probe = 0; + + spin_unlock(&ts.lock); +} + +/* AC97 registration info */ +static struct ac97_driver wm9705_driver = { + .codec_id = (WM97XX_ID1 << 16) | WM9705_ID2, + .codec_mask = 0xFFFFFFFF, + .name = "Wolfson WM9705 Touchscreen/Battery Monitor", + .probe = wm97xx_probe, + .remove = __devexit_p(wm97xx_remove), + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static struct ac97_driver wm9712_driver = { + .codec_id = (WM97XX_ID1 << 16) | WM9712_ID2, + .codec_mask = 0xFFFFFFFF, + .name = "Wolfson WM9712 Touchscreen/Battery Monitor", + .probe = wm97xx_probe, + .remove = __devexit_p(wm97xx_remove), + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static struct ac97_driver wm9713_driver = { + .codec_id = (WM97XX_ID1 << 16) | WM9713_ID2, + .codec_mask = 0xFFFFFFFF, + .name = "Wolfson WM9713 Touchscreen/Battery Monitor", + .probe = wm97xx_probe, + .remove = __devexit_p(wm97xx_remove), + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static int __init wm97xx_plugin_init(void) +{ + info("Wolfson WM9705/WM9712/WM9713 Touchscreen Controller driver"); + info("Version %s liam.girdwood@wolfsonmicro.com", WM_TS_VERSION); + + memset(&ts, 0, sizeof(ts)); + init_MUTEX(&ts.sem); + spin_lock_init (&ts.lock); + init_waitqueue_head (&ts.pen_irq_wait); + init_waitqueue_head (&ts.aux_wait); + + /* register with the AC97 layer */ + ac97_register_driver(&wm9705_driver); + ac97_register_driver(&wm9712_driver); + ac97_register_driver(&wm9713_driver); + + return 0; +} + +static void __exit wm97xx_plugin_exit(void) +{ + /* Unregister from the AC97 layer */ + ac97_unregister_driver(&wm9713_driver); + ac97_unregister_driver(&wm9712_driver); + ac97_unregister_driver(&wm9705_driver); +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("WM9705/WM9712/WM9713 Touch Screen / AUX ADC / GPIO Driver"); +MODULE_LICENSE("GPL"); + +module_init(wm97xx_plugin_init); +module_exit(wm97xx_plugin_exit); diff -Nur linux-2.6.9-intc1/sound/oss/Kconfig linux-2.6.9-intc1-mih1/sound/oss/Kconfig --- linux-2.6.9-intc1/sound/oss/Kconfig 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/Kconfig 2005-04-28 16:48:26.000000000 -0400 @@ -1096,16 +1096,39 @@ int " DAC channel" default "1" depends on SOUND_SH_DAC_AUDIO + +config SOUND_WM97XX + tristate "WM97xx touch screen plugin" + depends on SOUND_PRIME!=n + +config SOUND_WM9713 + tristate "WM9713 touch screen plugin" + depends on SOUND_PRIME!=n config SOUND_PXA tristate "PXA Audio" depends on SOUND_PRIME!=n && ARCH_PXA config SOUND_PXA_AC97 - tristate "PXA AC97 audio support" - depends on SOUND_PRIME!=n && SOUND_PXA + tristate 'Intel PXA250/210/27x AC97 audio' + depends on SOUND_PRIME!=n && SOUND_PXA + help + Say Y or M if you have a PXA cpu with an AC97 codec + +config SOUND_NO_SPEAKER_INIT + bool "Disable mixer initialization for SPEAKER" + depends on SOUND_PXA help - Say Y hear or M if you want to include support for AC97 audio. + Say Y if you have a Glencoe board Rev.1.2 and + hear noises from the Beeper after sound initialization + +config SOUND_PXA_WM97XX + tristate "PXA WM97xx continuous touch screen" + depends on SOUND_PRIME!=n && SOUND_PXA_AC97 && SOUND_WM97XX + +config SOUND_PXA_WM97XX_BATT + tristate "PXA WM97xx Battery Monitor" + depends on SOUND_PRIME!=n && SOUND_PXA_AC97 && SOUND_WM97XX config SOUND_UCB1X00 tristate "UCB1X00 core support" diff -Nur linux-2.6.9-intc1/sound/oss/Makefile linux-2.6.9-intc1-mih1/sound/oss/Makefile --- linux-2.6.9-intc1/sound/oss/Makefile 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/Makefile 2005-03-22 15:20:31.000000000 -0500 @@ -79,10 +79,12 @@ obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o +obj-$(CONFIG_SOUND_PXA_WM97XX) += pxa-wm97xx-touch.o +obj-$(CONFIG_SOUND_PXA_WM97XX_BATT) += pxa-wm97xx-battery.o -obj-$(CONFIG_SOUND_PXA) += pxa-audio.o +obj-$(CONFIG_SOUND_PXA) += pxa-audio.o obj-$(CONFIG_SOUND_PXA_AC97) += pxa-ac97.o ac97_codec.o -obj-$(CONFIG_SOUND_UCB1X00) += ucb1x00-core.o +obj-$(CONFIG_SOUND_UCB1X00) += ucb1x00-core.o obj-$(CONFIG_SOUND_UCB1X00_TS) += ucb1x00-ts.o ifeq ($(CONFIG_MIDI_EMU10K1),y) diff -Nur linux-2.6.9-intc1/sound/oss/pxa-ac97.c linux-2.6.9-intc1-mih1/sound/oss/pxa-ac97.c --- linux-2.6.9-intc1/sound/oss/pxa-ac97.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/pxa-ac97.c 2005-05-01 02:35:41.000000000 -0400 @@ -18,6 +18,9 @@ * Added codec init failure error detection, fast codec GPIO * for PXA25x and PXA27x, new PXA27x AC97 read/write and * shared the AC97 interrupt for use with codec specifig GPIO's. + * + * Mar/Apr 2005: Matthias Ihmig + * Modified for Glencoe and backport of some patches from later versions */ #include @@ -39,13 +42,20 @@ #include #include #include -#include - #include "pxa-audio.h" -/* We need a irq tag to request/free our shared irq handler */ -#define AC97_IRQ_TAG 0xac97c0de -//#define DEBUG 1 +#ifdef CONFIG_MACH_MAINSTONE + #include +#endif +#ifdef CONFIG_MACH_GLENCOE + #include +#endif + + +/* We need a dummy irq tag to request/free our shared irq handler */ +#define AC97_IRQ_TAG (void*)0xac97c0de + +//#define DEBUG 1 static DECLARE_MUTEX(CAR_mutex); @@ -57,20 +67,23 @@ static int pxa27x_read_wait(void) { /* XLLP: timeout in reading and writing codec registers through AC link is 200us */ + /* a PXA27x AC97 read takes 4 AC link frames to complete */ int timeout = 20; + u32 gsr; while ( (!(GSR & GSR_SDONE)) && (timeout--) ) { udelay(10); } - GSR = GSR_SDONE | GSR_CDONE; + gsr = GSR; + GSR = gsr | GSR_SDONE | GSR_CDONE; - if (timeout < 0) { + if (timeout <= 0) { printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); return 0; } #ifdef DEBUG - else{ + else { printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (20-timeout)*10); } #endif @@ -80,6 +93,7 @@ static int pxa27x_write_wait(void) { + u32 gsr; /* XLLP: timeout in reading and writing codec registers through AC link is 200us */ int timeout = 20; @@ -87,9 +101,12 @@ udelay(10); } - GSR = GSR_CDONE; + /* a PXA27x AC97 write takes 2 AC link frames to complete */ + udelay (50); + gsr = GSR; + GSR = gsr | GSR_CDONE; - if (timeout < 0) { + if (timeout <= 0) { printk(KERN_CRIT "%s: write codec register timeout.\n", __FUNCTION__); return 0; } @@ -194,7 +211,7 @@ wait_for_completion(&CAR_completion); if (GSR & GSR_RDCS) { - GSR = GSR_RDCS; //write a 1 to clear + GSR |= GSR_RDCS; //write a 1 to clear printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); } @@ -276,12 +293,13 @@ #else GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; #endif - - /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ + /* link should be up after 10 uS */ + udelay(10); + /* XLLP: timeout in waiting for codec's ready signal during setup process is 5ms */ while (!(GSR & GSR_PCR) && timeout--) { - udelay(10); + udelay(100); } - if (timeout < 0) { + if (timeout <= 0) { printk(KERN_CRIT "%s: cold reset codec timeout.\n", __FUNCTION__); return 0; } @@ -303,10 +321,11 @@ #else GCR |= GCR_WARM_RST|GCR_CDONE_IE|GCR_SDONE_IE; #endif + udelay(500); - /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ + /* XLLP: timeout in waiting for codec's ready signal during setup process is 5ms */ while (!(GSR & GSR_PCR) && timeout--) { - udelay(10); + udelay(100); } if (timeout < 0) { printk(KERN_CRIT "%s: warm reset codec timeout.\n", __FUNCTION__); @@ -346,7 +365,15 @@ #ifdef CONFIG_MACH_MAINSTONE MST_MSCWR2 &= 0xffff000b; pxa_gpio_mode(GPIO45_SYSCLK_AC97_MD); -#endif +#endif + +#ifdef CONFIG_MACH_GLENCOE + pxa_gpio_mode(GLN_GPIO_nAUDIO_PWR_DWN | GPIO_OUT); + GPIO_set(GLN_GPIO_nAUDIO_PWR_DWN); + + pxa_gpio_mode(GLN_GPIO_nAC97_INT | GPIO_IN); + set_irq_type(GLENCOE_nAC97_IRQ, IRQT_FALLING); +#endif pxa_gpio_mode(GPIO31_SYNC_AC97_MD); pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); @@ -357,7 +384,7 @@ * Use GPIO 113 as AC97 Reset on Bulverde */ #ifdef CONFIG_PXA27x - pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); + pxa_gpio_mode(GPIO113_AC97_RESET_N_MD); #endif /* @@ -383,10 +410,17 @@ MST_MSCWR2 &= ~0xc0; pxa_gpio_mode(GPIO45_BTRTS_MD); #endif +#ifdef CONFIG_MACH_GLENCOE + GPIO_clr(GLN_GPIO_nAUDIO_PWR_DWN); +#endif + +#ifndef CONFIG_PXA27x free_irq(IRQ_AC97, (void*)AC97_IRQ_TAG); +#endif + GCR = GCR_ACLINK_OFF; pxa_set_cken(CKEN2_AC97, 0); - printk(KERN_ERR "could not initialise AC97 codec."); + printk(KERN_ERR "could not initialise AC97 codec.\n"); return -ENODEV; } @@ -399,6 +433,10 @@ MST_MSCWR2 &= ~0xc0; pxa_gpio_mode(GPIO45_BTRTS_MD); #endif +#ifdef CONFIG_MACH_GLENCOE + GPIO_clr(GLN_GPIO_nAUDIO_PWR_DWN); +#endif + GCR = GCR_ACLINK_OFF; pxa_set_cken(CKEN2_AC97, 0); #ifndef CONFIG_PXA27x @@ -479,7 +517,7 @@ unsigned int cmd, unsigned long arg) { int ret; - long val=0; + long val = 0; switch(cmd) { case SNDCTL_DSP_STEREO: @@ -554,6 +592,83 @@ return pxa_audio_attach(inode, file, &ac97_audio_state); } +static int pxa_ac97_suspend(struct device * dev, u32 state, u32 level) +{ + u16 reg, pwrmode; + int ret, i = 0; + + if ( level != SUSPEND_POWER_DOWN ) + return 0; + + switch (state) { + case 1: pwrmode = AC97_PWR_D1; break; + case 2: pwrmode = AC97_PWR_D2; break; + case 3: pwrmode = AC97_PWR_D3; break; + default: return 1; + } + + ret = ac97_suspend(&pxa_ac97_codec, state); + if (ret) + return ret; + + /* now gracefully power down the codec and AC link */ + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + reg |= (pwrmode & ~(AC97_PWR_PR6|AC97_PWR_PR5|AC97_PWR_PR4)); + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, reg); + udelay(500); + + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + reg |= pwrmode; + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, reg); + + GCR |= GCR_ACLINK_OFF; + pxa_set_cken(CKEN31_AC97, 1); + while (!(GSR & (1 << 3)) && i < 99999) + i++; + pxa_set_cken(CKEN31_AC97, 0); + udelay(20); + pxa_set_cken(CKEN2_AC97, 0); + + return 0; +} + +static int pxa_ac97_resume(struct device * dev, u32 level) +{ + if ( level != RESUME_POWER_ON ) + return 0; + + pxa_set_cken(CKEN2_AC97, 1); +#ifdef CONFIG_MACH_MAINSTONE + MST_MSCWR2 &= 0xffff000b; +#endif + if (pxa_ac97_warm_reset() < 0) { + /* ok, warm reset didn't wake the codec, so we have to do + * a cold reset and restore the codec state + */ + ac97_probe_codec(&pxa_ac97_codec); + } + /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ + int timeout=50; + while (!(GSR & GSR_PCR) && timeout--) { + udelay(10); + } + + if (timeout < 0) { + printk(KERN_CRIT "%s: resume codec timeout.\n", __FUNCTION__); + } +#ifdef DEBUG + else{ + printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (50-timeout)*10); + } +#endif + + /* make sure that everything we need is powered up */ + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, AC97_PWR_D0); + ac97_resume(&pxa_ac97_codec); + + return 0; +} + /* * Missing fields of this structure will be patched with the call * to pxa_audio_attach(). @@ -565,7 +680,7 @@ }; -static int pxa_ac97_probe(struct device *dev) +static int __devinit pxa_ac97_probe(struct device *dev) { int ret; struct ac97_codec *dummy; @@ -576,100 +691,45 @@ ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); - + ac97_audio_state.output_stream->dev = dev; + ac97_audio_state.input_stream->dev = dev; + return 0; } -static int pxa_ac97_remove(struct device *dev) +static int __devexit pxa_ac97_remove(struct device *dev) { unregister_sound_dsp(ac97_audio_state.dev_dsp); unregister_sound_mixer(pxa_ac97_codec.dev_mixer); pxa_ac97_put(); - + ac97_audio_state.output_stream->dev = NULL; + ac97_audio_state.input_stream->dev = NULL; return 0; } -/* - * Suspend & Resume the PXA AC97 Controller. - */ -static int pxa_ac97_suspend(struct device * dev, u32 state, u32 level) -{ - if (level == SUSPEND_POWER_DOWN) { - /* initiate power-down of codec */ - unsigned int pr; - int i = 0; - - printk("AC97 suspend\n"); - pr = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); - pr |= AC97_PWR_PR4; - pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, pr); - GCR |= GCR_ACLINK_OFF; - pxa_set_cken(CKEN31_AC97, 1); - while (!(GSR & (1 << 3)) && i < 99999) - i++; - pxa_set_cken(CKEN31_AC97, 0); - udelay(20); - pxa_set_cken(CKEN2_AC97, 0); - } - return 0; -} - -static int pxa_ac97_resume(struct device * dev, u32 level) -{ - if (level == RESUME_POWER_ON) { - printk("ac97 resume\n"); - pxa_set_cken(CKEN2_AC97, 1); -#ifdef CONFIG_MACH_MAINSTONE - MST_MSCWR2 &= 0xffff000b; -#endif - - /* cold reset the codec */ - GCR = 0; - pxa_set_cken(CKEN31_AC97, 1); - udelay(120); - pxa_set_cken(CKEN31_AC97, 0); - GCR = GCR_COLD_RST; - udelay(30); - - /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ - int timeout=50; - while (!(GSR & GSR_PCR) && timeout--) { - udelay(10); - } - - if (timeout < 0) { - printk(KERN_CRIT "%s: resume codec timeout.\n", __FUNCTION__); - } -#ifdef DEBUG - else{ - printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (50-timeout)*10); - } -#endif - } - return 0; -} - -static struct device_driver pxa_ac97_driver = { - .name = "pxa2xx-ac97", - .probe = pxa_ac97_probe, - .remove = pxa_ac97_remove, - .bus = &platform_bus_type, - .suspend = pxa_ac97_suspend, - .resume = pxa_ac97_resume, +static struct device_driver pxa_ac97_driver = { + .name = "pxa-ac97", + .bus = &platform_bus_type, + .probe = pxa_ac97_probe, + .remove = pxa_ac97_remove, + .suspend = pxa_ac97_suspend, + .resume = pxa_ac97_resume, + .shutdown = NULL, }; - -static int __devinit pxa_ac97_init(void) + +static int __init pxa_ac97_init(void) { return driver_register(&pxa_ac97_driver); } static void __exit pxa_ac97_exit(void) { - return driver_unregister(&pxa_ac97_driver); + driver_unregister(&pxa_ac97_driver); } module_init(pxa_ac97_init); module_exit(pxa_ac97_exit); -MODULE_LICENSE("GPL"); - +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("AC97 interface for the Cotula chip"); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/sound/oss/pxa-audio.c linux-2.6.9-intc1-mih1/sound/oss/pxa-audio.c --- linux-2.6.9-intc1/sound/oss/pxa-audio.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/pxa-audio.c 2005-03-30 19:20:27.000000000 -0500 @@ -778,6 +778,9 @@ down(&state->sem); + /* set file to be nonseekable */ + nonseekable_open(inode,file); + /* access control */ err = -ENODEV; if ((file->f_mode & FMODE_WRITE) && !os) diff -Nur linux-2.6.9-intc1/sound/oss/pxa-audio.h linux-2.6.9-intc1-mih1/sound/oss/pxa-audio.h --- linux-2.6.9-intc1/sound/oss/pxa-audio.h 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/pxa-audio.h 2005-02-21 21:26:35.000000000 -0500 @@ -24,6 +24,7 @@ u_int dma_frag; /* DMA fragment index */ u_int fragsize; /* fragment size */ u_int nbfrags; /* number of fragments */ + struct device *dev; /* device handle for DMA */ u_int dma_ch; /* DMA channel number */ dma_addr_t dma_desc_phys; /* phys addr of descriptor ring */ u_int descs_per_frag; /* nbr descriptors per fragment */ diff -Nur linux-2.6.9-intc1/sound/oss/pxa-wm97xx-battery.c linux-2.6.9-intc1-mih1/sound/oss/pxa-wm97xx-battery.c --- linux-2.6.9-intc1/sound/oss/pxa-wm97xx-battery.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/oss/pxa-wm97xx-battery.c 2005-02-07 15:03:20.000000000 -0500 @@ -0,0 +1,212 @@ +/* + * wm97xx-battery.c -- Battery monitor for + * Wolfson WM97xx AC97 Codecs. + * + * Copyright 2004 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * 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. + * + * Notes: + * This code uses an API exposed by ac97_plugin_wm97xx to capture battery + * data. + * + * Features: + * - codecs supported:- WM9712, WM9713 + * + * Revision history + * 20th Sep 2004 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" + +#define TS_NAME "wm97xx_battery" +#define WM_TS_VERSION "0.2" + +/* + * Battery sampling period jiffies. + */ +static int period = 2; +module_param(period, int, 0); +MODULE_PARM_DESC(period, "Battery sampling period (secs)"); + +static struct wm97xx_device *dev; + +#ifdef CONFIG_PXA27x +/* + * Sometimes the PXA27x cannot reset sticky codec GPIO's. In this case we + * also need to reset the GPIO in codec space. + */ +static int pxa27x_clear_sticky(int status) +{ + volatile u32 *reg_addr; + u32 gsr; + + reg_addr = (u32 *)&PAC_REG_BASE + (0x54 >> 1); + *reg_addr = (status & ~WM97XX_GPIO_13) << 1; + + udelay(50); + gsr = GSR; + GSR = gsr & GSR_CDONE; + + if (!(gsr & GSR_CDONE)) { + printk(KERN_CRIT "%s: write codec register timeout.\n", __FUNCTION__); + return 0; + } + return 1; +} +#endif + + +/* + * PXA arch pen down handler. + */ +static int pxa_handler(int status) +{ + u32 gsr; +#ifdef CONFIG_PXA27x + pxa27x_clear_sticky(status); +#endif + gsr = GSR; + GSR = gsr & GSR_GSCI; + return 0; +} + +static int battery_dead_handler(int status) +{ + printk("dead\n"); + pxa_handler(status); + return 0; +} + +static int battery_low_handler(int status) +{ + printk("low\n"); + pxa_handler(status); + return 0; +} + +static void battery_read (wm97xx_aux_conv_t ac, int stage) +{ + int val = 0; + + if (stage) { + val = wm97xx_get_auxconv(dev, WM97XX_AC_BMON); + printk("got %d\n", val & 0xfff); + } +} + +static int pxa_wm97xx_probe(void) +{ + /* register the battery alarm handlers */ + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + return -ENODEV; + case WM9712_ID2: + case WM9713_ID2: + /* use the virtual PEN_DOWN GPIO to send irq over AC97 */ + wm97xx_set_codec_irq(dev, IRQ_AC97, 1); + wm97xx_request_codec_event(dev, WM97XX_GPIO_14, battery_low_handler); + wm97xx_request_codec_event(dev, WM97XX_GPIO_15, battery_dead_handler); + GCR |= GCR_GIE; + break; + } + + /* register battery reader */ + wm97xx_request_auxconv(dev, WM97XX_AC_BMON, period * HZ, battery_read); + return 0; +} + +static int pxa_wm97xx_remove(void) +{ + wm97xx_free_auxconv(dev, WM97XX_AC_BMON); + + /* codec specific deconfig */ + switch (wm97xx_id & 0xffff) { + case WM9712_ID2: + case WM9713_ID2: + /* disable interrupt */ + if (wm97xx_clear_codec_irq(dev) == 0) + GCR &= ~GCR_GIE; + wm97xx_free_codec_event(dev, WM97XX_GPIO_14); + wm97xx_free_codec_event(dev, WM97XX_GPIO_15); + break; + } + return 0; +} + +#if defined(CONFIG_PM) +static int pxa_wm97xx_suspend(u32 state, u32 level) +{ + return 0; +} + +static int pxa_wm97xx_resume(u32 level) +{ + return 0; +} +#else +#define pxa_wm97xx_suspend NULL +#define pxa_wm97xx_resume NULL +#endif + +static struct wm97xx_device pxa_wm97xx_batt_driver = { + .name = "pxa-wm97xx-battery", + .probe = pxa_wm97xx_probe, + .remove = pxa_wm97xx_remove, + .suspend= pxa_wm97xx_suspend, + .resume = pxa_wm97xx_resume, + .cont_read = NULL, +}; + +static int __init pxa_wm97xx_battery_init(void) +{ + dev = &pxa_wm97xx_batt_driver; + return wm97xx_driver_register(dev); +} + +static void __exit pxa_wm97xx_battery_exit(void) +{ + wm97xx_driver_unregister(dev); + dev = NULL; +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("PXA WM97xx Battery Monitor"); +MODULE_LICENSE("GPL"); + +module_init(pxa_wm97xx_battery_init); +module_exit(pxa_wm97xx_battery_exit); diff -Nur linux-2.6.9-intc1/sound/oss/pxa-wm97xx-touch.c linux-2.6.9-intc1-mih1/sound/oss/pxa-wm97xx-touch.c --- linux-2.6.9-intc1/sound/oss/pxa-wm97xx-touch.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/oss/pxa-wm97xx-touch.c 2005-02-26 15:12:27.000000000 -0500 @@ -0,0 +1,311 @@ +/* + * pxa-wm97xx-touch.c -- PXA Continuous Touch screen driver for + * Wolfson WM97xx AC97 Codecs. + * + * Copyright 2004 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * 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. + * + * Notes: + * This code uses an API exposed by ac97_plugin_wm97xx to capture touch + * data in a continuous manner on the Intel XScale archictecture + * + * Features: + * - codecs supported:- WM9705, WM9712, WM9713 + * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x + * + * Revision history + * 18th Aug 2004 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" + +#define TS_NAME "pxa_wm97xx_touch" +#define WM_TS_VERSION "0.4" + +#define WM97XX_PXA_SLOT 5 + +/* + * The WM97xx codecs can signal a pen down event by either sending an irq over + * the AC97 link (not wm9705) or by asserting a pen down GPIO to interrupt the + * OS. Set WM_PEN_DOWN_GPIO to 1 if you are using the dedicated GPIO to signal + * pen down events, otherwise set to 0 if you want to signal over the AC link. + */ +#define WM_PEN_DOWN_GPIO 0 + +/* + * Pen sampling frequency in continuous mode. + * + * This parameter needs an additional architecture-dependent driver + * such as pxa-ac97-wm97xx. + * + * The sampling frequency is given by a number in range 0-3: + * + * 0: 93.75Hz 1: 187.5Hz 2: 375Hz 3: 750Hz + */ +static int cont_rate = 1; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (0-3, default 2)"); + + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int = 1; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* flush AC97 slot 5 FIFO on pxa machines */ +#define PXA_FLUSH_FIFO \ + do { \ + int count = 0; \ + for (; count < 16; count++) MODR; \ + } while (0); + +static struct wm97xx_device *dev; + +static int pxa_wm97xx_cont_read (struct input_dev* idev, int pen_down_time, int pressure) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int count = 0, find_x = 3; + + /* sometimes there is stale data in the FIFO just after a pen down */ + if (pen_down_time < 4 ) { + PXA_FLUSH_FIFO; + return RC_PENDOWN; + } + + /* find the first X and sync */ + while (find_x--) { + x = MODR; + if ((x & 0x7000) == WM97XX_ADCSEL_X) + break; + } + + /* are we now syncronised X,Y[,P],X,Y[,P],X... ? */ + if (!find_x) + return RC_PENDOWN; + + /* read Y [and pressure] */ + y = MODR & 0xffff; + if (pressure) + p = MODR & 0xffff; + +#ifdef CONFIG_PXA27x + while (MISR & (1 << 2)) { +#else + while (count < 16) { +#endif + + /* are samples valid */ + if ((x & 0x7000) != WM97XX_ADCSEL_X || + (y & 0x7000) != WM97XX_ADCSEL_Y || + (p & 0x7000) != WM97XX_ADCSEL_PRES) + goto next; + + /* is pen down pressure great enough ? */ + if (pressure && ((p & 0xfff) > pressure)) + goto next; + + /* coordinate is good */ + printk(KERN_DEBUG "x %x y %x p %x\n", x,y,p); + input_report_abs (idev, ABS_X, x & 0xfff); + input_report_abs (idev, ABS_Y, y & 0xfff); + input_report_abs (idev, ABS_PRESSURE, p & 0xfff); + input_sync (idev); + +next: + x = MODR; + y = MODR; + count += 2; + if (pressure) { + p = MODR; + count++; + } + } + + return RC_PENDOWN; +} + +#ifdef CONFIG_PXA27x +/* + * Sometimes the PXA27x cannot reset sticky codec GPIO's. In this case we + * also need to reset the GPIO in codec space. + */ +static int pxa27x_clear_sticky(int status) +{ + volatile u32 *reg_addr; + u32 gsr; + + reg_addr = (u32 *)&PAC_REG_BASE + (0x54 >> 1); + *reg_addr = (status & ~WM97XX_GPIO_13) << 1; + + udelay(50); + gsr = GSR; + GSR = gsr & GSR_CDONE; + + if (!(gsr & GSR_CDONE)) { + printk(KERN_CRIT "%s: write codec register timeout.\n", __FUNCTION__); + return 0; + } + return 1; +} +#endif + + +/* + * PXA arch pen down handler. + */ +static int pxa_pen_handler(int status) +{ + u32 gsr; +#ifdef CONFIG_PXA27x + pxa27x_clear_sticky(status); +#endif + gsr = GSR; + GSR = gsr & GSR_GSCI; + return 0; +} + + +static int pxa_wm97xx_probe(void) +{ + /* codec specific irq config */ + if (pen_int) { + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + wm97xx_set_pen_irq(dev, IRQ_GPIO(4)); + set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); + break; + case WM9712_ID2: + case WM9713_ID2: + /* enable pen down interrupt */ +#if WM_PEN_DOWN_GPIO + /* use the PEN_DOWN GPIO */ + wm97xx_set_pen_irq(dev, GLENCOE_nAC97_IRQ); + wm97xx_config_codec_gpio(dev, WM97XX_GPIO_3, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); +#else + /* use the virtual PEN_DOWN GPIO to send irq over AC97 */ + wm97xx_set_codec_irq(dev, IRQ_AC97, 1); + wm97xx_request_codec_event(dev, WM97XX_GPIO_13, pxa_pen_handler); + GCR |= GCR_GIE; +#endif + break; + default: + printk("pen down irq not supported on this device\n"); + pen_int = 0; + break; + } + } + + /* Go you big red fire engine */ + wm97xx_set_continuous_mode (dev, pxa_wm97xx_cont_read, 1, WM97XX_PXA_SLOT, cont_rate); + return 0; +} + +static int pxa_wm97xx_remove(void) +{ + /* codec specific deconfig */ + if (pen_int) { + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + wm97xx_clear_pen_irq(dev); + break; + case WM9712_ID2: + case WM9713_ID2: + /* disable interrupt */ +#if WM_PEN_DOWN_GPIO + wm97xx_clear_pen_irq(dev); +#else + if (wm97xx_clear_codec_irq(dev) == 0) + GCR &= ~GCR_GIE; + wm97xx_free_codec_event(dev, WM97XX_GPIO_13); +#endif + break; + } + } + + wm97xx_set_continuous_mode (dev, NULL, 0, 0, 0); + return 0; +} + +#if defined(CONFIG_PM) +static int pxa_wm97xx_suspend(u32 state, u32 level) +{ + return 0; +} + +static int pxa_wm97xx_resume(u32 level) +{ + return 0; +} +#else +#define pxa_wm97xx_suspend NULL +#define pxa_wm97xx_resume NULL +#endif + +static struct wm97xx_device pxa_wm97xx_touch_driver = { + .name = "pxa-wm97xx-touch", + .probe = pxa_wm97xx_probe, + .remove = pxa_wm97xx_remove, + .suspend= pxa_wm97xx_suspend, + .resume = pxa_wm97xx_resume, + .cont_read = pxa_wm97xx_cont_read, +}; + +static int __init pxa_wm97xx_touch_init(void) +{ + dev = &pxa_wm97xx_touch_driver; + return wm97xx_driver_register(dev); +} + +static void __exit pxa_wm97xx_touch_exit(void) +{ + wm97xx_driver_unregister(&pxa_wm97xx_touch_driver); + dev = NULL; +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("AC97 continuous touch driver for PXA25x,PXA26x and PXA27x"); +MODULE_LICENSE("GPL"); + +module_init(pxa_wm97xx_touch_init); +module_exit(pxa_wm97xx_touch_exit); diff -Nur linux-2.6.9-intc1/sound/oss/ucb1x00-core.c linux-2.6.9-intc1-mih1/sound/oss/ucb1x00-core.c --- linux-2.6.9-intc1/sound/oss/ucb1x00-core.c 2005-05-02 15:16:11.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/oss/ucb1x00-core.c 2005-02-21 19:19:52.000000000 -0500 @@ -590,6 +590,12 @@ } #endif +#ifdef CONFIG_MACH_GLENCOE + if (machine_is_glencoe()) { + ucb->irq = GLENCOE_nAC97_IRQ; + } +#endif + #ifdef CONFIG_PXA_CERF_PDA if (machine_is_pxa_cerf()) { irq_gpio_pin = CERF_GPIO_UCB1400_IRQ; diff -Nur linux-2.6.9-intc1/sound/oss/wm97xx.h linux-2.6.9-intc1-mih1/sound/oss/wm97xx.h --- linux-2.6.9-intc1/sound/oss/wm97xx.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/oss/wm97xx.h 2005-02-07 15:03:20.000000000 -0500 @@ -0,0 +1,301 @@ +/* + * Register bits for Wolfson WM97xx series of codecs + */ + +#ifndef _WM97XX_H_ +#define _WM97XX_H_ + +#include /* AC97 control layer */ +#include /* Input device layer */ + +/* + * WM97xx AC97 Touchscreen registers + */ +#define AC97_WM97XX_DIGITISER1 0x76 +#define AC97_WM97XX_DIGITISER2 0x78 +#define AC97_WM97XX_DIGITISER_RD 0x7a +#define AC97_WM9713_DIG1 0x74 +#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 +#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 + +/* + * WM97xx register bits + */ +#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ +#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ +#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ +#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ +#define WM97XX_ADCSEL_MASK 0x7000 +#define WM97XX_COO 0x0800 /* enable coordinate mode */ +#define WM97XX_CTC 0x0400 /* enable continuous mode */ +#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ +#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ +#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ +#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ +#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ +#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ +#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ +#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ +#define WM97XX_CM_RATE_MASK 0x03f0 +#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) +#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ +#define WM97XX_DELAY_MASK 0x00f0 +#define WM97XX_SLEN 0x0008 /* slot read back enable */ +#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ +#define WM97XX_SLT_MASK 0x0007 +#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ +#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ +#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ +#define WM97XX_RPR 0x2000 /* wake up on pen down */ +#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ +#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ + +/* WM9712 Bits */ +#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ +#define WM9712_PDEN 0x0800 /* measure only when pen down */ +#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ +#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ +#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ +#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ +#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ +#define WM9712_ADCSEL_COMP1 0x4000 /* COMP1/AUX1 measurement (pin29) */ +#define WM9712_ADCSEL_COMP2 0x5000 /* COMP2/AUX2 measurement (pin30) */ +#define WM9712_ADCSEL_BMON 0x6000 /* BMON/AUX3 measurement (pin31) */ +#define WM9712_ADCSEL_WIPER 0x7000 /* WIPER/AUX4 measurement (pin12) */ +#define WM9712_PD(i) (0x1 << i) /* power management */ + +/* WM9712 Registers */ +#define AC97_WM9712_POWER 0x24 +#define AC97_WM9712_REV 0x58 + +/* WM9705 Bits */ +#define WM9705_PDEN 0x1000 /* measure only when pen is down */ +#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ +#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ +#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ +#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ +#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ +#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ +#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ +#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ +#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ +#define WM9705_ADCSEL_BMON 0x4000 /* BMON measurement */ +#define WM9705_ADCSEL_AUX 0x5000 /* AUX measurement */ +#define WM9705_ADCSEL_PHONE 0x6000 /* PHONE measurement */ +#define WM9705_ADCSEL_PCBEEP 0x7000 /* PCBEEP measurement */ + +/* WM9713 Bits */ +#define WM9713_PDPOL 0x0400 /* Pen down polarity */ +#define WM9713_POLL 0x0200 /* initiate a polling measurement */ +#define WM9713_CTC 0x0100 /* enable continuous mode */ +#define WM9713_ADCSEL_AUX4 0x0080 /* AUX 4 measurement */ +#define WM9713_ADCSEL_AUX3 0x0040 /* AUX 3 measurement */ +#define WM9713_ADCSEL_AUX2 0x0020 /* AUX 2 measurement */ +#define WM9713_ADCSEL_AUX1 0x0010 /* AUX 1 measurement */ +#define WM9713_ADCSEL_X 0x0002 /* X measurement */ +#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ +#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ +#define WM9713_COO 0x0001 /* enable coordinate mode */ +#define WM9713_PDEN 0x0800 /* measure only when pen down */ +#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ + +/* AUX ADC ID's */ +#define TS_COMP1 0x0 +#define TS_COMP2 0x1 +#define TS_BMON 0x2 +#define TS_WIPER 0x3 + +/* ID numbers */ +#define WM97XX_ID1 0x574d +#define WM9712_ID2 0x4c12 +#define WM9705_ID2 0x4c05 +#define WM9713_ID2 0x4c13 + +/* Codec GPIO's */ +#define WM97XX_MAX_GPIO 16 +#define WM97XX_GPIO_1 (1 << 1) +#define WM97XX_GPIO_2 (1 << 2) +#define WM97XX_GPIO_3 (1 << 3) +#define WM97XX_GPIO_4 (1 << 4) +#define WM97XX_GPIO_5 (1 << 5) +#define WM97XX_GPIO_6 (1 << 6) +#define WM97XX_GPIO_7 (1 << 7) +#define WM97XX_GPIO_8 (1 << 8) +#define WM97XX_GPIO_9 (1 << 9) +#define WM97XX_GPIO_10 (1 << 10) +#define WM97XX_GPIO_11 (1 << 11) +#define WM97XX_GPIO_12 (1 << 12) +#define WM97XX_GPIO_13 (1 << 13) +#define WM97XX_GPIO_14 (1 << 14) +#define WM97XX_GPIO_15 (1 << 15) + + +#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ + + +/*---------------- Return codes from sample reading functions ---------------*/ + +/* More data is available; call the sample gathering function again */ +#define RC_AGAIN 0x00000001 +/* The returned sample is valid */ +#define RC_VALID 0x00000002 +/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ +#define RC_PENUP 0x00000004 +/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful + to tell the handler that the pen is down but we don't know yet his coords, + so the handler should not sleep or wait for pendown irq) */ +#define RC_PENDOWN 0x00000008 + +/* The wm97xx driver provides a private API for writing platform-specific + * drivers. + */ + +/* The structure used to return sampled data into */ +typedef struct { + int x; + int y; + int p; +} wm97xx_data_t; + +/* The identifier of an auxiliary ADC conversion. + */ +typedef enum { + WM97XX_AC_BMON, + WM97XX_AC_AUX1, /* WM9705: AUX WM9712: COMP1 */ + WM97XX_AC_AUX2, /* WM9705: PHONE WM9712: COMP2 */ + WM97XX_AC_AUX3 /* WM9705: PCBEEP WM9712: WIPER */ +} wm97xx_aux_conv_t; + +/* Codec GPIO status + */ +typedef enum { + WM97XX_GPIO_HIGH, + WM97XX_GPIO_LOW +} wm97xx_gpio_status_t; + +/* Codec GPIO direction + */ +typedef enum { + WM97XX_GPIO_IN, + WM97XX_GPIO_OUT +} wm97xx_gpio_dir_t; + +/* Codec GPIO polarity + */ +typedef enum { + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_POL_LOW +} wm97xx_gpio_pol_t; + +/* Codec GPIO sticky + */ +typedef enum { + WM97XX_GPIO_STICKY, + WM97XX_GPIO_NOTSTICKY +} wm97xx_gpio_sticky_t; + +/* Codec GPIO wake + */ +typedef enum { + WM97XX_GPIO_WAKE, + WM97XX_GPIO_NOWAKE +} wm97xx_gpio_wake_t; + +/* + * codec event callback. + */ +typedef int (*codec_event_t)(int status); + +/* + * continuous mode read callback + */ +typedef int (*codec_read_t)(struct input_dev* idev, int pen_down_time, int pressure); + +/* + * codec id + */ +extern u32 wm97xx_id; + +struct wm97xx_device { + char *name; + struct list_head devices; + int (*probe) (void); + int (*remove) (void); + int (*suspend) (u32 state, u32 level); + int (*resume) (u32 level); + codec_read_t cont_read; +}; + +/* + * WM97xx arch specific drivers must register with the AC97 plugin + */ +extern int wm97xx_driver_register (struct wm97xx_device *dev); +extern void wm97xx_driver_unregister (struct wm97xx_device *dev); + +/* Enable or disable continuous mode. The driver itself doesn't + * support continuous mode since it is highly architecture-specific. + */ +extern void wm97xx_set_continuous_mode (struct wm97xx_device *dev, codec_read_t read, int enable, int slot, int rate); + +/* A special function to be called before and after the auxiliary conversion. + * This function can e.g. toggle some external switch via a platform specific + * GPIO to route the required analog signal to the respective WM97xx input. + * The 'stage' parameter is 0 before conversion, 1 after. + */ +typedef void (*wm97xx_ac_prepare) (wm97xx_aux_conv_t ac, int stage); + +/* Start a periodic auxiliary ADC conversion with given period. + * Note that auxiliary conversions are performed only when the + * pen is not down, since they are considered slowly-changing + * events. Period is given in jiffies. Additionally, a function can + * be defined to be called before and after every conversion. This + * can be used e.g. to route some analog signal to the respective + * input via platform-specific GPIOs and such. + * Use a period of 0 to disable sampling. + */ +extern int wm97xx_request_auxconv(struct wm97xx_device *dev, wm97xx_aux_conv_t ac, int period, wm97xx_ac_prepare func); +extern void wm97xx_free_auxconv(struct wm97xx_device *dev, wm97xx_aux_conv_t ac); + +/* Get the result of an auxiliary ADC conversion. + * If an auxiliary ADC reading is not yet available, the function + * returns -1. The platform-specific driver must start + */ +extern int wm97xx_get_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac); + +/* Codec IRQ access + * The codec can generate IRQ's (over AC97 link or IRQ pin) for + * numerous external or internal events. This is used to + * enable and disable codec IRQ's + */ +extern int wm97xx_set_codec_irq(struct wm97xx_device *dev, int irq, int link); +extern int wm97xx_clear_codec_irq(struct wm97xx_device *dev); + +/* Register interest in external/internal codec events */ +extern int wm97xx_request_codec_event(struct wm97xx_device *dev, int gpio, codec_event_t work); +extern void wm97xx_free_codec_event(struct wm97xx_device *dev, int gpio); + +/* Codec PEN IRQ access + * The codec can additionally generate an IRQ (using a dedicated pin) + * for Pen down events. This is used to enable and disable codec + * Pen down IRQ's + */ +extern int wm97xx_set_pen_irq(struct wm97xx_device *dev, int irq); +extern void wm97xx_clear_pen_irq(struct wm97xx_device *dev); + +/* Codec GPIO access (not supported on WM9705) + * This can be used to set/get codec GPIO and Virtual GPIO status. + */ +extern wm97xx_gpio_status_t wm97xx_get_codec_gpio (struct wm97xx_device *dev, u32 gpio); +extern void wm97xx_set_codec_gpio (struct wm97xx_device *dev, u32 gpio, wm97xx_gpio_status_t status); +extern void wm97xx_config_codec_gpio (struct wm97xx_device *dev, u32 gpio, wm97xx_gpio_dir_t dir, wm97xx_gpio_pol_t pol, + wm97xx_gpio_sticky_t sticky, wm97xx_gpio_wake_t wake); + +/* Codec register access + * This can be used to read and write codec registers. + */ +extern u16 wm97xx_read (struct wm97xx_device *dev, u16 reg); +extern void wm97xx_write (struct wm97xx_device *dev, u16 reg, u16 val); + +#endif diff -Nur linux-2.6.9-intc1/sound/pci/ac97/ac97_bus.c linux-2.6.9-intc1-mih1/sound/pci/ac97/ac97_bus.c --- linux-2.6.9-intc1/sound/pci/ac97/ac97_bus.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.9-intc1-mih1/sound/pci/ac97/ac97_bus.c 2005-04-22 02:26:33.000000000 -0400 @@ -0,0 +1,80 @@ +/* + * Linux driver model AC97 bus interface + * + * Author: Nicolas Pitre + * Created: Jan 14, 2005 + * Copyright: (C) MontaVista Software 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. + */ + +#include +#include +#include +#include + +#include +#include +#include + + +static int ac97_bus_match(struct device *dev, struct device_driver *drv) +{ + return (strncmp(drv->name, dev->bus_id, BUS_ID_SIZE) == 0); +} + +static int ac97_bus_suspend(struct device *dev, unsigned int state) +{ + int ret = 0; + + if (dev->driver && dev->driver->suspend) { + ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); + } + return ret; +} + +static int ac97_bus_resume(struct device *dev) +{ + int ret = 0; + + if (dev->driver && dev->driver->resume) { + ret = dev->driver->resume(dev, RESUME_POWER_ON); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_ENABLE); + } + return ret; +} + +struct bus_type ac97_bus_type = { + .name = "ac97", + .match = ac97_bus_match, + .suspend = ac97_bus_suspend, + .resume = ac97_bus_resume, +}; + +static int __init ac97_bus_init(void) +{ + return bus_register(&ac97_bus_type); +} + +subsys_initcall(ac97_bus_init); + +static void __exit ac97_bus_exit(void) +{ + bus_unregister(&ac97_bus_type); +} + +module_exit(ac97_bus_exit); + +EXPORT_SYMBOL(ac97_bus_type); + +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9-intc1/sound/pci/ac97/ac97_codec.c linux-2.6.9-intc1-mih1/sound/pci/ac97/ac97_codec.c --- linux-2.6.9-intc1/sound/pci/ac97/ac97_codec.c 2004-10-18 17:53:50.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/pci/ac97/ac97_codec.c 2005-04-22 17:57:15.000000000 -0400 @@ -1063,6 +1063,8 @@ { if (ac97) { snd_ac97_proc_done(ac97); + if (ac97->dev.bus) + device_unregister(&ac97->dev); if (ac97->bus) { ac97->bus->codec[ac97->num] = NULL; if (ac97->bus->shared_type) { @@ -1726,6 +1728,22 @@ sprintf(name + strlen(name), " id %x", id & 0xff); } +/** + * snd_ac97_get_short_name - retrieve codec name + * @ac97: the codec instance + * + * Returns the short identifying name of the codec. + */ +const char *snd_ac97_get_short_name(ac97_t *ac97) +{ + const ac97_codec_id_t *pid; + + for (pid = snd_ac97_codec_ids; pid->id; pid++) + if (pid->id == (ac97->id & pid->mask)) + return pid->name; + return "unknown codec"; +} + /* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready @@ -2085,6 +2103,12 @@ snd_ac97_free(ac97); return err; } + ac97->dev.bus = &ac97_bus_type; + ac97->dev.parent = ac97->bus->card->dev; + ac97->dev.platform_data = ac97; + strncpy(ac97->dev.bus_id, snd_ac97_get_short_name(ac97), BUS_ID_SIZE); + if ((err = device_register(&ac97->dev)) < 0) + return err; *rac97 = ac97; if (bus->shared_type) { @@ -2433,6 +2457,7 @@ EXPORT_SYMBOL(snd_ac97_write_cache); EXPORT_SYMBOL(snd_ac97_update); EXPORT_SYMBOL(snd_ac97_update_bits); +EXPORT_SYMBOL(snd_ac97_get_short_name); EXPORT_SYMBOL(snd_ac97_bus); EXPORT_SYMBOL(snd_ac97_mixer); EXPORT_SYMBOL(snd_ac97_pcm_assign); diff -Nur linux-2.6.9-intc1/sound/pci/ac97/Makefile linux-2.6.9-intc1-mih1/sound/pci/ac97/Makefile --- linux-2.6.9-intc1/sound/pci/ac97/Makefile 2004-10-18 17:54:37.000000000 -0400 +++ linux-2.6.9-intc1-mih1/sound/pci/ac97/Makefile 2005-04-22 02:26:18.000000000 -0400 @@ -7,7 +7,7 @@ snd-ak4531-codec-objs := ak4531_codec.o # Toplevel Module Dependency -obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o +obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o ac97_bus.o obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o obj-m := $(sort $(obj-m))