diff -Nru linux-2.6.23/.gitignore kernel.android/.gitignore --- linux-2.6.23/.gitignore 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/.gitignore 1970-01-01 10:00:00.000000000 +1000 @@ -1,52 +0,0 @@ -# -# NOTE! Don't add files that are generated in specific -# subdirectories here. Add them in the ".gitignore" file -# in that subdirectory instead. -# -# Normal rules -# -.* -*.o -*.o.* -*.a -*.s -*.ko -*.so -*.mod.c -*.i -*.lst -*.symtypes - -# -# Top-level generic files -# -tags -TAGS -vmlinux* -!vmlinux.lds.S -System.map -Module.symvers - -# -# Generated include files -# -include/asm -include/asm-*/asm-offsets.h -include/config -include/linux/autoconf.h -include/linux/compile.h -include/linux/version.h -include/linux/utsrelease.h - -# stgit generated dirs -patches-* - -# quilt's files -patches -series - -# cscope files -cscope.* - -*.orig -*.rej diff -Nru linux-2.6.23/.mailmap kernel.android/.mailmap --- linux-2.6.23/.mailmap 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/.mailmap 1970-01-01 10:00:00.000000000 +1000 @@ -1,98 +0,0 @@ -# -# This list is used by git-shortlog to fix a few botched name translations -# in the git archive, either because the author's full name was messed up -# and/or not always written the same way, making contributions from the -# same person appearing not to be so or badly displayed. -# -# repo-abbrev: /pub/scm/linux/kernel/git/ -# - -Aaron Durbin -Adam Oldham -Adam Radford -Adrian Bunk -Alan Cox -Alan Cox -Aleksey Gorelov -Al Viro -Al Viro -Andreas Herrmann -Andrew Morton -Andrew Vasquez -Andy Adamson -Arnaud Patard -Arnd Bergmann -Axel Dyks -Ben Gardner -Ben M Cahill -Björn Steinbrink -Brian Avery -Brian King -Christoph Hellwig -Corey Minyard -David Brownell -David Woodhouse -Domen Puncer -Douglas Gilbert -Ed L. Cashin -Evgeniy Polyakov -Felipe W Damasio -Felix Kuhling -Felix Moeller -Filipe Lautert -Franck Bui-Huu -Frank Zago -Greg Kroah-Hartman -Greg Kroah-Hartman -Greg Kroah-Hartman -Henk Vergonet -Henrik Kretzschmar -Herbert Xu -Jacob Shin -James Bottomley -James Bottomley -James E Wilson -James Ketrenos -Jean Tourrilhes -Jeff Garzik -Jens Axboe -Jens Osterkamp -John Stultz -Juha Yrjola -Juha Yrjola -Juha Yrjola -Kay Sievers -Kenneth W Chen -Koushik -Leonid I Ananiev -Linas Vepstas -Matthieu CASTET -Michael Buesch -Michael Buesch -Michel Dänzer -Mitesh shah -Morten Welinder -Morten Welinder -Morten Welinder -Morten Welinder -Nguyen Anh Quynh -Paolo 'Blaisorblade' Giarrusso -Patrick Mochel -Peter A Jonsson -Praveen BP -Rajesh Shah -Ralf Baechle -Ralf Wildenhues -Rémi Denis-Courmont -Rudolf Marek -Rui Saraiva -Sachin P Sant -Sam Ravnborg -Simon Kelley -Stéphane Witzmann -Stephen Hemminger -Tejun Heo -Thomas Graf -Tony Luck -Tsuneo Yoshioka -Valdis Kletnieks diff -Nru linux-2.6.23/Makefile kernel.android/Makefile --- linux-2.6.23/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -162,6 +162,7 @@ -e s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ ) +SUBARCH := arm # Cross compiling and selecting different set of gcc/bin-utils # --------------------------------------------------------------------------- @@ -183,7 +184,7 @@ # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= +CROSS_COMPILE ?= arm-eabi- # Architecture as present in compile.h UTS_MACHINE := $(ARCH) diff -Nru linux-2.6.23/arch/arm/Kconfig kernel.android/arch/arm/Kconfig --- linux-2.6.23/arch/arm/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -152,6 +152,11 @@ help This enables support for systems based on the Agilent AAEC-2000 +config ARCH_GOLDFISH + bool "Goldfish" + help + Support for Goldfish Virtual Platform. + config ARCH_INTEGRATOR bool "ARM Ltd. Integrator family" select ARM_AMBA @@ -408,6 +413,8 @@ endchoice +source "arch/arm/mach-goldfish/Kconfig" + source "arch/arm/mach-clps711x/Kconfig" source "arch/arm/mach-ep93xx/Kconfig" @@ -1055,6 +1062,8 @@ source "drivers/dma/Kconfig" +source "drivers/android/Kconfig" + endmenu source "fs/Kconfig" diff -Nru linux-2.6.23/arch/arm/Makefile kernel.android/arch/arm/Makefile --- linux-2.6.23/arch/arm/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -128,6 +128,7 @@ machine-$(CONFIG_ARCH_IMX) := imx machine-$(CONFIG_ARCH_H720X) := h720x machine-$(CONFIG_ARCH_AAEC2000) := aaec2000 + machine-$(CONFIG_ARCH_GOLDFISH) := goldfish machine-$(CONFIG_ARCH_REALVIEW) := realview machine-$(CONFIG_ARCH_AT91) := at91 machine-$(CONFIG_ARCH_EP93XX) := ep93xx diff -Nru linux-2.6.23/arch/arm/configs/goldfish_defconfig kernel.android/arch/arm/configs/goldfish_defconfig --- linux-2.6.23/arch/arm/configs/goldfish_defconfig 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/configs/goldfish_defconfig 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,1043 @@ + +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.21 +# Fri Jun 29 20:16:53 2007 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +# CONFIG_GENERIC_GPIO is not set +# CONFIG_GENERIC_TIME is not set +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_IPC_NS is not set +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_UTS_NS is not set +# CONFIG_AUDIT is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_PANIC_TIMEOUT=0 +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_SLAB=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# Block layer +# +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +CONFIG_ARCH_GOLDFISH=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# 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_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_OMAP is not set + +# +# Goldfish Options +# +CONFIG_MACH_GOLDFISH=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +# CONFIG_OUTER_CACHE is not set + +# +# Bus support +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=n +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y +# CONFIG_ARTHUR is not set + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_LEGACY=y +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_SYSFS_DEPRECATED is not set +# CONFIG_APM_EMULATION is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +# CONFIG_DECNET is not set +CONFIG_LLC=y +# 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_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +CONFIG_CONNECTOR=y +CONFIG_PROC_EVENTS=y + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +# CONFIG_MTD_PARTITIONS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# 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_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 +CONFIG_MTD_GOLDFISH_NAND=y + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# OneNAND Flash Device Drivers +# +# CONFIG_MTD_ONENAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +# CONFIG_PNPACPI is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_NETLINK is not set + +# +# Serial ATA (prod) and Parallel ATA (experimental) drivers +# +# CONFIG_ATA is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_UINPUT is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +CONFIG_HW_RANDOM=y +# CONFIG_NVRAM is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set +CONFIG_GOLDFISH_TTY=y +CONFIG_BINDER=y + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set + +# +# Misc devices +# +# CONFIG_PROC_EXMAP is not set +CONFIG_LOW_MEMORY_KILLER=y + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# +# LED devices +# +# CONFIG_NEW_LEDS is not set + +# +# LED drivers +# + +# +# LED Triggers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_GOLDFISH=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +# CONFIG_LOGO is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# HID Devices +# +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_GOLDFISH=y + +# +# Real Time Clock +# +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set + +# +# RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_V3020 is not set +CONFIG_RTC_DRV_GOLDFISH=y + +# +# Android +# +# CONFIG_ANDROID_GADGET is not set +# CONFIG_ANDROID_RAM_CONSOLE is not set +CONFIG_ANDROID_POWER=y +CONFIG_ANDROID_LOGGER=y + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +# CONFIG_JFFS2_FS 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 is not set +CONFIG_NFSD=y +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V3_ACL is not set +# CONFIG_NFSD_V4 is not set +# CONFIG_NFSD_TCP is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=y +CONFIG_NFS_COMMON=y +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 +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# 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 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Distributed Lock Manager +# +# CONFIG_DLM is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_DETECT_SOFTLOCKUP is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_LIST is not set +CONFIG_FRAME_POINTER=y +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_PCBC=y +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y + +CONFIG_QEMU_TRACE=y diff -Nru linux-2.6.23/arch/arm/kernel/entry-armv.S kernel.android/arch/arm/kernel/entry-armv.S --- linux-2.6.23/arch/arm/kernel/entry-armv.S 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/kernel/entry-armv.S 2007-11-12 07:49:02.000000000 +1100 @@ -607,6 +607,15 @@ ldr r0, =thread_notify_head mov r1, #THREAD_NOTIFY_SWITCH bl atomic_notifier_call_chain +#ifdef CONFIG_QEMU_TRACE + /* + * tell QEMU the context switch + * -- wen + */ +/* + mcr p15, 0, r0, c15, c0, 0 @ signal context switch +*/ +#endif mov r0, r5 ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously diff -Nru linux-2.6.23/arch/arm/kernel/process.c kernel.android/arch/arm/kernel/process.c --- linux-2.6.23/arch/arm/kernel/process.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/kernel/process.c 2007-11-12 07:49:02.000000000 +1100 @@ -396,6 +396,16 @@ } EXPORT_SYMBOL(dump_thread); +/* + * Capture the user space registers if the task is not running (in user space) + */ +int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) +{ + struct pt_regs ptregs = *task_pt_regs(tsk); + elf_core_copy_regs(regs, &ptregs); + return 1; +} + /* * Shuffle the argument into the correct register before calling the * thread function. r1 is the thread argument, r2 is the pointer to diff -Nru linux-2.6.23/arch/arm/kernel/signal.c kernel.android/arch/arm/kernel/signal.c --- linux-2.6.23/arch/arm/kernel/signal.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/kernel/signal.c 2007-11-12 07:49:02.000000000 +1100 @@ -534,6 +534,14 @@ static inline void restart_syscall(struct pt_regs *regs) { + if (regs->ARM_ORIG_r0 == -ERESTARTNOHAND || + regs->ARM_ORIG_r0 == -ERESTARTSYS || + regs->ARM_ORIG_r0 == -ERESTARTNOINTR || + regs->ARM_ORIG_r0 == -ERESTART_RESTARTBLOCK) { + /* the syscall cannot be safely restarted, return -EINTR instead */ + regs->ARM_r0 = -EINTR; + return; + } regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; } @@ -650,6 +658,7 @@ */ if (syscall) { if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) { + regs->ARM_r0 = -EAGAIN; /* prevent multiple restarts */ if (thumb_mode(regs)) { regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; regs->ARM_pc -= 2; diff -Nru linux-2.6.23/arch/arm/mach-goldfish/Kconfig kernel.android/arch/arm/mach-goldfish/Kconfig --- linux-2.6.23/arch/arm/mach-goldfish/Kconfig 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,11 @@ +if ARCH_GOLDFISH + +menu "Goldfish Options" + +config MACH_GOLDFISH + bool "Goldfish (Virtual Platform)" + select CPU_ARM926T + +endmenu + +endif diff -Nru linux-2.6.23/arch/arm/mach-goldfish/Makefile kernel.android/arch/arm/mach-goldfish/Makefile --- linux-2.6.23/arch/arm/mach-goldfish/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,9 @@ +# +# Makefile for the linux kernel. +# + +# Object file lists. + +obj-y := pdev_bus.o timer.o switch.o audio.o +obj-$(CONFIG_MACH_GOLDFISH) += board-goldfish.o + diff -Nru linux-2.6.23/arch/arm/mach-goldfish/Makefile.boot kernel.android/arch/arm/mach-goldfish/Makefile.boot --- linux-2.6.23/arch/arm/mach-goldfish/Makefile.boot 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/Makefile.boot 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,4 @@ + zreladdr-y := 0x00008000 +params_phys-y := 0x00000100 +initrd_phys-y := 0x00800000 + diff -Nru linux-2.6.23/arch/arm/mach-goldfish/audio.c kernel.android/arch/arm/mach-goldfish/audio.c --- linux-2.6.23/arch/arm/mach-goldfish/audio.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/audio.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,379 @@ +/* arch/arm/mach-goldfish/audio.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 +#include + + +MODULE_AUTHOR("Google, Inc."); +MODULE_DESCRIPTION("Android QEMU Audio Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +struct goldfish_audio { + uint32_t reg_base; + int irq; + spinlock_t lock; + wait_queue_head_t wait; + + char __iomem *buffer_virt; /* combined buffer virtual address */ + unsigned long buffer_phys; /* combined buffer physical address */ + + char __iomem *write_buffer1; /* write buffer 1 virtual address */ + char __iomem *write_buffer2; /* write buffer 2 virtual address */ + char __iomem *read_buffer; /* read buffer virtual address */ + int buffer_status; + int read_supported; /* true if we have audio input support */ +}; + +/* We will allocate two read buffers and two write buffers. + Having two read buffers facilitate stereo -> mono conversion. + Having two write buffers facilitate interleaved IO. +*/ +#define READ_BUFFER_SIZE 16384 +#define WRITE_BUFFER_SIZE 16384 +#define COMBINED_BUFFER_SIZE ((2 * READ_BUFFER_SIZE) + (2 * WRITE_BUFFER_SIZE)) + +#define GOLDFISH_AUDIO_READ(data, addr) (readl(data->reg_base + addr)) +#define GOLDFISH_AUDIO_WRITE(data, addr, x) (writel(x, data->reg_base + addr)) + +/* temporary variable used between goldfish_audio_probe() and goldfish_audio_open() */ +static struct goldfish_audio* audio_data; + +enum { + /* audio status register */ + AUDIO_INT_STATUS = 0x00, + /* set this to enable IRQ */ + AUDIO_INT_ENABLE = 0x04, + /* set these to specify buffer addresses */ + AUDIO_SET_WRITE_BUFFER_1 = 0x08, + AUDIO_SET_WRITE_BUFFER_2 = 0x0C, + /* set number of bytes in buffer to write */ + AUDIO_WRITE_BUFFER_1 = 0x10, + AUDIO_WRITE_BUFFER_2 = 0x14, + + /* true if audio input is supported */ + AUDIO_READ_SUPPORTED = 0x18, + /* buffer to use for audio input */ + AUDIO_SET_READ_BUFFER = 0x1C, + + /* driver writes number of bytes to read */ + AUDIO_START_READ = 0x20, + + /* number of bytes available in read buffer */ + AUDIO_READ_BUFFER_AVAILABLE = 0x24, + + /* AUDIO_INT_STATUS bits */ + + /* this bit set when it is safe to write more bytes to the buffer */ + AUDIO_INT_WRITE_BUFFER_1_EMPTY = 1U << 0, + AUDIO_INT_WRITE_BUFFER_2_EMPTY = 1U << 1, + AUDIO_INT_READ_BUFFER_FULL = 1U << 2, + + AUDIO_INT_MASK = AUDIO_INT_WRITE_BUFFER_1_EMPTY | + AUDIO_INT_WRITE_BUFFER_2_EMPTY | + AUDIO_INT_READ_BUFFER_FULL, +}; + + +static atomic_t open_count = ATOMIC_INIT(0); + + +static ssize_t goldfish_audio_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct goldfish_audio* data = fp->private_data; + int length; + int result = 0; + + if (!data->read_supported) + return -ENODEV; + + while (count > 0) { + length = (count > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : count); + GOLDFISH_AUDIO_WRITE(data, AUDIO_START_READ, length); + + wait_event_interruptible(data->wait, (data->buffer_status & AUDIO_INT_READ_BUFFER_FULL)); + + length = GOLDFISH_AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE); + + /* copy data to user space */ + if (copy_to_user(buf, data->read_buffer, length)) + { + printk("copy_from_user failed!\n"); + return -EFAULT; + } + + result += length; + buf += length; + count -= length; + } + + return result; +} + +static ssize_t goldfish_audio_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct goldfish_audio* data = fp->private_data; + unsigned long irq_flags; + ssize_t result = 0; + char __iomem *kbuf; + + while (count > 0) + { + ssize_t copy = count; + if (copy > WRITE_BUFFER_SIZE) + copy = WRITE_BUFFER_SIZE; + wait_event_interruptible(data->wait, + (data->buffer_status & (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY))); + + if ((data->buffer_status & AUDIO_INT_WRITE_BUFFER_1_EMPTY) != 0) { + kbuf = data->write_buffer1; + } else { + kbuf = data->write_buffer2; + } + + /* copy from user space to the appropriate buffer */ + if (copy_from_user(kbuf, buf, copy)) + { + printk("copy_from_user failed!\n"); + result = -EFAULT; + break; + } + else + { + spin_lock_irqsave(&data->lock, irq_flags); + + /* clear the buffer empty flag, and signal the emulator to start writing the buffer */ + if (kbuf == data->write_buffer1) { + data->buffer_status &= ~AUDIO_INT_WRITE_BUFFER_1_EMPTY; + GOLDFISH_AUDIO_WRITE(data, AUDIO_WRITE_BUFFER_1, copy); + } else { + data->buffer_status &= ~AUDIO_INT_WRITE_BUFFER_2_EMPTY; + GOLDFISH_AUDIO_WRITE(data, AUDIO_WRITE_BUFFER_2, copy); + } + + spin_unlock_irqrestore(&data->lock, irq_flags); + } + + buf += copy; + result += copy; + count -= copy; + } + + return result; +} + +static int goldfish_audio_open(struct inode *ip, struct file *fp) +{ + if (!audio_data) + return -ENODEV; + + if (atomic_inc_return(&open_count) == 1) + { + fp->private_data = audio_data; + audio_data->buffer_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY); + GOLDFISH_AUDIO_WRITE(audio_data, AUDIO_INT_ENABLE, AUDIO_INT_MASK); + return 0; + } + else + { + atomic_dec(&open_count); + return -EBUSY; + } +} + +static int goldfish_audio_release(struct inode *ip, struct file* fp) +{ + atomic_dec(&open_count); + GOLDFISH_AUDIO_WRITE(audio_data, AUDIO_INT_ENABLE, 0); + return 0; +} + +static int goldfish_audio_ioctl(struct inode* ip, struct file* fp, unsigned int cmd, unsigned long arg) +{ + /* temporary workaround, until we switch to the ALSA API */ + if (cmd == 315) + return -1; + else + return 0; +} + +static irqreturn_t +goldfish_audio_interrupt(int irq, void *dev_id) +{ + unsigned long irq_flags; + struct goldfish_audio *data = dev_id; + uint32_t status; + + spin_lock_irqsave(&data->lock, irq_flags); + + /* read buffer status flags */ + status = GOLDFISH_AUDIO_READ(data, AUDIO_INT_STATUS); + status &= AUDIO_INT_MASK; + /* if buffers are newly empty, wake up blocked goldfish_audio_write() call */ + if(status) { + data->buffer_status = status; + wake_up(&data->wait); + } + + spin_unlock_irqrestore(&data->lock, irq_flags); + return status ? IRQ_HANDLED : IRQ_NONE; +} + +/* file operations for /dev/eac */ +static struct file_operations goldfish_audio_fops = { + .owner = THIS_MODULE, + .read = goldfish_audio_read, + .write = goldfish_audio_write, + .open = goldfish_audio_open, + .release = goldfish_audio_release, + .ioctl = goldfish_audio_ioctl, + +}; + +static struct miscdevice goldfish_audio_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "eac", + .fops = &goldfish_audio_fops, +}; + +static int goldfish_audio_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct goldfish_audio *data; + dma_addr_t buf_addr; + +printk("goldfish_audio_probe\n"); + data = kzalloc(sizeof(*data), GFP_KERNEL); + if(data == NULL) { + ret = -ENOMEM; + goto err_data_alloc_failed; + } + spin_lock_init(&data->lock); + init_waitqueue_head(&data->wait); + platform_set_drvdata(pdev, data); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) { + printk("platform_get_resource failed\n"); + ret = -ENODEV; + goto err_no_io_base; + } + data->reg_base = IO_ADDRESS(r->start - IO_START); + + data->irq = platform_get_irq(pdev, 0); + if(data->irq < 0) { + printk("platform_get_irq failed\n"); + ret = -ENODEV; + goto err_no_irq; + } + + data->buffer_virt = dma_alloc_writecombine(&pdev->dev, COMBINED_BUFFER_SIZE, + &buf_addr, GFP_KERNEL); + if(data->buffer_virt == 0) { + ret = -ENOMEM; + goto err_alloc_write_buffer_failed; + } + data->buffer_phys = buf_addr; + data->write_buffer1 = data->buffer_virt; + data->write_buffer2 = data->buffer_virt + WRITE_BUFFER_SIZE; + data->read_buffer = data->buffer_virt + 2 * WRITE_BUFFER_SIZE; + + ret = request_irq(data->irq, goldfish_audio_interrupt, IRQF_SHARED, pdev->name, data); + if(ret) + goto err_request_irq_failed; + + if((ret = misc_register(&goldfish_audio_device))) + { + printk("misc_register returned %d in goldfish_audio_init\n", ret); + goto err_misc_register_failed; + } + + + GOLDFISH_AUDIO_WRITE(data, AUDIO_SET_WRITE_BUFFER_1, buf_addr); + GOLDFISH_AUDIO_WRITE(data, AUDIO_SET_WRITE_BUFFER_2, buf_addr + WRITE_BUFFER_SIZE); + + data->read_supported = GOLDFISH_AUDIO_READ(data, AUDIO_READ_SUPPORTED); + if (data->read_supported) + GOLDFISH_AUDIO_WRITE(data, AUDIO_SET_READ_BUFFER, buf_addr + 2 * WRITE_BUFFER_SIZE); + + audio_data = data; + return 0; + +err_misc_register_failed: +err_request_irq_failed: + dma_free_writecombine(&pdev->dev, COMBINED_BUFFER_SIZE, data->buffer_virt, data->buffer_phys); +err_alloc_write_buffer_failed: +err_no_irq: +err_no_io_base: + kfree(data); +err_data_alloc_failed: + return ret; +} + +static int goldfish_audio_remove(struct platform_device *pdev) +{ + struct goldfish_audio *data = platform_get_drvdata(pdev); + + misc_deregister(&goldfish_audio_device); + free_irq(data->irq, data); + dma_free_writecombine(&pdev->dev, COMBINED_BUFFER_SIZE, data->buffer_virt, data->buffer_phys); + kfree(data); + audio_data = NULL; + return 0; +} + +static struct platform_driver goldfish_audio_driver = { + .probe = goldfish_audio_probe, + .remove = goldfish_audio_remove, + .driver = { + .name = "goldfish_audio" + } +}; + +static int __init goldfish_audio_init(void) +{ + int ret; + + ret = platform_driver_register(&goldfish_audio_driver); + if (ret < 0) + { + printk("platform_driver_register returned %d\n", ret); + return ret; + } + + return ret; +} + +static void __exit goldfish_audio_exit(void) +{ + platform_driver_unregister(&goldfish_audio_driver); +} + +module_init(goldfish_audio_init); +module_exit(goldfish_audio_exit); diff -Nru linux-2.6.23/arch/arm/mach-goldfish/board-goldfish.c kernel.android/arch/arm/mach-goldfish/board-goldfish.c --- linux-2.6.23/arch/arm/mach-goldfish/board-goldfish.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/board-goldfish.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,122 @@ +/* arch/arm/mach-goldfish/board-goldfish.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +int GOLDFISH_READY = 0; + +static struct resource goldfish_pdev_bus_resources[] = { + { + .start = GOLDFISH_PDEV_BUS_BASE, + .end = GOLDFISH_PDEV_BUS_BASE + GOLDFISH_PDEV_BUS_END - 1, + .flags = IORESOURCE_IO, + }, + { + .start = IRQ_PDEV_BUS, + .end = IRQ_PDEV_BUS, + .flags = IORESOURCE_IRQ, + } +}; + + +struct platform_device goldfish_pdev_bus_device = { + .name = "goldfish_pdev_bus", + .id = -1, + .num_resources = ARRAY_SIZE(goldfish_pdev_bus_resources), + .resource = goldfish_pdev_bus_resources +}; + +static void __init goldfish_init(void) +{ + platform_device_register(&goldfish_pdev_bus_device); +} + +void goldfish_mask_irq(unsigned int irq) +{ + writel(irq, IO_ADDRESS(GOLDFISH_INTERRUPT_BASE) + GOLDFISH_INTERRUPT_DISABLE); +} + +void goldfish_unmask_irq(unsigned int irq) +{ + writel(irq, IO_ADDRESS(GOLDFISH_INTERRUPT_BASE) + GOLDFISH_INTERRUPT_ENABLE); +} + +static struct irq_chip goldfish_irq_chip = { + .name = "goldfish", + .mask = goldfish_mask_irq, + .mask_ack = goldfish_mask_irq, + .unmask = goldfish_unmask_irq, +}; + +void goldfish_init_irq(void) +{ + unsigned int i; + uint32_t int_base = IO_ADDRESS(GOLDFISH_INTERRUPT_BASE); + + /* + * Disable all interrupt sources + */ + writel(1, int_base + GOLDFISH_INTERRUPT_DISABLE_ALL); + + for (i = 0; i < NR_IRQS; i++) { + set_irq_chip(i, &goldfish_irq_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } +} + +static struct map_desc goldfish_io_desc[] __initdata = { + { + .virtual = IO_BASE, + .pfn = __phys_to_pfn(IO_START), + .length = IO_SIZE, + .type = MT_DEVICE + }, +}; + +static void __init goldfish_map_io(void) +{ + iotable_init(goldfish_io_desc, ARRAY_SIZE(goldfish_io_desc)); + GOLDFISH_READY = 1; +} + +extern struct sys_timer goldfish_timer; + +MACHINE_START(GOLDFISH, "Goldfish") + .phys_io = 0xff000000, + .io_pg_offst = ((0xfe000000) >> 18) & 0xfffc, + .boot_params = 0x00000100, + .map_io = goldfish_map_io, + .init_irq = goldfish_init_irq, + .init_machine = goldfish_init, + .timer = &goldfish_timer, +MACHINE_END diff -Nru linux-2.6.23/arch/arm/mach-goldfish/pdev_bus.c kernel.android/arch/arm/mach-goldfish/pdev_bus.c --- linux-2.6.23/arch/arm/mach-goldfish/pdev_bus.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/pdev_bus.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,222 @@ +/* arch/arm/mach-goldfish/pdev_bus.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 PDEV_BUS_OP_DONE (0x00) +#define PDEV_BUS_OP_REMOVE_DEV (0x04) +#define PDEV_BUS_OP_ADD_DEV (0x08) + +#define PDEV_BUS_OP_INIT (0x00) + +#define PDEV_BUS_OP (0x00) +#define PDEV_BUS_GET_NAME (0x04) +#define PDEV_BUS_NAME_LEN (0x08) +#define PDEV_BUS_ID (0x0c) +#define PDEV_BUS_IO_BASE (0x10) +#define PDEV_BUS_IO_SIZE (0x14) +#define PDEV_BUS_IRQ (0x18) +#define PDEV_BUS_IRQ_COUNT (0x1c) + +struct pdev_bus_dev { + struct list_head list; + struct platform_device pdev; + struct resource resources[0]; +}; + +static void goldfish_pdev_worker(struct work_struct *work); + +static uint32_t pdev_bus_base; +static uint32_t pdev_bus_irq; +static LIST_HEAD(pdev_bus_new_devices); +static LIST_HEAD(pdev_bus_registered_devices); +static LIST_HEAD(pdev_bus_removed_devices); +static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker); + + +static void goldfish_pdev_worker(struct work_struct *work) +{ + int ret; + struct pdev_bus_dev *pos, *n; + + list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) { + list_del(&pos->list); + platform_device_unregister(&pos->pdev); + kfree(pos); + } + list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { + list_del(&pos->list); + ret = platform_device_register(&pos->pdev); + if(ret) { + printk("goldfish_pdev_worker failed to register device, %s\n", pos->pdev.name); + } + else { + printk("goldfish_pdev_worker registered %s\n", pos->pdev.name); + } + list_add(&pos->list, &pdev_bus_registered_devices); + } +} + +static void goldfish_pdev_remove(void) +{ + struct pdev_bus_dev *pos, *n; + uint32_t base; + + base = readl(pdev_bus_base + PDEV_BUS_IO_BASE); + + list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { + if(pos->resources[0].start == base) { + list_del(&pos->list); + kfree(pos); + return; + } + } + list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) { + if(pos->resources[0].start == base) { + list_del(&pos->list); + list_add(&pos->list, &pdev_bus_removed_devices); + schedule_work(&pdev_bus_worker); + return; + } + }; + printk("goldfish_pdev_remove could not find device at %x\n", base); +} + +static int goldfish_new_pdev(void) +{ + struct pdev_bus_dev *dev; + uint32_t name_len; + uint32_t irq = -1, irq_count; + int resource_count = 2; + uint32_t base; + char *name; + + base = readl(pdev_bus_base + PDEV_BUS_IO_BASE); + + irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT); + name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN); + if(irq_count) + resource_count++; + + dev = kzalloc(sizeof(*dev) + sizeof(struct resource) * resource_count + name_len + 1, GFP_ATOMIC); + if(dev == NULL) + return -ENOMEM; + + dev->pdev.num_resources = resource_count; + dev->pdev.resource = (struct resource *)(dev + 1); + dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count); + dev->pdev.dev.coherent_dma_mask = ~0; + + writel(name, pdev_bus_base + PDEV_BUS_GET_NAME); + name[name_len] = '\0'; + dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID); + dev->pdev.resource[0].start = base; + dev->pdev.resource[0].end = base + readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1; + dev->pdev.resource[0].flags = IORESOURCE_MEM; + if(irq_count) { + irq = readl(pdev_bus_base + PDEV_BUS_IRQ); + dev->pdev.resource[1].start = irq; + dev->pdev.resource[1].end = irq + irq_count - 1; + dev->pdev.resource[1].flags = IORESOURCE_IRQ; + } + + printk("goldfish_new_pdev %s at %x irq %d\n", name, base, irq); + list_add(&dev->list, &pdev_bus_new_devices); + schedule_work(&pdev_bus_worker); + + return 0; +} + +static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + while(1) { + uint32_t op = readl(pdev_bus_base + PDEV_BUS_OP); + switch(op) { + case PDEV_BUS_OP_DONE: + return IRQ_NONE; + + case PDEV_BUS_OP_REMOVE_DEV: + goldfish_pdev_remove(); + break; + + case PDEV_BUS_OP_ADD_DEV: + goldfish_new_pdev(); + break; + } + ret = IRQ_HANDLED; + } +} + +static int __devinit goldfish_pdev_bus_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + r = platform_get_resource(pdev, IORESOURCE_IO, 0); + if(r == NULL) + return -EINVAL; + pdev_bus_base = IO_ADDRESS(r->start); + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if(r == NULL) + return -EINVAL; + pdev_bus_irq = r->start; + + ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt, IRQF_SHARED, "goldfish_pdev_bus", pdev); + if(ret) + goto err_request_irq_failed; + + writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP); + +err_request_irq_failed: + return ret; +} + +static int __devexit goldfish_pdev_bus_remove(struct platform_device *pdev) +{ + free_irq(pdev_bus_irq, pdev); + return 0; +} + +static struct platform_driver goldfish_pdev_bus = { + .probe = goldfish_pdev_bus_probe, + .remove = goldfish_pdev_bus_remove, + .driver = { + .name = "goldfish_pdev_bus" + } +}; + +static int __init goldfish_pdev_bus_init(void) +{ + return platform_driver_register(&goldfish_pdev_bus); +} + +static void goldfish_pdev_bus_exit(void) +{ + platform_driver_unregister(&goldfish_pdev_bus); +} + +module_init(goldfish_pdev_bus_init); +module_exit(goldfish_pdev_bus_exit); + diff -Nru linux-2.6.23/arch/arm/mach-goldfish/switch.c kernel.android/arch/arm/mach-goldfish/switch.c --- linux-2.6.23/arch/arm/mach-goldfish/switch.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/switch.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,218 @@ +/* arch/arm/mach-goldfish/timer.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +enum { + SW_NAME_LEN = 0x00, + SW_NAME_PTR = 0x04, + SW_FLAGS = 0x08, + SW_STATE = 0x0c, + SW_INT_STATUS = 0x10, + SW_INT_ENABLE = 0x14, + + SW_FLAGS_OUTPUT = 1U << 0 +}; + +static struct class *goldfish_switch_class; + +struct goldfish_switch { + uint32_t base; + int irq; + uint32_t state; + uint32_t flags; + struct class_device *cdev; + struct work_struct work; + char name[0]; +}; + +static irqreturn_t +goldfish_switch_interrupt(int irq, void *dev_id) +{ + struct goldfish_switch *qs = dev_id; + uint32_t status; + + status = readl(qs->base + SW_INT_STATUS); + if(status) { + qs->state = readl(qs->base + SW_STATE); + schedule_work(&qs->work); + } + + return status ? IRQ_HANDLED : IRQ_NONE; +} + +static ssize_t goldfish_switch_state_store(struct class_device *dev, + const char *buf, + size_t count) +{ + struct goldfish_switch *qs = class_get_devdata(dev); + uint32_t state; + + if (!(qs->flags & SW_FLAGS_OUTPUT)) + return -EPERM; + + if (sscanf(buf, "%d", &state) != 1) + return -EINVAL; + + writel(state, qs->base + SW_STATE); + qs->state = readl(qs->base + SW_STATE); + if(state != qs->state) + return -EINVAL; + + return count; +} + +static ssize_t goldfish_switch_state_show(struct class_device *dev, char *buf) +{ + struct goldfish_switch *qs = class_get_devdata(dev); + return sprintf(buf, "%d\n", qs->state); +} + +static ssize_t goldfish_switch_direction_show(struct class_device *dev, char *buf) +{ + struct goldfish_switch *qs = class_get_devdata(dev); + return sprintf(buf, "%s\n", (qs->flags & SW_FLAGS_OUTPUT) ? "output" : "input"); +} + + +static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, goldfish_switch_state_show, goldfish_switch_state_store); +static CLASS_DEVICE_ATTR(direction, S_IRUGO, goldfish_switch_direction_show, NULL); + +void goldfish_switch_work(struct work_struct *work) +{ + struct goldfish_switch *qs = container_of(work, struct goldfish_switch, work); + int ret; + ret = sysfs_update_file(&qs->cdev->kobj, &class_device_attr_state.attr); +} + +static int __devinit goldfish_switch_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct goldfish_switch *qs; + uint32_t base; + uint32_t name_len; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) { + ret = -ENODEV; + goto err_no_io_base; + } + base = IO_ADDRESS(r->start - IO_START); + name_len = readl(base + SW_NAME_LEN); + + qs = kzalloc(sizeof(*qs) + name_len + 1, GFP_KERNEL); + if(qs == NULL) { + ret = -ENOMEM; + goto err_qs_alloc_failed; + } + platform_set_drvdata(pdev, qs); + qs->base = base; + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if(r == NULL) { + ret = -ENODEV; + goto err_no_irq; + } + qs->irq = r->start; + + writel(qs->name, base + SW_NAME_PTR); + qs->name[name_len] = '\0'; + writel(0, base + SW_INT_ENABLE); + + qs->flags = readl(base + SW_FLAGS); + qs->state = readl(base + SW_STATE); + INIT_WORK(&qs->work, goldfish_switch_work); + + qs->cdev = class_device_create(goldfish_switch_class, NULL, 0, + &pdev->dev, "%s", qs->name); + if(unlikely(IS_ERR(qs->cdev))) { + ret = PTR_ERR(qs->cdev); + goto err_class_device_create_failed; + } + class_set_devdata(qs->cdev, qs); + + ret = class_device_create_file(qs->cdev, &class_device_attr_state); + if(ret) + goto err_class_device_create_file_failed; + + ret = class_device_create_file(qs->cdev, &class_device_attr_direction); + if(ret) + goto err_class_device_create_file_failed; + + ret = request_irq(qs->irq, goldfish_switch_interrupt, IRQF_SHARED, "goldfish_switch", qs); + if(ret) + goto err_request_irq_failed; + writel(1, base + SW_INT_ENABLE); + + return 0; + + +// free_irq(qs->irq, qs); +err_request_irq_failed: +err_class_device_create_file_failed: + class_device_unregister(qs->cdev); +err_class_device_create_failed: +err_no_irq: + kfree(qs); +err_qs_alloc_failed: +err_no_io_base: + printk("goldfish_switch_probe failed %d\n", ret); + return ret; +} + +static int __devexit goldfish_switch_remove(struct platform_device *pdev) +{ + struct goldfish_switch *qs = platform_get_drvdata(pdev); + writel(0, qs->base + SW_INT_ENABLE); + free_irq(qs->irq, qs); + class_device_unregister(qs->cdev); + kfree(qs); + return 0; +} + +static struct platform_driver goldfish_switch_driver = { + .probe = goldfish_switch_probe, + .remove = goldfish_switch_remove, + .driver = { + .name = "goldfish-switch" + } +}; + +static int __init goldfish_switch_init(void) +{ + goldfish_switch_class = class_create(THIS_MODULE, "switch"); + if (IS_ERR(goldfish_switch_class)) + return PTR_ERR(goldfish_switch_class); + return platform_driver_register(&goldfish_switch_driver); +} + +static void goldfish_switch_exit(void) +{ + platform_driver_unregister(&goldfish_switch_driver); + class_destroy(goldfish_switch_class); +} + +module_init(goldfish_switch_init); +module_exit(goldfish_switch_exit); + diff -Nru linux-2.6.23/arch/arm/mach-goldfish/timer.c kernel.android/arch/arm/mach-goldfish/timer.c --- linux-2.6.23/arch/arm/mach-goldfish/timer.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/arch/arm/mach-goldfish/timer.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,89 @@ +/* arch/arm/mach-goldfish/timer.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +static int64_t goldfish_timer_offset; + +static irqreturn_t +goldfish_timer_interrupt(int irq, void *dev_id) +{ + uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE); + uint64_t next_tick; + write_seqlock(&xtime_lock); + + /* + * clear the interrupt + */ + writel(1, timer_base + TIMER_CLEAR_INTERRUPT); + + timer_tick(); + + next_tick = (jiffies_64 + 1) * (1000000000 / HZ); + next_tick += goldfish_timer_offset; + writel(next_tick >> 32, timer_base + TIMER_ALARM_HIGH); + writel(next_tick, timer_base + TIMER_ALARM_LOW); + + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + + +static struct irqaction goldfish_timer_irq = { + .name = "Goldfish Timer Tick", + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = goldfish_timer_interrupt, +}; + +static void __init goldfish_timer_init(void) +{ + uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE); + + goldfish_timer_offset = readl(timer_base + TIMER_TIME_LOW); + goldfish_timer_offset |= (int64_t)readl(timer_base + TIMER_TIME_HIGH) << 32; + goldfish_timer_offset -= get_jiffies_64() * (NSEC_PER_SEC / HZ); + setup_irq(IRQ_TIMER, &goldfish_timer_irq); + writel(0, timer_base + TIMER_ALARM_HIGH); + writel(0, timer_base + TIMER_ALARM_LOW); +} + +unsigned long goldfish_gettimeoffset(void) +{ + int64_t dt; + uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE); + + dt = readl(timer_base + TIMER_TIME_LOW); + dt |= (int64_t)readl(timer_base + TIMER_TIME_HIGH) << 32; + dt -= goldfish_timer_offset; + //dt -= get_jiffies_64() * (1000000000 / HZ); // we may get called with xtime_lock held + dt -= jiffies_64 * (NSEC_PER_SEC / HZ); + do_div(dt, NSEC_PER_USEC); + return dt; +} + +struct sys_timer goldfish_timer = { + .init = goldfish_timer_init, + .offset = goldfish_gettimeoffset, +}; + diff -Nru linux-2.6.23/arch/arm/mm/Kconfig kernel.android/arch/arm/mm/Kconfig --- linux-2.6.23/arch/arm/mm/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/mm/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -171,7 +171,7 @@ # ARM926T config CPU_ARM926T bool "Support ARM926T processor" - depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_NS9XXX || ARCH_DAVINCI + depends on ARCH_GOLDFISH || ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_NS9XXX || ARCH_DAVINCI default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_NS9XXX || ARCH_DAVINCI select CPU_32v5 select CPU_ABRT_EV5TJ diff -Nru linux-2.6.23/arch/arm/tools/mach-types kernel.android/arch/arm/tools/mach-types --- linux-2.6.23/arch/arm/tools/mach-types 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/arch/arm/tools/mach-types 2007-11-12 07:49:02.000000000 +1100 @@ -1367,3 +1367,7 @@ csb726 MACH_CSB726 CSB726 1359 tik27 MACH_TIK27 TIK27 1360 mx_uc7420 MACH_MX_UC7420 MX_UC7420 1361 +sardine MACH_SARDINE SARDINE 1438 +halibut MACH_HALIBUT HALIBUT 1439 +trout MACH_TROUT TROUT 1440 +goldfish MACH_GOLDFISH GOLDFISH 1441 diff -Nru linux-2.6.23/drivers/Kconfig kernel.android/drivers/Kconfig --- linux-2.6.23/drivers/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -89,4 +89,7 @@ source "drivers/uio/Kconfig" source "drivers/lguest/Kconfig" + +source "drivers/android/Kconfig" + endmenu diff -Nru linux-2.6.23/drivers/Makefile kernel.android/drivers/Makefile --- linux-2.6.23/drivers/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -88,3 +88,5 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ +obj-y += android/ +obj-$(CONFIG_BINDER) += binder/ diff -Nru linux-2.6.23/drivers/android/Kconfig kernel.android/drivers/android/Kconfig --- linux-2.6.23/drivers/android/Kconfig 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,60 @@ +menu "Android" + +config ANDROID_GADGET + tristate "New gadget based Android USB Driver" + default n + help + none + +config ANDROID_KDBG + bool "Android low level (usb) debug interface" + depends on ANDROID_GADGET + default n + help + none + +config ANDROID_RAM_CONSOLE + bool "RAM buffer console" + default n + help + none + +config ANDROID_RAM_CONSOLE_ENABLE_VERBOSE + bool "Enable verbose console messages" + default y + depends on ANDROID_RAM_CONSOLE + +config ANDROID_RAM_CONSOLE_EARLY_INIT + bool "Start ram console early" + default n + depends on ANDROID_RAM_CONSOLE + +config ANDROID_RAM_CONSOLE_EARLY_ADDR + hex "RAM console virtual address" + default 0 + depends on ANDROID_RAM_CONSOLE_EARLY_INIT + +config ANDROID_RAM_CONSOLE_EARLY_SIZE + hex "RAM console buffer size" + default 0 + depends on ANDROID_RAM_CONSOLE_EARLY_INIT + +config ANDROID_POWER + bool "Android power driver" + depends on PM && RTC_CLASS + default n + help + none + +config ANDROID_POWER_STAT + bool "Android power driver lock stats" + depends on ANDROID_POWER + default y + help + none + +config ANDROID_LOGGER + bool "Android log driver" + default y + +endmenu diff -Nru linux-2.6.23/drivers/android/Makefile kernel.android/drivers/android/Makefile --- linux-2.6.23/drivers/android/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,6 @@ +obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o +obj-$(CONFIG_ANDROID_GADGET) += android_gadget.o +obj-$(CONFIG_ANDROID_KDBG) += android_kernel_debug.o +obj-$(CONFIG_ANDROID_POWER) += power.o +obj-$(CONFIG_ANDROID_POWER) += alarm.o +obj-$(CONFIG_ANDROID_LOGGER) += logger.o diff -Nru linux-2.6.23/drivers/android/alarm.c kernel.android/drivers/android/alarm.c --- linux-2.6.23/drivers/android/alarm.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/alarm.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,523 @@ +/* drivers/android/alarm.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 ANDROID_ALARM_PRINT_ERRORS (1U << 0) +#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) +#define ANDROID_ALARM_PRINT_INFO (1U << 2) +#define ANDROID_ALARM_PRINT_IO (1U << 3) +#define ANDROID_ALARM_PRINT_INT (1U << 4) +#define ANDROID_ALARM_PRINT_FLOW (1U << 5) + +#if 0 +#define ANDROID_ALARM_DPRINTF_MASK (~0) +#define ANDROID_ALARM_DPRINTF(debug_level_mask, args...) \ + do { \ + if(ANDROID_ALARM_DPRINTF_MASK & debug_level_mask) { \ + printk(args); \ + } \ + } while(0) +#else +#define ANDROID_ALARM_DPRINTF(args...) +#endif + +// support old usespace code +#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) // set alarm +#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) + +static struct rtc_device *alarm_rtc_dev; +static int alarm_opened; +static DEFINE_SPINLOCK(alarm_slock); +static DEFINE_MUTEX(alarm_setrtc_mutex); +static android_suspend_lock_t alarm_suspend_lock = { + .name = "android_alarm" +}; +static android_suspend_lock_t alarm_rtc_suspend_lock = { + .name = "android_alarm_rtc" +}; +static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); +static uint32_t alarm_pending; +static uint32_t alarm_enabled; +static uint32_t wait_pending; +static struct platform_device *alarm_platform_dev; +static struct hrtimer alarm_timer[ANDROID_ALARM_TYPE_COUNT]; +static struct timespec alarm_time[ANDROID_ALARM_TYPE_COUNT]; +static struct timespec elapsed_rtc_delta; + +static void alarm_start_hrtimer(android_alarm_type_t alarm_type) +{ + struct timespec hr_alarm_time; + if(!(alarm_enabled & (1U << alarm_type))) + return; + hr_alarm_time = alarm_time[alarm_type]; + if(alarm_type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP || alarm_type == ANDROID_ALARM_ELAPSED_REALTIME) + set_normalized_timespec(&hr_alarm_time, hr_alarm_time.tv_sec + elapsed_rtc_delta.tv_sec, + hr_alarm_time.tv_nsec + elapsed_rtc_delta.tv_nsec); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, "alarm start hrtimer %d at %ld.%09ld\n", alarm_type, hr_alarm_time.tv_sec, hr_alarm_time.tv_nsec); + hrtimer_start(&alarm_timer[alarm_type], timespec_to_ktime(hr_alarm_time), HRTIMER_MODE_ABS); +} + +static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rv = 0; + unsigned long flags; + struct timespec new_alarm_time; + struct timespec new_rtc_time; + struct timespec tmp_time; + struct rtc_time rtc_new_rtc_time; + android_alarm_type_t alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); + uint32_t alarm_type_mask = 1U << alarm_type; + + if(alarm_type >= ANDROID_ALARM_TYPE_COUNT) + return -EINVAL; + + if(ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + if(file->private_data == NULL && cmd != ANDROID_ALARM_SET_RTC) { + spin_lock_irqsave(&alarm_slock, flags); + if(alarm_opened) { + spin_unlock_irqrestore(&alarm_slock, flags); + return -EBUSY; + } + alarm_opened = 1; + file->private_data = (void *)1; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + + switch(ANDROID_ALARM_BASE_CMD(cmd)) { + //case ANDROID_ALARM_CLEAR_OLD: // same as ANDROID_ALARM_CLEAR(0) + case ANDROID_ALARM_CLEAR(0): + spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm %d clear\n", alarm_type); + hrtimer_cancel(&alarm_timer[alarm_type]); + if(alarm_pending) { + alarm_pending &= ~alarm_type_mask; + if(!alarm_pending && !wait_pending) { + android_unlock_suspend(&alarm_suspend_lock); + } + } + alarm_enabled &= ~alarm_type_mask; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + + case ANDROID_ALARM_SET_OLD: + case ANDROID_ALARM_SET_AND_WAIT_OLD: + if(get_user(new_alarm_time.tv_sec, (int __user *)arg)) { + rv = -EFAULT; + goto err1; + } + new_alarm_time.tv_nsec = 0; + goto from_old_alarm_set; + + case ANDROID_ALARM_SET_AND_WAIT(0): + case ANDROID_ALARM_SET(0): + if(copy_from_user(&new_alarm_time, (void __user *)arg, sizeof(new_alarm_time))) { + rv = -EFAULT; + goto err1; + } +from_old_alarm_set: + spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm %d set %ld.%09ld\n", alarm_type, new_alarm_time.tv_sec, new_alarm_time.tv_nsec); + alarm_time[alarm_type] = new_alarm_time; + alarm_enabled |= alarm_type_mask; + alarm_start_hrtimer(alarm_type); + spin_unlock_irqrestore(&alarm_slock, flags); + if(ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) + break; + // fall though + case ANDROID_ALARM_WAIT: + spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm wait\n"); + if(!alarm_pending && wait_pending) { + android_unlock_suspend(&alarm_suspend_lock); + wait_pending = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); + if(rv) + goto err1; + spin_lock_irqsave(&alarm_slock, flags); + rv = alarm_pending; + wait_pending = 1; + alarm_pending = 0; + if(rv & (ANDROID_ALARM_RTC_WAKEUP_MASK | ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)) { + android_unlock_suspend(&alarm_rtc_suspend_lock); + } + spin_unlock_irqrestore(&alarm_slock, flags); + break; + case ANDROID_ALARM_SET_RTC: + if(copy_from_user(&new_rtc_time, (void __user *)arg, sizeof(new_rtc_time))) { + rv = -EFAULT; + goto err1; + } + rtc_time_to_tm(new_rtc_time.tv_sec, &rtc_new_rtc_time); + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, + "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", + new_rtc_time.tv_sec, new_rtc_time.tv_nsec, + rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, + rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, + rtc_new_rtc_time.tm_mday, rtc_new_rtc_time.tm_year + 1900); + + mutex_lock(&alarm_setrtc_mutex); + spin_lock_irqsave(&alarm_slock, flags); + getnstimeofday(&tmp_time); + elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, timespec_sub(tmp_time, new_rtc_time)); + rv = do_settimeofday(&new_rtc_time); + if(rv >= 0) { + alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); // restart with new offset + alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME); + } + spin_unlock_irqrestore(&alarm_slock, flags); + if(rv < 0) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, "Failed to set time\n"); + mutex_unlock(&alarm_setrtc_mutex); + goto err1; + } + rv = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); + spin_lock_irqsave(&alarm_slock, flags); + alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; + wake_up(&alarm_wait_queue); + spin_unlock_irqrestore(&alarm_slock, flags); + mutex_unlock(&alarm_setrtc_mutex); + if(rv < 0) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, "Failed to set RTC, time will be lost on reboot\n"); + goto err1; + } + break; + case ANDROID_ALARM_GET_TIME(0): + spin_lock_irqsave(&alarm_slock, flags); + if(alarm_type != ANDROID_ALARM_SYSTEMTIME) { + getnstimeofday(&tmp_time); + if(alarm_type >= ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP) { + tmp_time = timespec_sub(tmp_time, elapsed_rtc_delta); + } + } + else + ktime_get_ts(&tmp_time); + spin_unlock_irqrestore(&alarm_slock, flags); + if(copy_to_user((void __user *)arg, &tmp_time, sizeof(tmp_time))) { + rv = -EFAULT; + goto err1; + } + break; + + default: + rv = -EINVAL; + goto err1; + } +err1: + return rv; +} + +static int alarm_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int alarm_release(struct inode *inode, struct file *file) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + if(file->private_data != 0) { + for(i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { + uint32_t alarm_type_mask = 1U << i; + if(alarm_enabled & alarm_type_mask) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm_release: clear alarm, pending %d\n", !!(alarm_pending & alarm_type_mask)); + hrtimer_cancel(&alarm_timer[i]); + alarm_enabled &= ~alarm_type_mask; + } + } + if(alarm_pending | wait_pending) { + if(alarm_pending) + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm_release: clear pending alarms %x\n", alarm_pending); + android_unlock_suspend(&alarm_suspend_lock); + wait_pending = 0; + alarm_pending = 0; + } + alarm_opened = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +{ + unsigned long flags; + android_alarm_type_t alarm_type = (timer - alarm_timer); + uint32_t alarm_type_mask = 1U << alarm_type; + + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, "alarm_timer_triggered type %d\n", alarm_type); + spin_lock_irqsave(&alarm_slock, flags); + android_lock_suspend_auto_expire(&alarm_suspend_lock, 5 * HZ); + alarm_enabled &= ~alarm_type_mask; + alarm_pending |= alarm_type_mask; + wake_up(&alarm_wait_queue); + spin_unlock_irqrestore(&alarm_slock, flags); + return HRTIMER_NORESTART; +} + +static void alarm_triggered_func(void *p) +{ +// unsigned long flags; + + struct rtc_device *rtc = alarm_rtc_dev; + if(rtc->irq_data & RTC_AF) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, "alarm_triggered_func:\n"); + android_lock_suspend_auto_expire(&alarm_rtc_suspend_lock, 1 * HZ); + } +} + +int alarm_suspend(struct platform_device *pdev, pm_message_t state) +{ + int err = 0; + unsigned long flags; + struct rtc_wkalrm rtc_alarm; + struct rtc_time rtc_current_rtc_time; + unsigned long rtc_current_time; + unsigned long rtc_alarm_time; + struct timespec rtc_current_timespec; + struct timespec rtc_delta; + struct timespec elapsed_realtime_alarm_time; + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, "alarm_suspend(%p, %d)\n", pdev, state.event); + spin_lock_irqsave(&alarm_slock, flags); + if(alarm_pending && (alarm_suspend_lock.flags & ANDROID_SUSPEND_LOCK_AUTO_EXPIRE)) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm pending\n"); + err = -EBUSY; + goto err1; + } + if(alarm_enabled & (ANDROID_ALARM_RTC_WAKEUP_MASK | ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)) { + if(alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) + hrtimer_cancel(&alarm_timer[ANDROID_ALARM_RTC_WAKEUP]); + if(alarm_enabled & ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + hrtimer_cancel(&alarm_timer[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]); + spin_unlock_irqrestore(&alarm_slock, flags); + + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + rtc_current_timespec.tv_nsec = 0; + rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_timespec.tv_sec); + save_time_delta(&rtc_delta, &rtc_current_timespec); + set_normalized_timespec(&elapsed_realtime_alarm_time, + alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].tv_sec + elapsed_rtc_delta.tv_sec, + alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].tv_nsec + elapsed_rtc_delta.tv_nsec); + if((alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) && + (!(alarm_enabled & ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + || timespec_compare(&alarm_time[ANDROID_ALARM_RTC_WAKEUP], &elapsed_realtime_alarm_time) < 0)) + rtc_alarm_time = timespec_sub(alarm_time[ANDROID_ALARM_RTC_WAKEUP], rtc_delta).tv_sec; + else { + rtc_alarm_time = timespec_sub(elapsed_realtime_alarm_time, rtc_delta).tv_sec; + } + rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time); + rtc_alarm.enabled = 1; + rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n", + rtc_alarm_time, rtc_current_time, + rtc_delta.tv_sec, rtc_delta.tv_nsec); + if(rtc_current_time + 1 >= rtc_alarm_time) { + //spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm about to go off\n"); + memset(&rtc_alarm, 0, sizeof(rtc_alarm)); + rtc_alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); + + spin_lock_irqsave(&alarm_slock, flags); + android_lock_suspend_auto_expire(&alarm_rtc_suspend_lock, 2 * HZ); // trigger a wakeup + alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); + alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + err = -EBUSY; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + else { +err1: + spin_unlock_irqrestore(&alarm_slock, flags); + } + return err; +} + +int alarm_resume(struct platform_device *pdev) +{ + struct rtc_wkalrm alarm; + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, "alarm_resume(%p)\n", pdev); + if(alarm_enabled & (ANDROID_ALARM_RTC_WAKEUP_MASK | ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)) { + memset(&alarm, 0, sizeof(alarm)); + alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &alarm); + alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); + alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + } + return 0; +} + +static struct rtc_task alarm_rtc_task = { + .func = alarm_triggered_func +}; + +static struct file_operations alarm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = alarm_ioctl, + .open = alarm_open, + .release = alarm_release, +}; + +static struct miscdevice alarm_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "alarm", + .fops = &alarm_fops, +}; + +static int __devinit rtc_alarm_add_device(struct device *dev, + struct class_interface *class_intf) +{ + int err; + struct rtc_device *rtc = to_rtc_device(dev); + err = misc_register(&alarm_device); + if(err) + goto err1; + alarm_platform_dev = platform_device_register_simple("alarm", -1, NULL, 0); + if(IS_ERR(alarm_platform_dev)) { + err = PTR_ERR(alarm_platform_dev); + goto err2; + } + err = rtc_irq_register(rtc, &alarm_rtc_task); + if(err) + goto err3; + alarm_rtc_dev = rtc; + + //device_pm_set_parent(&alarm_platform_dev->dev, dev); // currently useless, drivers are suspended in reverse creation order + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm: parent %p\n", alarm_platform_dev->dev.power.pm_parent); + return 0; + +err3: + platform_device_unregister(alarm_platform_dev); +err2: + misc_deregister(&alarm_device); +err1: + return err; +} + +static void rtc_alarm_remove_device(struct device *dev, + struct class_interface *class_intf) +{ + if(dev == &alarm_rtc_dev->dev) { + rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); + platform_device_unregister(alarm_platform_dev); + misc_deregister(&alarm_device); + alarm_rtc_dev = NULL; + } +} + +static struct class_interface rtc_alarm_interface = { + .add_dev = &rtc_alarm_add_device, + .remove_dev = &rtc_alarm_remove_device, +}; + +static struct platform_driver alarm_driver = { + .suspend = alarm_suspend, + .resume = alarm_resume, + .driver = { + .name = "alarm" + } +}; + +static int __init alarm_late_init(void) +{ + unsigned long flags; + struct timespec system_time; + + // this needs to run after the rtc is read at boot + spin_lock_irqsave(&alarm_slock, flags); + // We read the current rtc and system time so we can later calulate + // elasped realtime to be (boot_systemtime + rtc - boot_rtc) == + // (rtc - (boot_rtc - boot_systemtime)) + getnstimeofday(&elapsed_rtc_delta); + ktime_get_ts(&system_time); + elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, system_time); + spin_unlock_irqrestore(&alarm_slock, flags); + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "alarm_late_init: rtc to elapsed realtime delta %ld.%09ld\n", + elapsed_rtc_delta.tv_sec, elapsed_rtc_delta.tv_nsec); + return 0; +} + +static int __init alarm_init(void) +{ + int err; + int i; + + for(i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_init(&alarm_timer[i], CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarm_timer[i].function = alarm_timer_triggered; + } + hrtimer_init(&alarm_timer[ANDROID_ALARM_SYSTEMTIME], CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + alarm_timer[ANDROID_ALARM_SYSTEMTIME].function = alarm_timer_triggered; + err = platform_driver_register(&alarm_driver); + if(err < 0) + goto err1; + err = android_init_suspend_lock(&alarm_suspend_lock); + if(err < 0) + goto err2; + err = android_init_suspend_lock(&alarm_rtc_suspend_lock); + if(err < 0) + goto err3; + rtc_alarm_interface.class = rtc_class; + err = class_interface_register(&rtc_alarm_interface); + if(err < 0) + goto err4; + + return 0; + +err4: + android_uninit_suspend_lock(&alarm_rtc_suspend_lock); +err3: + android_uninit_suspend_lock(&alarm_suspend_lock); +err2: + platform_driver_unregister(&alarm_driver); +err1: + return err; +} + +static void __exit alarm_exit(void) +{ + class_interface_unregister(&rtc_alarm_interface); + android_uninit_suspend_lock(&alarm_rtc_suspend_lock); + android_uninit_suspend_lock(&alarm_suspend_lock); + platform_driver_unregister(&alarm_driver); +} + +late_initcall(alarm_late_init); +module_init(alarm_init); +module_exit(alarm_exit); + diff -Nru linux-2.6.23/drivers/android/android_gadget.c kernel.android/drivers/android/android_gadget.c --- linux-2.6.23/drivers/android/android_gadget.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/android_gadget.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,2022 @@ +/* drivers/android/android_gadget.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#undef DEBUG +#undef VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* using "" instead of <> to allow building a module against a different set of kernel headers */ +#include "linux/android_gadget.h" + +#ifdef CONFIG_ANDROID_KDBG +#include "android_kernel_debug.h" +#endif + +#define xprintk(d,level,fmt,args...) \ + { if ((d)->gadget) dev_printk(level , &(d)->gadget->dev , fmt , ## args); else printk(fmt , ## args); } + +#ifdef DEBUG +#define DBG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define DBG(dev,fmt,args...) do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(dev,fmt,args...) do { } while (0) +#endif /* VERBOSE */ + +#define ERROR(dev,fmt,args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +#define WARN(dev,fmt,args...) \ + xprintk(dev , KERN_WARNING , fmt , ## args) +#define INFO(dev,fmt,args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) + +MODULE_AUTHOR("Google, Inc."); +MODULE_DESCRIPTION("Android USB Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +/* IO Thread commands */ +#define ANDROID_THREAD_QUIT 1 +#define ANDROID_THREAD_OPEN_FILE 2 +#define ANDROID_THREAD_CLOSE_FILE 3 +#define ANDROID_THREAD_READ_TO_FILE 4 +#define ANDROID_THREAD_WRITE_FROM_FILE 5 + + +/* IDs for string descriptors */ +#define STRING_MANUFACTURER 1 +#define STRING_PRODUCT 2 +#define STRING_SERIAL 3 +#define STRING_CONFIG 4 +#define STRING_ADB_INTERFACE 5 +#define STRING_UMS_INTERFACE 6 +#ifdef CONFIG_ANDROID_KDBG +#define STRING_KDBG_INTERFACE 7 +#endif +#define STRING_MTP_DESCRIPTOR 238 + +/* String Table */ +static char manufacturer[64]; +static char product[64]; +static char serial[64]; + +static struct usb_string strings[] = { + { STRING_MANUFACTURER, manufacturer }, + { STRING_PRODUCT, product }, + { STRING_SERIAL, serial }, + { STRING_CONFIG, "Android Config" }, + { STRING_ADB_INTERFACE, "ADB Interface" }, + { STRING_UMS_INTERFACE, "UMS Interface" }, +#ifdef CONFIG_ANDROID_KDBG + { STRING_KDBG_INTERFACE, "Android Kernel Debug Interface" }, +#endif + {} +}; + +static struct usb_gadget_strings stringtab = { + .language = 0x0409, /* en-US */ + .strings = strings, +}; + +static const char mtp_descriptor_string[18] = + { 18, USB_DT_STRING, 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, 0xFE, 0 }; +static const u8 mtp_vendor_descriptor[40] = + { 40, 0, 0, 0, 0, 1, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 'M', 'T', 'P', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* ID for our one configuration. */ +#define CONFIG_VALUE 1 + +/* UMS setup class requests */ +#define USB_BULK_GET_MAX_LUN_REQUEST 0xFE +#define USB_BULK_RESET_REQUEST 0xFF + + +#define ADB_BULK_BUFFER_SIZE 4096 +#define UMS_BULK_BUFFER_SIZE 4096 + +#define NO_SECONDARY_REQUESTS 0 +#define CREATE_SECONDARY_REQUESTS 1 + + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof(device_desc), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, +// these are set in android_init and via ioctl() +// .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), +// .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), + .bcdDevice = __constant_cpu_to_le16(0xffff), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor config_desc = { + .bLength = sizeof(config_desc), + .bDescriptorType = USB_DT_CONFIG, +// .bNumInterfaces is filled in later + .bConfigurationValue = CONFIG_VALUE, + .iConfiguration = STRING_CONFIG, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0x80, /* 250ma */ +}; + + +static struct usb_qualifier_descriptor qualifier_desc = { + .bLength = sizeof(struct usb_qualifier_descriptor), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = __constant_cpu_to_le16 (0x0200), + .bNumConfigurations = 1, +}; + +#define EP0_BUFSIZE 256 +#define MAX_DESC_LEN 256 + +static const char longname [] = "Android Gadget Driver"; +static const char shortname [] = "android_gadget"; + +static atomic_t adb_open = ATOMIC_INIT(0); +static atomic_t ums_open = ATOMIC_INIT(0); +static struct platform_device *android_platform_dev; + +/* Number of logical units, default is 1. Settable via ANDROID_GADGET_SET_UMS_DEVICE_COUNT ioctl() */ +static int lun_count = 1; +static int enable_adb = 0; +static int enable_ums = 0; +static int enable_mtp = 0; +static int enable_kdbg = 0; + +static int product_id = 0xD00D; /* product ID when running with single interface */ +static int composite_product_id = 0xDEED; /* product ID when running with multiple interfaces */ + + +/* counter to handle usb_gadget_register_driver() / usb_gadget_unregister_driver() */ +static int gadget_registered = 0; + + +struct android_gadget_data { + spinlock_t lock; + struct usb_gadget *gadget; + struct usb_request *req; /* request for ep0 */ + u8 config; + u8 connected; + + struct android_file_data *adb_data; + struct android_file_data *ums_data; +#ifdef CONFIG_ANDROID_KDBG + struct android_kdbg_data *kdbg_data; +#endif + + /* for our file IO thread */ + struct task_struct *thread; + /* gadget_file_data for android file for current command */ + struct android_file_data *thread_fdata; + /* current command for IO thread (or zero for none) */ + int thread_command; + /* data for current IO thread command */ + void *thread_command_data; + /* used to wait for thread to complete current command */ + struct completion thread_wait; + /* result from current command */ + int thread_result; +}; + +struct android_request { + struct usb_request *req; + struct usb_ep *ep; + int buffer_size; + struct completion complete; + int status; +}; + +struct android_file_data { + spinlock_t lock; + struct android_gadget_data *gdata; + + wait_queue_head_t wait; /* used to wait in read */ + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; + struct android_request *bulk_in_1; + struct android_request *bulk_in_2; + struct android_request *bulk_out_1; + struct android_request *bulk_out_2; + u8 disconnected; /* used to force android_read() to fail */ + u8 read_disconnect:1, /* used to force android_read() to fail */ + write_disconnect:1; /* used to force android_write() to fail */}; + +static struct usb_interface_descriptor adb_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, +// .bInterfaceNumber is filled in later + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = STRING_ADB_INTERFACE, +}; + +static struct usb_interface_descriptor ums_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, +// .bInterfaceNumber is filled in later + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = US_SC_SCSI, + .bInterfaceProtocol = US_PR_BULK, + .iInterface = STRING_UMS_INTERFACE, +}; + +#ifdef CONFIG_ANDROID_KDBG +static struct usb_interface_descriptor kdbg_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, +// .bInterfaceNumber is filled in later + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .iInterface = STRING_KDBG_INTERFACE, +}; +#endif + +static struct usb_endpoint_descriptor adb_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor adb_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor ums_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor ums_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor ums_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor ums_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_intr_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(8), + .bInterval = 6, +}; + +#ifdef CONFIG_ANDROID_KDBG +static struct usb_endpoint_descriptor kdbg_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor kdbg_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor kdbg_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor kdbg_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; +#endif + + +/* temporary variable used between android_open() and android_gadget_bind() */ +static struct android_gadget_data* gdata_temp; + +static void request_complete(struct usb_ep* ep, struct usb_request* req); + +static struct android_request* android_request_new(struct usb_ep* ep, int buffer_size) +{ + struct android_request* req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + req->req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req->req) { + kfree(req); + return NULL; + } + + /* now allocate buffers for the requests */ + req->req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->req->buf) { + usb_ep_free_request(ep, req->req); + kfree(req); + return NULL; + } + + init_completion(&req->complete); + + req->ep = ep; + req->buffer_size = buffer_size; + req->req->complete = request_complete; + req->req->context = req; + + return req; +} + +static void android_request_free(struct android_request* req) +{ + if (req) { + if (req->req) { + if (req->req->buf) + kfree(req->req->buf); + + usb_ep_free_request(req->ep, req->req); + } + + kfree(req); + } +} + +static struct android_file_data* android_file_data_new(struct android_gadget_data* gdata) +{ + struct android_file_data* data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->gdata = gdata; + spin_lock_init(&data->lock); + init_waitqueue_head(&data->wait); + return data; +} + +static void android_file_data_free(struct android_file_data* data) +{ + BUG_ON(waitqueue_active(&data->wait)); + kfree(data); +} + +static void android_gadget_data_free(struct android_gadget_data* data) +{ + if (data->adb_data) + android_file_data_free(data->adb_data); + if (data->ums_data) + android_file_data_free(data->ums_data); +#ifdef CONFIG_ANDROID_KDBG + if (data->kdbg_data) + android_kdbg_data_free(data->kdbg_data); +#endif + kfree(data); +} + +static struct android_gadget_data* android_gadget_data_new(void) +{ + struct android_gadget_data* data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->adb_data = android_file_data_new(data); + if (!data->adb_data) { + android_gadget_data_free(data); + return NULL; + } + + data->ums_data = android_file_data_new(data); + if (!data->ums_data) { + if (data->adb_data) + android_file_data_free(data->adb_data); + android_gadget_data_free(data); + return NULL; + } + +#ifdef CONFIG_ANDROID_KDBG + data->kdbg_data = android_kdbg_data_new(data); + if (!data->adb_data) { + android_file_data_free(data->ums_data); + android_file_data_free(data->adb_data); + android_gadget_data_free(data); + return NULL; + } +#endif + + spin_lock_init(&data->lock); + init_completion(&data->thread_wait); + return data; +} + +static void ep0_complete(struct usb_ep* ep, struct usb_request* req) +{ +// printk("ep0_complete\n"); +} + +static int enable_bulk_endpoints(struct android_file_data* fdata, const struct usb_endpoint_descriptor *in_desc, const struct usb_endpoint_descriptor *out_desc) +{ + int result = usb_ep_enable(fdata->bulk_in, in_desc); + if (result) + return result; + result = usb_ep_enable(fdata->bulk_out, out_desc); + if (result) + return result; + + return 0; +} + +static void disable_bulk_endpoints(struct android_file_data* fdata) +{ + usb_ep_disable(fdata->bulk_in); + usb_ep_disable(fdata->bulk_out); +} + +static int set_configuration(struct android_gadget_data* gdata, int config, int speed) +{ + struct android_file_data* adb_data = gdata->adb_data; + struct android_file_data* ums_data = gdata->ums_data; +#ifdef CONFIG_ANDROID_KDBG + struct android_kdbg_data* kdbg_data = gdata->kdbg_data; +#endif + int result; + + if (gdata->config == config) + return 0; + + if (config == CONFIG_VALUE) { + result = enable_bulk_endpoints(adb_data, + (speed == USB_SPEED_HIGH ? &adb_highspeed_in_desc : &adb_fullspeed_in_desc), + (speed == USB_SPEED_HIGH ? &adb_highspeed_out_desc : &adb_fullspeed_out_desc)); + if (result) { + DBG(gdata, "enable_bulk_endpoints for adb returned %d\n", result); + return result; + } + + result = enable_bulk_endpoints(ums_data, + (speed == USB_SPEED_HIGH ? &ums_highspeed_in_desc : &ums_fullspeed_in_desc), + (speed == USB_SPEED_HIGH ? &ums_highspeed_out_desc : &ums_fullspeed_out_desc)); + if (result) { + DBG(gdata, "enable_bulk_endpoints for ums returned %d\n", result); + return result; + } + +#ifdef CONFIG_ANDROID_KDBG + result = enable_kdbg_bulk_endpoints(kdbg_data, + (speed == USB_SPEED_HIGH ? &kdbg_highspeed_in_desc : &kdbg_fullspeed_in_desc), + (speed == USB_SPEED_HIGH ? &kdbg_highspeed_out_desc : &kdbg_fullspeed_out_desc)); + if (result) { + DBG(gdata, "enable_bulk_endpoints for kdbg returned %d\n", result); + return result; + } +#endif + } else { + disable_bulk_endpoints(adb_data); + disable_bulk_endpoints(ums_data); +#ifdef CONFIG_ANDROID_KDBG + disable_kdbg_bulk_endpoints(kdbg_data); +#endif + } + + gdata->config = config; + + if (adb_data) + wake_up_interruptible(&adb_data->wait); + if (ums_data) + wake_up_interruptible(&ums_data->wait); + + return 0; +} + +static int create_bulk_endpoints(struct usb_gadget *gadget, struct android_file_data* fdata, + int buffer_size, + int create_secondary_requests, + struct usb_endpoint_descriptor* in_desc, + struct usb_endpoint_descriptor* out_desc) +{ + struct usb_ep* ep; + + ep = usb_ep_autoconfig(gadget, in_desc); + if (!ep) { + DBG(fdata->gdata, "usb_ep_autoconfig for bulk_in failed\n"); + return -ENODEV; + } + DBG(fdata->gdata, "usb_ep_autoconfig for bulk_in got %s\n", ep->name); + ep->driver_data = fdata; + fdata->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, out_desc); + if (!ep) { + DBG(fdata->gdata, "usb_ep_autoconfig for bulk_out failed\n"); + return -ENODEV; + } + DBG(fdata->gdata, "usb_ep_autoconfig for adb bulk_out got %s\n", ep->name); + ep->driver_data = fdata; + fdata->bulk_out = ep; + + /* now allocate requests for our endpoints */ + fdata->bulk_in_1 = android_request_new(fdata->bulk_in, buffer_size); + if (!fdata->bulk_in_1) + return -ENOMEM; + + fdata->bulk_out_1 = android_request_new(fdata->bulk_out, buffer_size); + if (!fdata->bulk_out_1) + return -ENOMEM; + + if (create_secondary_requests) { + fdata->bulk_in_2 = android_request_new(fdata->bulk_in, buffer_size); + if (!fdata->bulk_in_2) + return -ENOMEM; + + fdata->bulk_out_2 = android_request_new(fdata->bulk_out, buffer_size); + if (!fdata->bulk_out_2) + return -ENOMEM; + } + + return 0; +} + +static void free_bulk_endpoints(struct android_file_data* fdata) +{ + android_request_free(fdata->bulk_in_1); + android_request_free(fdata->bulk_in_2); + android_request_free(fdata->bulk_out_1); + android_request_free(fdata->bulk_out_2); + + fdata->bulk_in_1 = NULL; + fdata->bulk_in_2 = NULL; + fdata->bulk_out_1 = NULL; + fdata->bulk_out_2 = NULL; +} + +static void android_gadget_unbind (struct usb_gadget *gadget) +{ + struct android_gadget_data* gdata = get_gadget_data(gadget); + + printk("android_gadget_unbind\n"); + + set_configuration(gdata, 0, USB_SPEED_UNKNOWN); + + gadget->ep0->driver_data = NULL; + set_gadget_data (gadget, NULL); + + spin_lock_irq(&gdata->lock); + free_bulk_endpoints(gdata->adb_data); + free_bulk_endpoints(gdata->ums_data); +#ifdef CONFIG_ANDROID_KDBG + free_android_kdbg_endpoints(gdata->kdbg_data); +#endif + + if (gdata->req) { + kfree(gdata->req->buf); + usb_ep_free_request(gadget->ep0, gdata->req); + gdata->req = NULL; + } + + gdata->connected = 0; + spin_unlock_irq(&gdata->lock); + + if (gdata->adb_data) { + gdata->adb_data->read_disconnect = 1; + gdata->adb_data->write_disconnect = 1; + wake_up_interruptible(&gdata->adb_data->wait); + } + if (gdata->ums_data) { + gdata->ums_data->read_disconnect = 1; + gdata->ums_data->write_disconnect = 1; + wake_up_interruptible(&gdata->ums_data->wait); + } +} + +static int android_gadget_bind (struct usb_gadget *gadget) +{ + struct android_gadget_data* gdata = gdata_temp; + + printk("android_gadget_bind\n"); + if (!gdata) + return -ESRCH; + + gdata->connected = 0; + gdata->adb_data->read_disconnect = 0; + gdata->adb_data->write_disconnect = 0; + gdata->ums_data->read_disconnect = 0; + gdata->ums_data->write_disconnect = 0; + + set_gadget_data(gadget, gdata); + gdata->gadget = gadget; + + /* auto configure our bulk endpoints */ + usb_ep_autoconfig_reset(gadget); + + DBG(gdata, "android_gadget_bind create_bulk_endpoints\n"); + create_bulk_endpoints(gadget, gdata->adb_data, ADB_BULK_BUFFER_SIZE, NO_SECONDARY_REQUESTS, &adb_fullspeed_in_desc, &adb_fullspeed_out_desc); + create_bulk_endpoints(gadget, gdata->ums_data, UMS_BULK_BUFFER_SIZE, CREATE_SECONDARY_REQUESTS, &ums_fullspeed_in_desc, &ums_fullspeed_out_desc); + if (enable_mtp) { + struct usb_ep* ep = usb_ep_autoconfig(gadget, &mtp_intr_desc); + if (ep) { + DBG(gdata, "usb_ep_autoconfig for mtp_intr_desc got %s\n", ep->name); + } else { + DBG(gdata, "usb_ep_autoconfig for mtp_intr_desc failed\n"); + } + } +#ifdef CONFIG_ANDROID_KDBG + create_android_kdbg_endpoints(gadget, gdata->kdbg_data, ADB_BULK_BUFFER_SIZE, &kdbg_fullspeed_in_desc, &kdbg_fullspeed_out_desc); +#endif + + /* copy endpoint addresses computed by usb_ep_autoconfig() to the high speed descriptors */ + adb_highspeed_in_desc.bEndpointAddress = adb_fullspeed_in_desc.bEndpointAddress; + adb_highspeed_out_desc.bEndpointAddress = adb_fullspeed_out_desc.bEndpointAddress; + ums_highspeed_in_desc.bEndpointAddress = ums_fullspeed_in_desc.bEndpointAddress; + ums_highspeed_out_desc.bEndpointAddress = ums_fullspeed_out_desc.bEndpointAddress; +#ifdef CONFIG_ANDROID_KDBG + kdbg_highspeed_in_desc.bEndpointAddress = kdbg_fullspeed_in_desc.bEndpointAddress; + kdbg_highspeed_out_desc.bEndpointAddress = kdbg_fullspeed_out_desc.bEndpointAddress; +#endif + + gdata->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!gdata->req) + goto err; + + gdata->req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); + if (!gdata->req->buf) + goto err; + + gdata->req->context = NULL; + gdata->req->complete = ep0_complete; + + /* set device max packet size */ + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + qualifier_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + usb_gadget_set_selfpowered(gadget); + + gdata->connected = 1; + return 0; + +err: + android_gadget_unbind(gadget); + return -ENOMEM; +} + + +static int ums_class_request(struct android_gadget_data* gdata, + u8 direction, u8 request, u16 index, u16 value, u16 length, u8* buffer) +{ + /* UMS setup class requests */ + DBG(gdata, "CLASS request\n"); + + if (request == USB_BULK_GET_MAX_LUN_REQUEST) { + if (direction != USB_DIR_IN || value != 0 || index != 0) + goto unsupported; + + buffer[0] = lun_count - 1; + DBG(gdata, "USB_BULK_GET_MAX_LUN_REQUEST returning %d\n", lun_count - 1); + return 1; + } else if (request == USB_BULK_RESET_REQUEST) { + if (direction != USB_DIR_OUT || value != 0 || index != 0) + goto unsupported; + + /* FIXME - I'm not sure what to do here */ + printk("USB_BULK_RESET_REQUEST\n"); + return 0; + } + +unsupported: + return -EOPNOTSUPP; +} + +static int ums_vendor_request(struct android_gadget_data* gdata, + u8 direction, u8 request, u16 index, u16 value, u16 length, u8* buffer) +{ + /* UMS setup vendor requests */ + + DBG(gdata, "VENDOR request\n"); + /* handle query for MTP support */ + if (request == 0xFE) { + if (!enable_mtp || direction != USB_DIR_IN || value != 0 || index != 4) + goto unsupported; + + DBG(gdata, "Sending MTP vendor string\n"); + memcpy(buffer, mtp_vendor_descriptor, sizeof(mtp_vendor_descriptor)); + return sizeof(mtp_vendor_descriptor); + } + +unsupported: + return -EOPNOTSUPP; +} + + +/* must have enough entries for 3 descriptors for each interface, an interrupt endpoint if MTP is enabled, and NULL termination */ +#ifdef CONFIG_ANDROID_KDBG +static const struct usb_descriptor_header* android_function[3 * 3 + 1 + 1]; +#else +static const struct usb_descriptor_header* android_function[2 * 3 + 1 + 1]; +#endif + +static const struct usb_descriptor_header** build_device_function(int high_speed) +{ + const struct usb_descriptor_header** descriptor = android_function; + int interface = 0; + + /* UMS interface needs to be first in order for usb_storage to work on Linux host */ + if (enable_ums) { + ums_interface_desc.bNumEndpoints = (enable_mtp ? 3 : 2); + + *descriptor++ = (struct usb_descriptor_header *)&ums_interface_desc; + ums_interface_desc.bInterfaceNumber = interface++; + + if (high_speed) { + *descriptor++ = (struct usb_descriptor_header *)&ums_highspeed_in_desc; + *descriptor++ = (struct usb_descriptor_header *)&ums_highspeed_out_desc; + } else { + *descriptor++ = (struct usb_descriptor_header *)&ums_fullspeed_in_desc; + *descriptor++ = (struct usb_descriptor_header *)&ums_fullspeed_out_desc; + } + + if (enable_mtp) { + *descriptor++ = (struct usb_descriptor_header *)&mtp_intr_desc; + } + } + + /* ADB interface is next */ + if (enable_adb) { + *descriptor++ = (struct usb_descriptor_header *)&adb_interface_desc; + adb_interface_desc.bInterfaceNumber = interface++; + + if (high_speed) { + *descriptor++ = (struct usb_descriptor_header *)&adb_highspeed_in_desc; + *descriptor++ = (struct usb_descriptor_header *)&adb_highspeed_out_desc; + } else { + *descriptor++ = (struct usb_descriptor_header *)&adb_fullspeed_in_desc; + *descriptor++ = (struct usb_descriptor_header *)&adb_fullspeed_out_desc; + } + } +#ifdef CONFIG_ANDROID_KDBG + if (enable_kdbg) { + *descriptor++ = (struct usb_descriptor_header *)&kdbg_interface_desc; + kdbg_interface_desc.bInterfaceNumber = interface++; + + if (high_speed) { + *descriptor++ = (struct usb_descriptor_header *)&kdbg_highspeed_in_desc; + *descriptor++ = (struct usb_descriptor_header *)&kdbg_highspeed_out_desc; + } else { + *descriptor++ = (struct usb_descriptor_header *)&kdbg_fullspeed_in_desc; + *descriptor++ = (struct usb_descriptor_header *)&kdbg_fullspeed_out_desc; + } + } +#endif + + /* NULL terminate */ + *descriptor = NULL; + + config_desc.bNumInterfaces = interface; + + return android_function; +} + +static int android_gadget_setup(struct usb_gadget* gadget, const struct usb_ctrlrequest* ctrl) +{ + struct android_gadget_data* gdata = get_gadget_data(gadget); + struct usb_request* req = gdata->req; + const struct usb_descriptor_header** function; + + int result = -EOPNOTSUPP; + u8 request_type = (ctrl->bRequestType & USB_TYPE_MASK); + u8 direction = (ctrl->bRequestType & USB_DIR_IN); + u8 request = ctrl->bRequest; + u16 index = le16_to_cpu(ctrl->wIndex); + u16 value = le16_to_cpu(ctrl->wValue); + u16 length = le16_to_cpu(ctrl->wLength); + + spin_lock(&gdata->lock); + + DBG(gdata, "SETUP type: %02x, request: %02x, index: %04x, value: %04x, length: %04x\n", + ctrl->bRequestType, request, index, value, length); + + if (request_type == USB_TYPE_STANDARD) { + switch (request) { + case USB_REQ_GET_DESCRIPTOR: { + int descriptorType = value >> 8; + int descriptorIndex = value & 0xFF; + + DBG(gdata, "USB_REQ_GET_DESCRIPTOR: "); + if (direction != USB_DIR_IN) { + DBG(gdata, "wrong direction!\n"); + goto unsupported; + } + + switch (descriptorType) { + case USB_DT_DEVICE: + DBG(gdata, "USB_DT_DEVICE\n"); + /* use composite product ID if more than one of these features are enabled */ + if (enable_adb + enable_ums + enable_kdbg > 1 && composite_product_id) + device_desc.idProduct = composite_product_id; + else + device_desc.idProduct = product_id; + result = sizeof(device_desc); + memcpy(req->buf, &device_desc, result); + break; + + case USB_DT_CONFIG: + case USB_DT_OTHER_SPEED_CONFIG: { + int high_speed; + + if (descriptorType == USB_DT_OTHER_SPEED_CONFIG) { + high_speed = (gadget->speed != USB_SPEED_HIGH); + DBG(gdata, "USB_DT_OTHER_SPEED_CONFIG\n"); + } else { + high_speed = (gadget->speed == USB_SPEED_HIGH); + DBG(gdata, "USB_DT_CONFIG\n"); + } + + DBG(gdata, "high_speed = %d\n", high_speed); + if (descriptorIndex >= device_desc.bNumConfigurations) + return -EINVAL; + + function = build_device_function(high_speed); + + result = usb_gadget_config_buf(&config_desc, req->buf, MAX_DESC_LEN, function); + if (result < 0) + break; + + ((struct usb_config_descriptor *)req->buf)->bDescriptorType = descriptorType; + break; + } + + case USB_DT_DEVICE_QUALIFIER: + DBG(gdata, "USB_DT_DEVICE_QUALIFIER\n"); + if (!gadget->is_dualspeed) + goto unsupported; + result = sizeof(qualifier_desc); + memcpy(req->buf, &qualifier_desc, result); + break; + + case USB_DT_STRING: + DBG(gdata, "USB_DT_STRING\n"); + // STRING_MTP_DESCRIPTOR should only be returned if MTP is enabled + if (descriptorIndex == STRING_MTP_DESCRIPTOR && enable_mtp) { + result = sizeof(mtp_descriptor_string); + memcpy(req->buf, mtp_descriptor_string, result); + break; + } + result = usb_gadget_get_string(&stringtab, descriptorIndex, req->buf); + break; + } + break; + } + + case USB_REQ_GET_INTERFACE: + DBG(gdata, "USB_REQ_GET_INTERFACE\n"); + if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) + goto unsupported; + if (gdata->config == 0) + goto unsupported; + + if (index != 0 && index != 1) + goto unsupported; + *(u8*)req->buf = 0; + result = 1; + break; + + case USB_REQ_SET_INTERFACE: { + int config, speed; + + DBG(gdata, "USB_REQ_SET_INTERFACE\n"); + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE)) + goto unsupported; + if (gdata->config == 0 || (index != 0 && index != 1) || value != 0) + goto unsupported; + + /* this will force a reset of our bulk endpoints */ + config = gdata->config; + speed = gadget->speed; + set_configuration(gdata, 0, USB_SPEED_UNKNOWN); + set_configuration(gdata, config, speed); + result = 0; + break; + } + + case USB_REQ_GET_CONFIGURATION: + DBG(gdata, "USB_REQ_GET_CONFIGURATION\n"); + if (direction != USB_DIR_IN) + goto unsupported; + *(u8*)req->buf = gdata->config; + result = 1; + break; + + case USB_REQ_SET_CONFIGURATION: + DBG(gdata, "USB_REQ_SET_CONFIGURATION\n"); + if (ctrl->bRequestType != USB_DIR_OUT) + goto unsupported; + + DBG(gdata, "set configuration: %d\n", value); + if (value != 0 && value != CONFIG_VALUE) + goto unsupported; + + set_configuration(gdata, value, gadget->speed); + result = 0; + break; + + default: + unsupported: + DBG(gdata, "Unsupported SETUP type: %02x, request: %02x, index: %04x, value: %04x, length: %04x\n", + ctrl->bRequestType, request, index, value, length); + } + } else if (request_type == USB_TYPE_CLASS) { + result = ums_class_request(gdata, direction, request, index, value, length, (u8*)req->buf); + } else if (request_type == USB_TYPE_VENDOR) { + result = ums_vendor_request(gdata, direction, request, index, value, length, (u8*)req->buf); + } + + /* send response */ + if (result >= 0) { + req->length = min(length, (u16)result); + req->zero = result < length && (result % gadget->ep0->maxpacket) == 0; + result = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (result < 0) { + ERROR(gdata, "usb_ep_queue returned %d in setup\n", result); + req->status = 0; + } + } + + spin_unlock(&gdata->lock); + + return result; +} + + +static void android_gadget_disconnect(struct usb_gadget* gadget) +{ +// struct android_gadget_data* gdata = get_gadget_data(gadget); + DBG(gdata, "android_gadget_disconnect\n"); +} + +static void android_gadget_suspend(struct usb_gadget* gadget) +{ +// struct android_gadget_data* gdata = get_gadget_data(gadget); + DBG(gdata, "android_gadget_suspend\n"); +} + +static void android_gadget_resume(struct usb_gadget* gadget) +{ +// struct android_gadget_data* gdata = get_gadget_data(gadget); + DBG(gdata, "android_gadget_resume\n"); +} + + +static int android_read_to_file(struct android_file_data* fdata, int fd, loff_t offset, size_t length); +static int android_write_from_file(struct android_file_data* fdata, int fd, loff_t offset, size_t length); + +/* Kernel thread for handling file IO operations */ +static int android_thread(void* data) +{ + struct android_gadget_data* gdata = (struct android_gadget_data*)data; + struct android_gadget_file_open* file_open; + struct android_gadget_file_copy* file_copy; + int fd; + + DBG(gdata, "android_thread started\n"); + + while (1) { + /* wait for a command */ + while (1) { + try_to_freeze(); + set_current_state(TASK_INTERRUPTIBLE); + if (gdata->thread_command != 0) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + + switch (gdata->thread_command) { + case ANDROID_THREAD_QUIT: + DBG(gdata, "ANDROID_THREAD_QUIT\n"); + gdata->thread_result = 0; + goto done; + + case ANDROID_THREAD_OPEN_FILE: + file_open = (struct android_gadget_file_open *)gdata->thread_command_data; + gdata->thread_result = sys_open(file_open->path, file_open->flags, file_open->mode); + DBG(gdata, "ANDROID_THREAD_OPEN_FILE %s returned %d\n", file_open->path, gdata->thread_result); + break; + + case ANDROID_THREAD_CLOSE_FILE: + fd = *((int *)gdata->thread_command_data); + gdata->thread_result = sys_close(fd); + DBG(gdata, "ANDROID_THREAD_CLOSE_FILE %d returned %d\n", fd, gdata->thread_result); + break; + + case ANDROID_THREAD_READ_TO_FILE: + file_copy = (struct android_gadget_file_copy *)gdata->thread_command_data; + gdata->thread_result = android_read_to_file(gdata->thread_fdata, file_copy->fd, file_copy->offset, file_copy->length); + DBG(gdata, "ANDROID_THREAD_READ_TO_FILE %d returned %d\n", file_copy->fd, gdata->thread_result); + break; + + case ANDROID_THREAD_WRITE_FROM_FILE: + file_copy = (struct android_gadget_file_copy *)gdata->thread_command_data; + gdata->thread_result = android_write_from_file(gdata->thread_fdata, file_copy->fd, file_copy->offset, file_copy->length); + DBG(gdata, "ANDROID_THREAD_WRITE_FROM_FILE %d returned %d\n", file_copy->fd, gdata->thread_result); + break; + } + + gdata->thread_command = 0; + complete(&gdata->thread_wait); + } + +done: + DBG(gdata, "android_thread done\n"); + complete_and_exit(&gdata->thread_wait, 0); +} + +static int do_thread_command(struct android_gadget_data* gdata, + struct android_file_data* fdata, + int command, void* data) +{ + /* pass command and data to the thread */ + gdata->thread_fdata = fdata; + gdata->thread_command = command; + gdata->thread_command_data = data; + + /* wake up the thread */ + init_completion(&gdata->thread_wait); + wake_up_process(gdata->thread); + + /* wait for the thread to complete the command */ + wait_for_completion(&gdata->thread_wait); + return gdata->thread_result; +} + +static struct usb_gadget_driver android_gadget_driver = { +#ifdef CONFIG_USB_GADGET_DUALSPEED + .speed = USB_SPEED_HIGH, +#else + .speed = USB_SPEED_FULL, +#endif + .function = (char *)longname, + .bind = android_gadget_bind, + .unbind = android_gadget_unbind, + .setup = android_gadget_setup, + .disconnect = android_gadget_disconnect, + .suspend = android_gadget_suspend, + .resume = android_gadget_resume, + + .driver = { + .name = (char *) shortname, + .owner = THIS_MODULE, + }, +}; + +static int enable_usb(int enable) +{ + int ret; + + if (enable && !gadget_registered) { + if (enable_adb || enable_ums || enable_kdbg) { + ret = usb_gadget_register_driver(&android_gadget_driver); + if (ret < 0) { + printk("usb_gadget_register_driver returned %d\n", ret); + return ret; + } + + gadget_registered = 1; + } else { + printk("You must enable ADB or UMS before attempting to enable USB\n"); + return -1; + } + } else if (gadget_registered && !enable) { + usb_gadget_unregister_driver(&android_gadget_driver); + gadget_registered = 0; + } + + return 0; +} + +/* used to handle both bulk reads and writes */ +static int send_request(struct android_request* req, int length) +{ + init_completion(&req->complete); + req->status = 0; + req->req->length = length; + return usb_ep_queue(req->ep, req->req, GFP_ATOMIC); +} + +/* completion routine for bulk reads and writes */ +static void request_complete(struct usb_ep* ep, struct usb_request* usb_req) +{ + struct android_request* req = usb_req->context; + if (!req) + return; + + if (usb_req->status) + req->status = usb_req->status; + else + req->status = usb_req->actual; + + complete(&req->complete); +} + +static int wait_request(struct android_gadget_data* gdata, + struct android_request* req) +{ + int result = wait_event_interruptible(req->complete.wait, req->complete.done); + + if (result != 0) { +//printk("wait_event_interruptible returned %d\n", result); + spin_lock_irq(&gdata->lock); + + if (likely (gdata->connected)) { + usb_ep_dequeue(req->ep, req->req); + spin_unlock_irq(&gdata->lock); + + wait_event(req->complete.wait, req->complete.done); + } else { + /* endpoint is gone */ + spin_unlock_irq (&gdata->lock); + } + } else { + result = req->status; + } + + return result; +} + +static int android_read_write(struct android_file_data* fdata, + struct android_request* req, + int length) +{ + int result; + + result = send_request(req, length); + if (result < 0) + return result; + + return wait_request(fdata->gdata, req); +} + +/* this function reads data from USB and writes it to a local file */ +static int android_read_to_file(struct android_file_data* fdata, int fd, loff_t offset, size_t length) +{ + struct android_gadget_data* gdata = fdata->gdata; + struct file *file; + int result = 0; + void* readBuffer; /* buffer for current read request */ + int readBufferSize; /* # of bytes in readBuffer */ + void* writeBuffer; /* buffer containing bytes to write to file */ + int writeBufferSize; /* # of bytes in writeBuffer */ + int remaining = length; + int totalCopied = 0; + struct android_request* req1 = fdata->bulk_out_1; + struct android_request* req2 = fdata->bulk_out_2; + struct android_request* req = req1; + int maxPacket = req->ep->maxpacket; + + if (!gdata->config) + return -ENODEV; + if (!req1 || !req2) + return -ENOMEM; + + /* special case zero length read */ + if (remaining == 0) + return android_read_write(fdata, req, 0); + + file = fget(fd); + if (!file) + return -EBADF; + if (!file->f_op->write) { + result = -EPERM; + goto error; + } + + /* initialize buffer pointers */ + readBuffer = req->req->buf; + readBufferSize = 0; + writeBuffer = NULL; + writeBufferSize = 0; + + while (remaining > 0 || writeBuffer != NULL) { + /* send new read request */ + if (remaining > 0) { + readBufferSize = remaining; + + /* omap_udc driver can't handle partial packet reads. see omap_udc.c, line 982. + So round up to multiple of maxPacket */ + readBufferSize = ((readBufferSize + maxPacket - 1) / maxPacket) * maxPacket; + + /* don't read past the buffer */ + if (readBufferSize > req->buffer_size) + readBufferSize = req->buffer_size; + + result = send_request(req, readBufferSize); + if (result < 0) + goto error; + + remaining -= readBufferSize; + } else { + readBuffer = NULL; + readBufferSize = 0; + } + + /* write available data to file */ + if (writeBuffer != NULL && writeBufferSize > 0) { + result = vfs_write(file, (const char __user *)writeBuffer, writeBufferSize, &offset); + if (result > 0) + totalCopied += result; + + writeBuffer = NULL; + writeBufferSize = 0; + } + + /* wait for previous read request, if any */ + if (readBuffer != NULL) { + int waitResult = wait_request(gdata, req); + if (waitResult >= 0) { + /* stop reading if we get a partial read */ + if (waitResult < readBufferSize) + remaining = 0; + + /* set up for file write */ + writeBuffer = readBuffer; + writeBufferSize = waitResult; + + /* swap requests */ + req = (req == req1 ? req2 : req1); + readBuffer = req->req->buf; + } else { + result = waitResult; + } + } + + if (result < 0) + goto error; + } + + result = totalCopied; + +error: + fput(file); + return result; +} + +/* this function reads data from a local file and writes it out USB */ +static int android_write_from_file(struct android_file_data* fdata, int fd, loff_t offset, size_t length) +{ + struct android_gadget_data* gdata = fdata->gdata; + struct file *file = NULL; + int result = 0; + void* readBuffer; /* buffer containing most recently read data */ + int readBufferSize; /* # of bytes in readBuffer */ + void* writeBuffer; /* buffer containing bytes to write via USB */ + int writeBufferSize; /* # of bytes in writeBuffer */ + int remaining = length; + int totalCopied = 0; + int totalWritten = 0; + struct android_request* req1 = fdata->bulk_in_1; + struct android_request* req2 = fdata->bulk_in_2; + struct android_request* req = req1; + + if (!gdata->config) + return -ENODEV; + if (!req1 || !req2) + return -ENOMEM; + + /* special case zero length write */ + if (remaining == 0) + return android_read_write(fdata, req, 0); + + file = fget(fd); + if (!file) + return -EBADF; + if (!file->f_op->read) { + result = -EPERM; + goto error; + } + + /* initialize buffer pointers */ + /* we use req2->buf as read buffer because we will swap requests before writing */ + readBuffer = req2->req->buf; + readBufferSize = 0; + writeBuffer = NULL; + writeBufferSize = 0; + + while (remaining > 0 || writeBuffer != NULL) { + /* write previously copied data out via USB */ + if (writeBuffer != NULL) { + result = send_request(req, writeBufferSize); + if (result < 0) + goto error; + } + + /* read more data from user buffer */ + if (remaining > 0) { + readBufferSize = min(req->buffer_size, remaining); + result = vfs_read(file, (char __user *)readBuffer, readBufferSize, &offset); + totalCopied += readBufferSize; + remaining -= readBufferSize; + } else { + readBuffer = NULL; + readBufferSize = 0; + } + + /* wait for previous write to complete */ + if (writeBuffer != NULL) { + int waitResult = wait_request(gdata, req); + if (waitResult >= 0) + totalWritten += waitResult; + else + result = waitResult; + } + + if (result < 0) + goto error; + + /* set up to write previously read data */ + writeBuffer = readBuffer; + writeBufferSize = readBufferSize; + + /* swap requests */ + readBuffer = req->req->buf; + req = (req == req1 ? req2 : req1); + } + + result = totalWritten; + +error: + if (file) + fput(file); + return result; +} + +static ssize_t android_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct android_file_data* fdata = fp->private_data; + struct android_gadget_data* gdata = fdata->gdata; + int read, result; + void* kbuf; + int remaining = count; + int totalRead = 0; + struct android_request* req; + int maxPacket; + + DBG(gdata, "android_read count: %ld\n", count); + + result = wait_event_interruptible(fdata->wait, gdata->config || fdata->read_disconnect); + DBG(gdata, "android_read: wait_event_interruptible returned %d\n", result); + + if (!gdata->config) { + fdata->read_disconnect = 0; + return -ENODEV; + } + + /* need to do this after wait_event_interruptible(), since the + ** requests might not be created until after read call starts. + */ + req = fdata->bulk_out_1; + if (!req) { + DBG(gdata, "android_read returning ENOMEM\n"); + return -ENOMEM; + } + kbuf = req->req->buf; + maxPacket = req->ep->maxpacket; + + /* special case zero length read */ + if (remaining == 0) { + result = android_read_write(fdata, req, 0); + if (result < 0) + goto done; + } + + while (remaining > 0) { + read = remaining; + + /* omap_udc driver can't handle partial packet reads. see omap_udc.c, line 982. + So round up to multiple of maxPacket */ + read = ((read + maxPacket - 1) / maxPacket) * maxPacket; + + /* don't read past the buffer */ + if (read > req->buffer_size) + read = req->buffer_size; + + result = android_read_write(fdata, req, read); + if (result < 0) + goto done; + + if (copy_to_user(buf + totalRead, kbuf, result)) { + result = -EFAULT; + goto done; + } + + remaining -= result; + totalRead += result; + + /* exit if we get a partial read */ + if (result < req->ep->maxpacket) + break; + } + + result = totalRead; + +done: + if (result == -ESHUTDOWN || result == -ECONNRESET) { + set_configuration(gdata, 0, USB_SPEED_UNKNOWN); + gdata->connected = 0; + fdata->write_disconnect = 1; + wake_up_interruptible(&fdata->wait); + } + + DBG(gdata, "android_read returning %d\n", result); + return result; +} + +static ssize_t android_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct android_file_data* fdata = fp->private_data; + struct android_gadget_data* gdata = fdata->gdata; + void* kbuf; + int write, result; + int remaining = count; + int totalWritten = 0; + struct android_request* req = fdata->bulk_in_1; + + DBG(gdata, "android_write count: %ld\n", count); + + result = wait_event_interruptible(fdata->wait, gdata->config || fdata->write_disconnect); + DBG(gdata, "wait_event_interruptible returned %d\n", result); + + if (!gdata->config) { + fdata->write_disconnect = 0; + return -ENODEV; + } + + /* need to do this after wait_event_interruptible(), since the + ** requests might not be created until after write call starts. + */ + req = fdata->bulk_in_1; + if (!req) + return -ENOMEM; + kbuf = req->req->buf; + + /* special case zero length write */ + if (remaining == 0) { + result = android_read_write(fdata, req, 0); + if (result < 0) + goto done; + } + + while (remaining > 0) { + write = min(req->buffer_size, remaining); + + if (copy_from_user(kbuf, buf + totalWritten, write)) { + result = -EFAULT; + goto done; + } + + result = android_read_write(fdata, req, write); + if (result < 0) + goto done; + + remaining -= result; + totalWritten += result; + } + + result = totalWritten; + +done: + if (result == -ESHUTDOWN || result == -ECONNRESET) { + set_configuration(gdata, 0, USB_SPEED_UNKNOWN); + gdata->connected = 0; + fdata->read_disconnect = 1; + wake_up_interruptible(&fdata->wait); + } + + DBG(gdata, "android_write returning %d\n", result); + return result; +} + +static int android_adb_open(struct inode *ip, struct file *fp) +{ + printk("android_adb_open\n"); + if (atomic_inc_return(&adb_open) == 1) { + fp->private_data = gdata_temp->adb_data; + return 0; + } else { + atomic_dec(&adb_open); + return -EBUSY; + } +} + +static int android_adb_release(struct inode *ip, struct file* fp) +{ + printk("android_adb_release\n"); + + atomic_dec(&adb_open); + if (!atomic_read(&ums_open)) + enable_usb(0); + return 0; +} + +static int android_ums_open(struct inode *ip, struct file *fp) +{ + printk("android_ums_open\n"); + if (atomic_inc_return(&ums_open) == 1) { + fp->private_data = gdata_temp->ums_data; + return 0; + } else { + atomic_dec(&ums_open); + return -EBUSY; + } +} + +static int android_ums_release(struct inode *ip, struct file* fp) +{ + printk("android_ums_release\n"); + + atomic_dec(&ums_open); + if (!atomic_read(&adb_open)) + enable_usb(0); + + return 0; +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long android_ioctl(struct file* fp, unsigned int cmd, unsigned long arg) +#else +static int android_ioctl(struct inode* ip, struct file* fp, unsigned int cmd, unsigned long arg) +#endif +{ + struct android_file_data* fdata = fp->private_data; + struct android_gadget_data* gdata = fdata->gdata; + int result = 0; + unsigned int __user* argptr = (unsigned int __user *)arg; + struct android_gadget_file_open file_open; + struct android_gadget_file_copy file_copy; + char* file_path = NULL; + const char* user_path; + int fd; + +// spin_lock(&gdata->lock); + + switch (cmd) { + case ANDROID_GADGET_SET_UMS_DEVICE_COUNT: + DBG(gdata, "ANDROID_GADGET_SET_UMS_DEVICE_COUNT %ld\n", arg); + if (arg > 0 && arg <= 8) + lun_count = arg; + else + result = -EINVAL; + break; + + case ANDROID_GADGET_SET_MANUFACTURER_NAME: + DBG(gdata, "ANDROID_GADGET_SET_MANUFACTURER_NAME\n"); + if (copy_from_user(manufacturer, argptr, sizeof(manufacturer))) + result = -EFAULT; + break; + + case ANDROID_GADGET_SET_PRODUCT_NAME: + DBG(gdata, "ANDROID_GADGET_SET_PRODUCT_NAME\n"); + if (copy_from_user(product, argptr, sizeof(product))) + result = -EFAULT; + break; + + case ANDROID_GADGET_SET_SERIAL: + DBG(gdata, "ANDROID_GADGET_SET_SERIAL\n"); + if (copy_from_user(serial, argptr, sizeof(serial))) + result = -EFAULT; + break; + + case ANDROID_GADGET_SET_VENDOR_ID: + DBG(gdata, "ANDROID_GADGET_SET_VENDOR_ID 0x%04lX\n", arg); + device_desc.idVendor = arg; + break; + + case ANDROID_GADGET_SET_PRODUCT_ID: + DBG(gdata, "ANDROID_GADGET_SET_PRODUCT_ID 0x%04lX\n", arg); + product_id = arg; + break; + + case ANDROID_GADGET_SET_COMPOSITE_PRODUCT_ID: + DBG(gdata, "ANDROID_GADGET_SET_PRODUCT_ID 0x%04lX\n", arg); + composite_product_id = arg; + break; + + case ANDROID_GADGET_ENABLE_ADB: + DBG(gdata, "ANDROID_GADGET_ENABLE_ADB %ld\n", arg); + enable_adb = (arg ? 1 : 0); + break; + + case ANDROID_GADGET_ENABLE_UMS: + DBG(gdata, "ANDROID_GADGET_ENABLE_UMS %ld\n", arg); + enable_ums = (arg ? 1 : 0); + break; + + case ANDROID_GADGET_ENABLE_MTP: + DBG(gdata, "ANDROID_GADGET_ENABLE_MTP %ld\n", arg); + enable_mtp = (arg ? 1 : 0); + break; + +#ifdef CONFIG_ANDROID_KDBG + case ANDROID_GADGET_ENABLE_KDBG: + DBG(gdata, "ANDROID_GADGET_ENABLE_KDBG %ld\n", arg); +#if 0 /* kdbg is ALWAYS enabled if compiled in */ + enable_kdbg = (arg ? 1 : 0); +#endif + break; +#endif + + case ANDROID_GADGET_ENABLE_USB: + DBG(gdata, "ANDROID_GADGET_ENABLE_USB %ld\n", arg); + result = enable_usb(arg); + break; + + case ANDROID_GADGET_OPEN_FILE: + DBG(gdata, "ANDROID_GADGET_OPEN_FILE\n"); + if (copy_from_user(&file_open, argptr, sizeof(file_open))) { + result = -EFAULT; + } else { + int path_length = file_open.path_length + 1; + + file_path = kzalloc(path_length, GFP_KERNEL); + if (!file_path) { + result = -ENOMEM; + break; + } + /* copy path from user to kernel */ + user_path = (const char *)file_open.path; + if (copy_from_user(file_path, user_path, path_length)) { + result = -EFAULT; + kfree(file_path); + break; + } + + file_open.path = file_path; + result = do_thread_command(gdata, fdata, ANDROID_THREAD_OPEN_FILE, &file_open); + kfree(file_path); + } + break; + + case ANDROID_GADGET_CLOSE_FILE: + DBG(gdata, "ANDROID_GADGET_CLOSE_FILE\n"); + fd = arg; + result = do_thread_command(gdata, fdata, ANDROID_THREAD_CLOSE_FILE, &fd); + break; + + case ANDROID_GADGET_READ_TO_FILE: + DBG(gdata, "ANDROID_GADGET_READ_TO_FILE\n"); + if (copy_from_user(&file_copy, argptr, sizeof(file_copy))) + result = -EFAULT; + else + result = do_thread_command(gdata, fdata, ANDROID_THREAD_READ_TO_FILE, &file_copy); + break; + + case ANDROID_GADGET_WRITE_FROM_FILE: + DBG(gdata, "ANDROID_GADGET_WRITE_FROM_FILE\n"); + if (copy_from_user(&file_copy, argptr, sizeof(file_copy))) + result = -EFAULT; + else + result = do_thread_command(gdata, fdata, ANDROID_THREAD_WRITE_FROM_FILE, &file_copy); + break; + + case ANDROID_GADGET_IS_CONNECTED: + result = (gdata->config == 0 ? 0 : 1); + break; + + case ANDROID_GADGET_IS_ADB_ENABLED: + result = (enable_adb ? 1 : 0); + break; + + case ANDROID_GADGET_IS_UMS_ENABLED: + result = (enable_ums ? 1 : 0); + break; + +#ifdef CONFIG_ANDROID_KDBG + case ANDROID_GADGET_IS_KDBG_ENABLED: + result = (enable_kdbg ? 1 : 0); + break; +#endif + + case ANDROID_GADGET_IS_CONFIGURED: + result = (gdata->config ? 1 : 0); + break; + + default: + result = -ENOIOCTLCMD; + break; + } + +// spin_unlock(&gdata->lock); + + return result; +} + +#ifdef HAVE_COMPAT_IOCTL +/* this function only needed in 64 bit kernels */ +static long android_compat_ioctl(struct file* fp, unsigned int cmd, unsigned long arg) +{ + /* handle this case separately, to convert struct from 32 bit to 64 bit */ + if (cmd == ANDROID_GADGET_OPEN_FILE) { + struct android_file_data* fdata = fp->private_data; + struct android_gadget_data* gdata = fdata->gdata; + struct android_gadget_file_open file_open; + char* file_path = NULL; + int path_length; + int result; + u8 buffer[sizeof(file_open)]; + +// spin_lock(&gdata->lock); + + if (copy_from_user(buffer, (unsigned int __user *)arg, sizeof(buffer))) { + result = -EFAULT; + goto error; + } + + file_open.path = (const char *)( *((u32 *)&buffer[0])); + file_open.path_length = *((int *)&buffer[4]); + file_open.flags = *((int *)&buffer[8]); + file_open.mode = *((int *)&buffer[12]); + + path_length = file_open.path_length + 1; + file_path = kzalloc(path_length, GFP_KERNEL); + if (!file_path) { + result = -ENOMEM; + goto error; + } + + /* copy path from user to kernel */ + if (copy_from_user(file_path, file_open.path, path_length)) { + result = -EFAULT; + goto error; + } + + file_open.path = file_path; + result = do_thread_command(gdata, fdata, ANDROID_THREAD_OPEN_FILE, &file_open); + +// spin_unlock(&gdata->lock); + + error: + if (file_path) + kfree(file_path); + return result; + } + + return android_ioctl(fp, cmd, arg); +} +#endif + +int android_suspend(struct platform_device *pdev, pm_message_t state) +{ + printk("android_suspend\n"); + return 0; +} + +int android_resume(struct platform_device *pdev) +{ + printk("android_resume\n"); + return 0; +} + +/* file operations for ADB device /dev/android_adb */ +static struct file_operations android_adb_fops = { + .owner = THIS_MODULE, + .read = android_read, + .write = android_write, + .open = android_adb_open, + .release = android_adb_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = android_ioctl, +#else + .ioctl = android_ioctl, +#endif +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = android_compat_ioctl, +#endif +}; + +/* file operations for UMS/MTP device /dev/android_ums */ +static struct file_operations android_ums_fops = { + .owner = THIS_MODULE, + .read = android_read, + .write = android_write, + .open = android_ums_open, + .release = android_ums_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = android_ioctl, +#else + .ioctl = android_ioctl, +#endif +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = android_compat_ioctl, +#endif +}; + +static struct miscdevice android_adb_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "android_adb", + .fops = &android_adb_fops, +}; + +static struct miscdevice android_ums_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "android_ums", + .fops = &android_ums_fops, +}; + +static struct platform_driver android_driver = { + //.probe = android_probe, + .suspend = android_suspend, + .resume = android_resume, + .driver = { + .name = "android_driver" + } +}; + + +static int __init android_init(void) +{ + int ret; + printk("android: usb client driver v0.1\n"); + +#ifdef CONFIG_ANDROID_KDBG + // enable kdbg by default, so we can start debugging as soon as possible. + enable_kdbg = 1; +#endif + + gdata_temp = android_gadget_data_new(); + if (!gdata_temp) { + printk("android_gadget_data_new failed!\n"); + return -ENOMEM; + } + + ret = platform_driver_register(&android_driver); + if (ret < 0) { + printk("platform_driver_register returned %d\n", ret); + goto err1; + } + + android_platform_dev = platform_device_register_simple("android_driver", -1, NULL, 0); + if(IS_ERR(android_platform_dev)) { + ret = PTR_ERR(android_platform_dev); + printk("platform_device_register_simple returning %d\n", ret); + goto err2; + } + + if((ret = misc_register(&android_adb_device))) { + printk("misc_register for ADB device returned %d\n", ret); + goto err3; + } + if((ret = misc_register(&android_ums_device))) { + printk("misc_register for UMS device returned %d\n", ret); + goto err4; + } + + gdata_temp->thread = kthread_create(android_thread, gdata_temp, "android_gadget"); + if (IS_ERR(gdata_temp->thread)) + goto err5; + + wake_up_process(gdata_temp->thread); // start the thread so it can freeze + + /* Initialize strings to default values. These are overridden via ioctls */ + strcpy(manufacturer, "Google, Inc"); + strcpy(product, "Android 1.0"); + strcpy(serial, "0123456789ABCDEF"); + + /* initialize VID and PID to values that ADB will recognize */ + device_desc.idVendor = 0x18D1; + device_desc.idProduct = 0xD00D; + + return 0; + +err5: + misc_deregister(&android_ums_device); +err4: + misc_deregister(&android_adb_device); +err3: + platform_device_unregister(android_platform_dev); +err2: + platform_driver_unregister(&android_driver); +err1: + printk("android: usb gadget driver failed to initialize\n"); + return ret; +} + +static void __exit android_exit(void) +{ + printk("android_exit\n"); + + do_thread_command(gdata_temp, NULL, ANDROID_THREAD_QUIT, NULL); + + enable_usb(0); + + misc_deregister(&android_adb_device); + misc_deregister(&android_ums_device); + platform_device_unregister(android_platform_dev); + platform_driver_unregister(&android_driver); + usb_gadget_unregister_driver(&android_gadget_driver); + + android_gadget_data_free(gdata_temp); + gdata_temp = NULL; +} + +module_init(android_init); +module_exit(android_exit); diff -Nru linux-2.6.23/drivers/android/android_kernel_debug.c kernel.android/drivers/android/android_kernel_debug.c --- linux-2.6.23/drivers/android/android_kernel_debug.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/android_kernel_debug.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,706 @@ +/* drivers/android/android_kernel_debug.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 // linux/usb_gadget.h deos not compile without this +//#include +//#include +#include +#include +#include +#include +#include + +#include "android_kernel_debug.h" + +#ifdef DEBUG +#define DBG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define DBG(dev,fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +struct android_kdbg_request { + struct usb_request* req; + struct usb_ep* ep; + int buffer_size; + int status; +}; + +struct android_kdbg_command_state +{ + enum { + KDBG_UNKNOWN_STATE, + KDBG_NEW_COMMAND, + KDBG_COMMAND_IN_PROGRESS, + KDBG_REPEAT_COMMAND, + } state; + struct android_kernel_debug_command *cmd; + uint32_t parsed_arg; + size_t partial_string_written; + void *restart_ptr; +}; + +struct android_kernel_debug_command { + const char *command; + const char *short_help; + const char *long_help; + int (*func)(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size); +}; + +struct android_kdbg_data { + spinlock_t lock; + struct android_gadget_data* gdata; + struct android_kdbg_command_state command_state; + + struct usb_ep* bulk_in; + struct usb_ep* bulk_out; + struct android_kdbg_request bulk_in_req; + struct android_kdbg_request bulk_out_req; + u8 disconnected; /* used to force android_read() to fail */ +}; + + +#define bprintf(buffer, buffer_size, args...) do { \ + int len; \ + len = scnprintf(buffer, buffer_size, args); \ + buffer += len; \ + buffer_size -= len; \ + } while(0) + +static inline struct task_struct *eldest_child(struct task_struct *p) +{ + if (list_empty(&p->children)) + return NULL; + return list_entry(p->children.next,struct task_struct,sibling); +} + +static inline struct task_struct *older_sibling(struct task_struct *p) +{ + if (p->sibling.prev==&p->parent->children) + return NULL; + return list_entry(p->sibling.prev,struct task_struct,sibling); +} + +static inline struct task_struct *younger_sibling(struct task_struct *p) +{ + if (p->sibling.next==&p->parent->children) + return NULL; + return list_entry(p->sibling.next,struct task_struct,sibling); +} + +static int android_kernel_debug_show_task(struct task_struct *p, char *buffer, size_t buffer_size) +{ + struct task_struct *relative; + unsigned state; + char *bp = buffer; + static const char stat_nam[] = "RSDTtZX"; + + state = p->state ? __ffs(p->state) + 1 : 0; + bprintf(bp, buffer_size, "%-13.13s %c", p->comm, + state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'); +#if (BITS_PER_LONG == 32) + if (state == TASK_RUNNING) + bprintf(bp, buffer_size, " running "); + else + bprintf(bp, buffer_size, " %08lX ", thread_saved_pc(p)); +#else + if (state == TASK_RUNNING) + bprintf(bp, buffer_size, " running task "); + else + bprintf(bp, buffer_size, " %016lx ", thread_saved_pc(p)); +#endif + bprintf(bp, buffer_size, "%3d %5d %6d ", p->prio, p->pid, p->parent->pid); + if ((relative = eldest_child(p))) + bprintf(bp, buffer_size, "%5d ", relative->pid); + else + bprintf(bp, buffer_size, " "); + if ((relative = younger_sibling(p))) + bprintf(bp, buffer_size, "%7d", relative->pid); + else + bprintf(bp, buffer_size, " "); + if ((relative = older_sibling(p))) + bprintf(bp, buffer_size, " %5d", relative->pid); + else + bprintf(bp, buffer_size, " "); + if (!p->mm) + bprintf(bp, buffer_size, " (L-TLB)\n"); + else + bprintf(bp, buffer_size, " (NOTLB)\n"); + + //if (state != TASK_RUNNING) + // show_stack(p, NULL); + return bp - buffer; +} + +#define SHOW_TASK_RUNNING (1UL << 31) + +static int android_kernel_debug_showstate(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size) +{ + int len; + struct task_struct *g, *p; + char *bp = buffer; + int invert; + long run_state; + + if(state->state == KDBG_NEW_COMMAND) { + state->parsed_arg = 0; + invert = 0; + while(arg && *arg) { + switch(*arg) { + case '-': + invert = 1; + break; + case 'r': + state->parsed_arg |= SHOW_TASK_RUNNING; + break; + case 's': + state->parsed_arg |= TASK_INTERRUPTIBLE; + break; + case 'd': + state->parsed_arg |= TASK_UNINTERRUPTIBLE; + break; + case 'S': + state->parsed_arg |= TASK_STOPPED; + break; + case 't': + state->parsed_arg |= TASK_TRACED; + break; + case 'z': + state->parsed_arg |= EXIT_ZOMBIE; + break; + case 'x': + state->parsed_arg |= EXIT_DEAD; + break; + default: + bprintf(bp, buffer_size, "unknown arg %c\n", *arg); + return bp - buffer; + } + arg++; + } + if(invert || state->parsed_arg == 0) + state->parsed_arg = ~state->parsed_arg; + } + if(state->state != KDBG_COMMAND_IN_PROGRESS) { +#if (BITS_PER_LONG == 32) + bprintf(bp, buffer_size, "\n" + " sibling\n" + " task PC prio pid father child younger older\n"); +#else + bprintf(bp, buffer_size, "\n" + " sibling\n" + " task PC prio pid father child younger older\n"); +#endif + } + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if(state->state == KDBG_COMMAND_IN_PROGRESS) { + if(state->restart_ptr != p) { + continue; + } + bprintf(bp, buffer_size, "restart from %p\n", state->restart_ptr); + state->state = KDBG_UNKNOWN_STATE; + } + run_state = p->state & ~SHOW_TASK_RUNNING; + if(run_state == 0) + run_state |= SHOW_TASK_RUNNING; + if((run_state & state->parsed_arg) == 0) + continue; + len = android_kernel_debug_show_task(p, bp, buffer_size); + bp += len; + buffer_size -= len; + if(buffer_size <= 1) { + printk("android_kernel_debug_showstate: buffer full %d, %d left\n", len, buffer_size); + state->state = KDBG_COMMAND_IN_PROGRESS; + state->partial_string_written = len; + state->restart_ptr = p; + goto buffer_full; + } + } while_each_thread(g, p); + if(state->state == KDBG_COMMAND_IN_PROGRESS) { + bprintf(bp, buffer_size, "could not restart from %p\n", state->restart_ptr); + state->state = KDBG_UNKNOWN_STATE; + } + else { + bprintf(bp, buffer_size, "\n"); + } +buffer_full: + read_unlock(&tasklist_lock); + //debug_show_all_locks(); + return bp - buffer; +} + + +static int android_kernel_debug_thread(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size) +{ + int len; + struct task_struct *task = current; + if(arg) { + int pid = simple_strtol(arg, NULL, 0); + task = find_task_by_pid(pid); + if(task == NULL) { + len = snprintf(buffer, buffer_size, "no thread found with pid %d\n", pid); + return len; + } + } + + + len = snprintf(buffer, buffer_size, + "pid %d\n" + "comm %s\n" + "state %lx\n" + "flags %lx\n" + "prio %d\n" + "static_prio %d\n" + "normal_prio %d\n" + , + task->pid, + task->comm, + task->state, + task->flags, + task->prio, + task->static_prio, + task->normal_prio + ); + return len; +} + +static int android_kernel_debug_kill(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size) +{ + int len; + int res; + int pid; + int sig = SIGTERM; + const char *arg_done = arg; + + if(arg) { + if(*arg == '-') { + arg++; + sig = simple_strtol(arg, &arg, 0); + while(isspace(*arg)) + arg++; + } + pid = simple_strtol(arg, &arg_done, 0); + } + if(arg == arg_done) { + len = snprintf(buffer, buffer_size, "missing pid argument\n"); + return len; + } + res = kill_proc(pid, sig, 1); + if(res) { + len = snprintf(buffer, buffer_size, + "kill pid %d sig %d failed, %d\n", + pid, sig, res); + return len; + } + len = snprintf(buffer, buffer_size, + "sent sig %d to %d\n", sig, pid); + return len; +} + +static int android_kernel_debug_echo(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size) +{ + int add_newline = 1; + size_t echolen; + + if(arg == NULL) { + buffer[0] = '\n'; + return 1; + } + + if(arg[0] == '-' && arg[1] == 'n') { + add_newline = 0; + arg += 2; + while(isspace(*arg)) + arg++; + } + echolen = strlen(arg); + if(arg[0] == '"') { + if(echolen > 1 && arg[echolen - 1] == '"') { + arg++; + echolen -= 2; + } + } + if(echolen > buffer_size) { + printk("android_kernel_debug_echo reply buffer should not be smaller than command buffer\n"); + echolen = buffer_size; + } + memcpy(buffer, arg, echolen); + if(add_newline && echolen < buffer_size - 1) { + buffer[echolen] = '\n'; + echolen++; + } + return echolen; +} + +static int android_kernel_debug_reboot(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size) +{ + kernel_restart(NULL); + return 0; +} + + +static int android_kernel_debug_help(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size); + +static struct android_kernel_debug_command android_kernel_debug_commands[] = { + { + .command = "help", + .short_help = "[command] - show list of command, or long help", + .func = android_kernel_debug_help + }, + { + .command = "thread", + .short_help = "[pid] - show thread information", + .long_help = + "pid = thread to show information for\n" + " uses current running thread if not specified\n", + .func = android_kernel_debug_thread + }, + { + .command = "kill", + .short_help = "[-s signum] - kill", + .func = android_kernel_debug_kill + }, + { + .command = "ps", + .short_help = "[-][rsdStzx] - show system information", + .func = android_kernel_debug_showstate + }, + { + .command = "echo", + .short_help = "[string] - print string or all arguments", + .func = android_kernel_debug_echo + }, + { + .command = "reboot", + .short_help = "- reboot system (without sync)", + .func = android_kernel_debug_reboot + } +}; + +static int android_kernel_debug_help(struct android_kdbg_command_state *state, const char *arg, char *buffer, size_t buffer_size) +{ + int i; + int len; + char *bp = buffer; + struct android_kernel_debug_command *cmd; + + for(i = 0; i < ARRAY_SIZE(android_kernel_debug_commands); i++) { + cmd = &android_kernel_debug_commands[i]; + if(!arg) { + len = snprintf(bp, buffer_size, "%s %s\n", cmd->command, cmd->short_help); + bp += len; + buffer_size -= len; + } + else if(strcmp(arg, cmd->command) == 0) { + len = snprintf(bp, buffer_size, "%s %s\n%s", + cmd->command, cmd->short_help, + cmd->long_help ? cmd->long_help : ""); + bp += len; + buffer_size -= len; + } + } + return bp - buffer; +} + +static void android_kernel_debug_write_done(struct android_kdbg_data* fdata) +{ + int err; + char *response = fdata->bulk_in_req.req->buf; + size_t response_bufsize = fdata->bulk_in_req.buffer_size - fdata->bulk_in->maxpacket + 1; // + size_t response_len = 0; + + printk("android_kernel_debug_write_done: state %d\n", fdata->command_state.state); + if(fdata->command_state.state != KDBG_COMMAND_IN_PROGRESS) + return; + + response_len = fdata->command_state.cmd->func(&fdata->command_state, NULL, response, response_bufsize); + + fdata->bulk_in_req.req->length = response_len; + fdata->bulk_in_req.req->zero = (fdata->command_state.state != KDBG_COMMAND_IN_PROGRESS); + + printk("android_kernel_debug_write_done: usb_ep_queue len %d\n", fdata->bulk_in_req.req->length); + + fdata->bulk_in_req.status = 1; // busy + err = usb_ep_queue(fdata->bulk_in_req.ep, fdata->bulk_in_req.req, GFP_ATOMIC); + if (err) { + printk("android_kernel_debug_write_done: usb_ep_queue failed %d\n", err); + } +} + +static void android_kernel_debug_command(struct android_kdbg_data* fdata, char *command, size_t len) +{ + int err; + int i; + char *response = fdata->bulk_in_req.req->buf; + size_t response_bufsize = fdata->bulk_in_req.buffer_size - fdata->bulk_in->maxpacket + 1; + size_t response_len = 0; + struct android_kernel_debug_command *cmd; + char *args; + + printk("android_kernel_debug_command %p %d\n", command, len); + + while(len && isspace(command[len - 1])) { + len--; + } + command[len] = '\0'; + args = strchr(command, ' '); + if(args) { + len = args - command; + command[len] = '\0'; + args++; + } + + if(fdata->command_state.state == KDBG_COMMAND_IN_PROGRESS) { + printk("android_kernel_debug_command: abort command: %s\n", fdata->command_state.cmd->command); + fdata->command_state.state = KDBG_UNKNOWN_STATE; + return; + } + + if(fdata->bulk_in_req.status) { + printk("android_kernel_debug_command: bulk_in_req is still in use\n"); + return; + } + + if(command[0] == '\0' && fdata->command_state.cmd) { + fdata->command_state.state = KDBG_REPEAT_COMMAND; + response_len = fdata->command_state.cmd->func(&fdata->command_state, args, response, response_bufsize); + } + else { + for(i = 0; i < ARRAY_SIZE(android_kernel_debug_commands); i++) { + cmd = &android_kernel_debug_commands[i]; + if(strcmp(cmd->command, command) == 0) { + fdata->command_state.state = KDBG_NEW_COMMAND; + fdata->command_state.cmd = cmd; + response_len = cmd->func(&fdata->command_state, args, response, response_bufsize); + break; + } + } + if(i == ARRAY_SIZE(android_kernel_debug_commands)) { + response_len = snprintf(response, response_bufsize, + "Unknown command %s, type help for a list of commands\n", command); + } + } + + fdata->bulk_in_req.req->length = response_len; + fdata->bulk_in_req.req->zero = (fdata->command_state.state != KDBG_COMMAND_IN_PROGRESS); + + printk("android_kernel_debug_command: usb_ep_queue len %d\n", fdata->bulk_in_req.req->length); + + fdata->bulk_in_req.status = 1; // busy + err = usb_ep_queue(fdata->bulk_in_req.ep, fdata->bulk_in_req.req, GFP_ATOMIC); + if (err) { + printk("android_kernel_debug_command: usb_ep_queue failed %d\n", err); + } +} + +/* completion routine for bulk reads and writes */ +static void request_in_complete(struct usb_ep* ep, struct usb_request* usb_req) +{ + struct android_kdbg_data* fdata = ep->driver_data; + struct android_kdbg_request *req = usb_req->context; + if (!req) + return; + + printk("kdbg request_in_complete status %d actual %d, len %d\n", usb_req->status, usb_req->actual, req->req->length); + req->status = 0; // avaliable + if(fdata->disconnected) { + printk("kdbg request_in_complete disconnected\n"); + return; + } + android_kernel_debug_write_done(fdata); +} + +static void request_out_complete(struct usb_ep* ep, struct usb_request* usb_req) +{ + int err; + struct android_kdbg_data* fdata = ep->driver_data; + struct android_kdbg_request *req = usb_req->context; + if (!req) + return; + + if (usb_req->status) + req->status = usb_req->status; + else + req->status = usb_req->actual; + + printk("kdbg request_out_complete %d\n", req->status); + if(fdata->disconnected) { + printk("kdbg request_out_complete disconnected\n"); + return; + } + + if(usb_req->status == 0) { + android_kernel_debug_command(fdata, req->req->buf, usb_req->actual); + } + + req->req->length = req->buffer_size; + err = usb_ep_queue(req->ep, req->req, GFP_ATOMIC); + printk("new out request %d\n", err); +} + +struct android_kdbg_data* android_kdbg_data_new(struct android_gadget_data* gdata) +{ + struct android_kdbg_data* data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->gdata = gdata; + spin_lock_init(&data->lock); + return data; +} + +void android_kdbg_data_free(struct android_kdbg_data* data) +{ +// BUG_ON(waitqueue_active(&data->wait)); + kfree(data); +} + + +static int android_kdbg_request_init(struct usb_ep* ep, int buffer_size, struct android_kdbg_request *req, int is_out) +{ + req->req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req->req) + { + kfree(req); + return -ENOMEM; + } + + /* now allocate buffers for the requests */ + req->req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->req->buf) + { + usb_ep_free_request(ep, req->req); + kfree(req); + return -ENOMEM; + } + +// init_completion(&req->complete); + + req->ep = ep; + req->buffer_size = buffer_size; + req->req->complete = is_out ? request_out_complete : request_in_complete; + req->req->context = req; + + return 0; +} + +static void android_kdbg_request_uninit(struct android_kdbg_request *req) +{ + if (req->req) + { + if (req->req->buf) + kfree(req->req->buf); + + usb_ep_free_request(req->ep, req->req); + req->req = NULL; + } +} + +int enable_kdbg_bulk_endpoints(struct android_kdbg_data* fdata, const struct usb_endpoint_descriptor *in_desc, const struct usb_endpoint_descriptor *out_desc) +{ + int result; + + fdata->disconnected = 0; + + result = usb_ep_enable(fdata->bulk_in, in_desc); + if (result) { + printk("enable_kdbg_bulk_endpoints: usb_ep_enable failed %d\n", result); + return result; + } + result = usb_ep_enable(fdata->bulk_out, out_desc); + if (result) { + printk("enable_kdbg_bulk_endpoints: usb_ep_enable failed %d\n", result); + return result; + } + + printk("enable_kdbg_bulk_endpoints\n"); + fdata->bulk_in_req.status = 0; + fdata->bulk_out_req.req->length = fdata->bulk_out_req.buffer_size; + + result = usb_ep_queue(fdata->bulk_out_req.ep, fdata->bulk_out_req.req, GFP_ATOMIC); + if (result) { + printk("enable_kdbg_bulk_endpoints: usb_ep_queue failed %d\n", result); + return result; + } + + return 0; +} + +void disable_kdbg_bulk_endpoints(struct android_kdbg_data* fdata) +{ + int err; + printk("disable_kdbg_bulk_endpoints (bulk in status %d)\n", fdata->bulk_in_req.status); + fdata->disconnected = 1; + if(fdata->bulk_in_req.status) { + err = usb_ep_dequeue(fdata->bulk_in_req.ep, fdata->bulk_in_req.req); + printk("disable_kdbg_bulk_endpoints usb_ep_dequeue returned %d (bulk in status %d\n", err, fdata->bulk_in_req.status); + } + + usb_ep_disable(fdata->bulk_in); + usb_ep_disable(fdata->bulk_out); +} + + +int create_android_kdbg_endpoints(struct usb_gadget *gadget, struct android_kdbg_data* fdata, + int buffer_size, + struct usb_endpoint_descriptor* in_desc, + struct usb_endpoint_descriptor* out_desc) +{ + int err; + struct usb_ep* ep; + + ep = usb_ep_autoconfig(gadget, in_desc); + if (!ep) + { + DBG(fdata->gdata, "usb_ep_autoconfig for bulk_in failed\n"); + return -ENODEV; + } + DBG(fdata->gdata, "usb_ep_autoconfig for bulk_in got %s\n", ep->name); + ep->driver_data = fdata; + fdata->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, out_desc); + if (!ep) + { + DBG(fdata->gdata, "usb_ep_autoconfig for bulk_out failed\n"); + return -ENODEV; + } + DBG(fdata->gdata, "usb_ep_autoconfig for adb bulk_out got %s\n", ep->name); + ep->driver_data = fdata; + fdata->bulk_out = ep; + + /* now allocate requests for our endpoints */ + err = android_kdbg_request_init(fdata->bulk_in, buffer_size, &fdata->bulk_in_req, 0); + if(err) + return err; + + err = android_kdbg_request_init(fdata->bulk_out, buffer_size, &fdata->bulk_out_req, 1); + if(err) + return err; + + return 0; +} + +void free_android_kdbg_endpoints(struct android_kdbg_data* fdata) +{ + android_kdbg_request_uninit(&fdata->bulk_in_req); + android_kdbg_request_uninit(&fdata->bulk_out_req); +} + + diff -Nru linux-2.6.23/drivers/android/android_kernel_debug.h kernel.android/drivers/android/android_kernel_debug.h --- linux-2.6.23/drivers/android/android_kernel_debug.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/android_kernel_debug.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,32 @@ +/* drivers/android/android_kernel_debug.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef ANDROID_KERNEL_DEBUG_H +#define ANDROID_KERNEL_DEBUG_H + +struct android_kdbg_data; +struct android_gadget_data; + +struct android_kdbg_data* android_kdbg_data_new(struct android_gadget_data* gdata); +void android_kdbg_data_free(struct android_kdbg_data* data); +int enable_kdbg_bulk_endpoints(struct android_kdbg_data* fdata, const struct usb_endpoint_descriptor *in_desc, const struct usb_endpoint_descriptor *out_desc); +void disable_kdbg_bulk_endpoints(struct android_kdbg_data* fdata); +int create_android_kdbg_endpoints(struct usb_gadget *gadget, struct android_kdbg_data* fdata, + int buffer_size, + struct usb_endpoint_descriptor* in_desc, + struct usb_endpoint_descriptor* out_desc); +void free_android_kdbg_endpoints(struct android_kdbg_data* fdata); + +#endif diff -Nru linux-2.6.23/drivers/android/logger.c kernel.android/drivers/android/logger.c --- linux-2.6.23/drivers/android/logger.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/logger.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,605 @@ +/* drivers/android/logger.c +** +** Android Logging Subsystem +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +/* + * struct logger_log - represents a specific log, such as 'main' or 'radio' + * + * This structure lives from module insertion until module removal, so it does + * not need additional reference counting. The structure is protected by the + * mutex 'mutex'. + */ +struct logger_log { + unsigned char * buffer; /* the ring buffer itself */ + struct miscdevice misc; /* misc device representing the log */ + wait_queue_head_t wq; /* wait queue for readers */ + struct list_head readers; /* this log's readers */ + struct mutex mutex; /* mutex protecting buffer */ + size_t w_off; /* current write head offset */ + size_t head; /* new readers start here */ + size_t size; /* size of the log */ +}; + +/* + * struct logger_reader - a logging device open for reading + * + * This object lives from open to release, so we don't need additional + * reference counting. The structure is protected by log->mutex. + */ +struct logger_reader { + struct logger_log * log; /* associated log */ + struct list_head list; /* entry in logger_log's list */ + size_t r_off; /* current read head offset */ +}; + +/* logger_offset - returns index 'n' into the log via (optimized) modulus */ +#define logger_offset(n) ((n) & (log->size - 1)) + +/* + * file_get_log - Given a file structure, return the associated log + * + * This isn't aesthetic. We have several goals: + * + * 1) Need to quickly obtain the associated log during an I/O operation + * 2) Readers need to maintain state (logger_reader) + * 3) Writers need to be very fast (open() should be a near no-op) + * + * In the reader case, we can trivially go file->logger_reader->logger_log. + * For a writer, we don't want to maintain a logger_reader, so we just go + * file->logger_log. Thus what file->private_data points at depends on whether + * or not the file was opened for reading. This function hides that dirtiness. + */ +static inline struct logger_log * file_get_log(struct file *file) +{ + if (file->f_mode & FMODE_READ) { + struct logger_reader *reader = file->private_data; + return reader->log; + } else + return file->private_data; +} + +/* + * get_entry_len - Grabs the length of the payload of the next entry starting + * from 'off'. + * + * Caller needs to hold log->mutex. + */ +static __u32 get_entry_len(struct logger_log *log, size_t off) +{ + __u16 val; + + switch (log->size - off) { + case 1: + memcpy(&val, log->buffer + off, 1); + memcpy(((char *) &val) + 1, log->buffer, 1); + break; + default: + memcpy(&val, log->buffer + off, 2); + } + + return sizeof(struct logger_entry) + val; +} + +/* + * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the + * user-space buffer 'buf'. Returns 'count' on success. + * + * Caller must hold log->mutex. + */ +static ssize_t do_read_log_to_user(struct logger_log *log, + struct logger_reader *reader, + char __user *buf, + size_t count) +{ + size_t len; + + /* + * We read from the log in two disjoint operations. First, we read from + * the current read head offset up to 'count' bytes or to the end of + * the log, whichever comes first. + */ + len = min(count, log->size - reader->r_off); + if (copy_to_user(buf, log->buffer + reader->r_off, len)) + return -EFAULT; + + /* + * Second, we read any remaining bytes, starting back at the head of + * the log. + */ + if (count != len) + if (copy_to_user(buf + len, log->buffer, count - len)) + return -EFAULT; + + reader->r_off = logger_offset(reader->r_off + count); + + return count; +} + +/* + * logger_read - our log's read() method + * + * Behavior: + * + * - O_NONBLOCK works + * - If there are no log entries to read, blocks until log is written to + * - Atomically reads exactly one log entry + * + * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read + * buffer is insufficient to hold next entry. + */ +static ssize_t logger_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct logger_reader *reader = file->private_data; + struct logger_log *log = reader->log; + ssize_t ret; + DEFINE_WAIT(wait); + +start: + while (1) { + prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE); + + mutex_lock(&log->mutex); + ret = (log->w_off == reader->r_off); + mutex_unlock(&log->mutex); + if (!ret) + break; + + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + if (signal_pending(current)) { + ret = -EINTR; + break; + } + + schedule(); + } + + finish_wait(&log->wq, &wait); + if (ret) + return ret; + + mutex_lock(&log->mutex); + + /* is there still something to read or did we race? */ + if (unlikely(log->w_off == reader->r_off)) { + mutex_unlock(&log->mutex); + goto start; + } + + /* get the size of the next entry */ + ret = get_entry_len(log, reader->r_off); + if (count < ret) { + ret = -EINVAL; + goto out; + } + + /* get exactly one entry from the log */ + ret = do_read_log_to_user(log, reader, buf, ret); + +out: + mutex_unlock(&log->mutex); + + return ret; +} + +/* + * get_next_entry - return the offset of the first valid entry at least 'len' + * bytes after 'off'. + * + * Caller must hold log->mutex. + */ +static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) +{ + size_t count = 0; + + do { + size_t nr = get_entry_len(log, off); + off = logger_offset(off + nr); + count += nr; + } while (count < len); + + return off; +} + +/* + * clock_interval - is a < c < b in mod-space? Put another way, does the line + * from a to b cross c? + */ +static inline int clock_interval(size_t a, size_t b, size_t c) +{ + if (b < a) { + if (a < c || b >= c) + return 1; + } else { + if (a < c && b >= c) + return 1; + } + + return 0; +} + +/* + * fix_up_readers - walk the list of all readers and "fix up" any who were + * lapped by the writer; also do the same for the default "start head". + * We do this by "pulling forward" the readers and start head to the first + * entry after the new write head. + * + * The caller needs to hold log->mutex. + */ +static void fix_up_readers(struct logger_log *log, size_t len) +{ + size_t old = log->w_off; + size_t new = logger_offset(old + len); + struct logger_reader *reader; + + if (clock_interval(old, new, log->head)) + log->head = get_next_entry(log, log->head, len); + + list_for_each_entry(reader, &log->readers, list) + if (clock_interval(old, new, reader->r_off)) + reader->r_off = get_next_entry(log, reader->r_off, len); +} + +/* + * do_write_log - writes 'len' bytes from 'buf' to 'log' + * + * The caller needs to hold log->mutex. + */ +static void do_write_log(struct logger_log *log, const void *buf, size_t count) +{ + size_t len; + + len = min(count, log->size - log->w_off); + memcpy(log->buffer + log->w_off, buf, len); + + if (count != len) + memcpy(log->buffer, buf + len, count - len); + + log->w_off = logger_offset(log->w_off + count); + +} + +/* + * do_write_log_user - writes 'len' bytes from the user-space buffer 'buf' to + * the log 'log' + * + * The caller needs to hold log->mutex. + * + * Returns 'count' on success, negative error code on failure. + */ +static ssize_t do_write_log_from_user(struct logger_log *log, + const void __user *buf, size_t count) +{ + size_t len; + + len = min(count, log->size - log->w_off); + if (len && copy_from_user(log->buffer + log->w_off, buf, len)) + return -EFAULT; + + if (count != len) + if (copy_from_user(log->buffer, buf + len, count - len)) + return -EFAULT; + + log->w_off = logger_offset(log->w_off + count); + + return count; +} + +/* + * logger_aio_write - our write method, implementing support for write(), + * writev(), and aio_write(). Writes are our fast path, and we try to optimize + * them above all else. + */ +ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t ppos) +{ + struct logger_log *log = file_get_log(iocb->ki_filp); + size_t orig = log->w_off; + struct logger_entry header; + struct timespec now; + ssize_t ret = 0; + + now = current_kernel_time(); + + header.pid = current->tgid; + header.tid = current->pid; + header.sec = now.tv_sec; + header.nsec = now.tv_nsec; + header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); + + /* null writes succeed, return zero */ + if (unlikely(!header.len)) + return 0; + + mutex_lock(&log->mutex); + + /* + * Fix up any readers, pulling them forward to the first readable + * entry after (what will be) the new write offset. We do this now + * because if we partially fail, we can end up with clobbered log + * entries that encroach on readable buffer. + */ + fix_up_readers(log, sizeof(struct logger_entry) + header.len); + + do_write_log(log, &header, sizeof(struct logger_entry)); + + while (nr_segs-- > 0) { + size_t len; + ssize_t nr; + + /* figure out how much of this vector we can keep */ + len = min_t(size_t, iov->iov_len, header.len - ret); + + /* write out this segment's payload */ + nr = do_write_log_from_user(log, iov->iov_base, len); + if (unlikely(nr < 0)) { + log->w_off = orig; + mutex_unlock(&log->mutex); + return nr; + } + + iov++; + ret += nr; + } + + mutex_unlock(&log->mutex); + + /* wake up any blocked readers */ + wake_up_interruptible(&log->wq); + + return ret; +} + +static struct logger_log * get_log_from_minor(int); + +/* + * logger_open - the log's open() file operation + * + * Note how near a no-op this is in the write-only case. Keep it that way! + */ +static int logger_open(struct inode *inode, struct file *file) +{ + struct logger_log *log; + int ret; + + ret = nonseekable_open(inode, file); + if (ret) + return ret; + + log = get_log_from_minor(MINOR(inode->i_rdev)); + if (!log) + return -ENODEV; + + if (file->f_mode & FMODE_READ) { + struct logger_reader *reader; + + reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL); + if (!reader) + return -ENOMEM; + + reader->log = log; + INIT_LIST_HEAD(&reader->list); + + mutex_lock(&log->mutex); + reader->r_off = log->head; + list_add_tail(&reader->list, &log->readers); + mutex_unlock(&log->mutex); + + file->private_data = reader; + } else + file->private_data = log; + + return 0; +} + +/* + * logger_release - the log's release file operation + * + * Note this is a total no-op in the write-only case. Keep it that way! + */ +static int logger_release(struct inode *ignored, struct file *file) +{ + if (file->f_mode & FMODE_READ) { + struct logger_reader *reader = file->private_data; + list_del(&reader->list); + kfree(reader); + } + + return 0; +} + +/* + * logger_poll - the log's poll file operation, for poll/select/epoll + * + * Note we always return POLLOUT, because you can always write() to the log. + * Note also that, strictly speaking, a return value of POLLIN does not + * guarantee that the log is readable without blocking, as there is a small + * chance that the writer can lap the reader in the interim between poll() + * returning and the read() request. + */ +static unsigned int logger_poll(struct file *file, poll_table *wait) +{ + struct logger_reader *reader; + struct logger_log *log; + unsigned int ret = POLLOUT | POLLWRNORM; + + if (!(file->f_mode & FMODE_READ)) + return ret; + + reader = file->private_data; + log = reader->log; + + poll_wait(file, &log->wq, wait); + + mutex_lock(&log->mutex); + if (log->w_off != reader->r_off) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&log->mutex); + + return ret; +} + +static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct logger_log *log = file_get_log(file); + struct logger_reader *reader; + long ret = -ENOTTY; + + mutex_lock(&log->mutex); + + switch (cmd) { + case LOGGER_GET_LOG_BUF_SIZE: + ret = log->size; + break; + case LOGGER_GET_LOG_LEN: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + if (log->w_off >= reader->r_off) + ret = log->w_off - reader->r_off; + else + ret = (log->size - reader->r_off) + log->w_off; + break; + case LOGGER_GET_NEXT_ENTRY_LEN: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + if (log->w_off != reader->r_off) + ret = get_entry_len(log, reader->r_off); + else + ret = 0; + break; + case LOGGER_FLUSH_LOG: + if (!(file->f_mode & FMODE_WRITE)) { + ret = -EBADF; + break; + } + list_for_each_entry(reader, &log->readers, list) + reader->r_off = log->w_off; + log->head = log->w_off; + ret = 0; + break; + } + + mutex_unlock(&log->mutex); + + return ret; +} + +static struct file_operations logger_fops = { + .owner = THIS_MODULE, + .read = logger_read, + .aio_write = logger_aio_write, + .poll = logger_poll, + .unlocked_ioctl = logger_ioctl, + .compat_ioctl = logger_ioctl, + .open = logger_open, + .release = logger_release, +}; + +/* + * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which + * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than + * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. + */ +#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \ +static unsigned char _buf_ ## VAR[SIZE]; \ +static struct logger_log VAR = { \ + .buffer = _buf_ ## VAR, \ + .misc = { \ + .minor = MISC_DYNAMIC_MINOR, \ + .name = NAME, \ + .fops = &logger_fops, \ + .parent = NULL, \ + }, \ + .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \ + .readers = LIST_HEAD_INIT(VAR .readers), \ + .mutex = __MUTEX_INITIALIZER(VAR .mutex), \ + .w_off = 0, \ + .head = 0, \ + .size = SIZE, \ +}; + +DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024) +DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 64*1024) +DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024) + +static struct logger_log * get_log_from_minor(int minor) +{ + if (log_main.misc.minor == minor) + return &log_main; + if (log_events.misc.minor == minor) + return &log_events; + if (log_radio.misc.minor == minor) + return &log_radio; + return NULL; +} + +static int __init init_log(struct logger_log *log) +{ + int ret; + + ret = misc_register(&log->misc); + if (unlikely(ret)) { + printk(KERN_ERR "logger: failed to register misc " + "device for log '%s'!\n", log->misc.name); + return ret; + } + + printk(KERN_INFO "logger: created %luK log '%s'\n", + (unsigned long) log->size >> 10, log->misc.name); + + return 0; +} + +static int __init logger_init(void) +{ + int ret; + + ret = init_log(&log_main); + if (unlikely(ret)) + goto out; + + ret = init_log(&log_events); + if (unlikely(ret)) + goto out; + + ret = init_log(&log_radio); + if (unlikely(ret)) + goto out; + +out: + return ret; +} +device_initcall(logger_init); diff -Nru linux-2.6.23/drivers/android/power.c kernel.android/drivers/android/power.c --- linux-2.6.23/drivers/android/power.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/power.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,1331 @@ +/* drivers/android/power.c +** +** Copyright (C) 2005-2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 +#include +#include +#include +#include // sys_sync +#ifdef CONFIG_ANDROID_POWER_STAT +#include +#endif + +#define ANDROID_POWER_TEST_EARLY_SUSPEND 0 +#define ANDROID_POWER_PRINT_USER_WAKE_LOCKS 0 + +MODULE_DESCRIPTION("OMAP CSMI Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static spinlock_t g_list_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_MUTEX(g_early_suspend_lock); + +wait_queue_head_t g_wait_queue; + +static LIST_HEAD(g_inactive_locks); +static LIST_HEAD(g_active_partial_wake_locks); +static LIST_HEAD(g_active_full_wake_locks); +static LIST_HEAD(g_early_suspend_handlers); +static enum { + USER_AWAKE, + USER_NOTIFICATION, + USER_SLEEP +} g_user_suspend_state; +static ktime_t g_user_suspend_state_changed; +static int g_current_event_num; +static ktime_t g_auto_off_timeout; +static ktime_t g_last_user_activity; +static ktime_t g_last_notification; +static ktime_t g_notification_timeout; +static struct hrtimer g_auto_off_timer; +static struct workqueue_struct *g_suspend_work_queue; +static void android_power_suspend(struct work_struct *work); +static DECLARE_WORK(g_suspend_work, android_power_suspend); +static int g_max_user_lockouts = 16; +//static const char g_free_user_lockout_name[] = "free_user"; +static struct { + enum { + USER_WAKE_LOCK_INACTIVE, + USER_WAKE_LOCK_PARTIAL, + USER_WAKE_LOCK_FULL + } state; + android_suspend_lock_t suspend_lock; + char name_buffer[32]; +} *g_user_wake_locks; +#ifdef CONFIG_ANDROID_POWER_STAT +android_suspend_lock_t g_deleted_wake_locks; +#endif +static int g_battery_level; +static int g_battery_level_low; +static int g_battery_level_scale = 5; +static int g_battery_low_level = 10; +static int g_battery_shutdown_level = 5; +static android_charging_state_t g_charging_state = ANDROID_CHARGING_STATE_UNKNOWN; + +static void update_battery_low_level(void); + +#if 0 +android_suspend_lock_t *android_allocate_suspend_lock(const char *debug_name) +{ + unsigned long irqflags; + struct android_power *e; + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if(e == NULL) { + printk("android_power_allocate: kzalloc failed\n"); + return NULL; + } + e->name = debug_name; + spin_lock_irqsave(&g_list_lock, irqflags); + list_add(&e->link, &g_allocated); + spin_unlock_irqrestore(&g_list_lock, irqflags); + return e; +} +#endif + +int android_init_suspend_lock(android_suspend_lock_t *lock) +{ + unsigned long irqflags; + + if(lock->name == NULL) { + printk("android_init_suspend_lock: error name=NULL, lock=%p\n", lock); + return -EINVAL; + } + + //printk("android_init_suspend_lock name=%s\n", lock->name); +#ifdef CONFIG_ANDROID_POWER_STAT + lock->stat.count = 0; + lock->stat.expire_count = 0; + lock->stat.total_time = ktime_set(0, 0); + lock->stat.max_time = ktime_set(0, 0); + lock->stat.last_time = ktime_set(0, 0); +#endif + lock->flags = 0; + + INIT_LIST_HEAD(&lock->link); + spin_lock_irqsave(&g_list_lock, irqflags); + list_add(&lock->link, &g_inactive_locks); + spin_unlock_irqrestore(&g_list_lock, irqflags); +// if(lock->flags & ANDROID_SUSPEND_LOCK_FLAG_USER_VISIBLE_MASK) { +// sysfs_create_file(struct kobject * k, const struct attribute * a) +// } + return 0; +} + +void android_uninit_suspend_lock(android_suspend_lock_t *lock) +{ + unsigned long irqflags; + //printk("android_uninit_suspend_lock name=%s\n", lock->name); + spin_lock_irqsave(&g_list_lock, irqflags); +#ifdef CONFIG_ANDROID_POWER_STAT + if(lock->stat.count) { + if(g_deleted_wake_locks.stat.count == 0) { + android_init_suspend_lock(&g_deleted_wake_locks); + } + g_deleted_wake_locks.stat.count += lock->stat.count; + g_deleted_wake_locks.stat.expire_count += lock->stat.expire_count; + g_deleted_wake_locks.stat.total_time = ktime_add(g_deleted_wake_locks.stat.total_time, lock->stat.total_time); + g_deleted_wake_locks.stat.max_time = ktime_add(g_deleted_wake_locks.stat.max_time, lock->stat.max_time); + } +#endif + list_del(&lock->link); + spin_unlock_irqrestore(&g_list_lock, irqflags); +} + +void android_lock_suspend(android_suspend_lock_t *lock) +{ + unsigned long irqflags; + //printk("android_lock_suspend name=%s\n", lock->name); + spin_lock_irqsave(&g_list_lock, irqflags); +#ifdef CONFIG_ANDROID_POWER_STAT + if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) { + lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE; + lock->stat.last_time = ktime_get(); + } +#endif + lock->expires = INT_MAX; + lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE; + list_del(&lock->link); + list_add(&lock->link, &g_active_partial_wake_locks); + g_current_event_num++; + spin_unlock_irqrestore(&g_list_lock, irqflags); +} + +void android_lock_suspend_auto_expire(android_suspend_lock_t *lock, int timeout) +{ + unsigned long irqflags; + //printk("android_lock_suspend name=%s\n", lock->name); + spin_lock_irqsave(&g_list_lock, irqflags); +#ifdef CONFIG_ANDROID_POWER_STAT + if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) { + lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE; + lock->stat.last_time = ktime_get(); + } +#endif + lock->expires = jiffies + timeout; + lock->flags |= ANDROID_SUSPEND_LOCK_AUTO_EXPIRE; + list_del(&lock->link); + list_add(&lock->link, &g_active_partial_wake_locks); + g_current_event_num++; + spin_unlock_irqrestore(&g_list_lock, irqflags); +} + +void android_lock_partial_suspend_auto_expire(android_suspend_lock_t *lock, int timeout) +{ + unsigned long irqflags; + //printk("android_lock_suspend name=%s\n", lock->name); + spin_lock_irqsave(&g_list_lock, irqflags); +#ifdef CONFIG_ANDROID_POWER_STAT + if(!(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE)) { + lock->flags |= ANDROID_SUSPEND_LOCK_ACTIVE; + lock->stat.last_time = ktime_get(); + } +#endif + lock->expires = jiffies + timeout; + lock->flags |= ANDROID_SUSPEND_LOCK_AUTO_EXPIRE; + list_del(&lock->link); + list_add(&lock->link, &g_active_full_wake_locks); + g_current_event_num++; + spin_unlock_irqrestore(&g_list_lock, irqflags); +} + +#ifdef CONFIG_ANDROID_POWER_STAT +static int print_lock_stat(char *buf, android_suspend_lock_t *lock) +{ + ktime_t active_time; + if(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE) + active_time = ktime_sub(ktime_get(), lock->stat.last_time); + else + active_time = ktime_set(0, 0); + return sprintf(buf, "\"%s\"\t%d\t%d\t%lld\t%lld\t%lld\t%lld\n", + lock->name, + lock->stat.count, lock->stat.expire_count, + ktime_to_ns(active_time), + ktime_to_ns(lock->stat.total_time), + ktime_to_ns(lock->stat.max_time), + ktime_to_ns(lock->stat.last_time)); +} + + +static int wakelocks_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long irqflags; + android_suspend_lock_t *lock; + int len = 0; + char *p = page; + + spin_lock_irqsave(&g_list_lock, irqflags); + + p += sprintf(p, "name\tcount\texpire_count\tactive_since\ttotal_time\tmax_time\tlast_change\n"); + list_for_each_entry(lock, &g_inactive_locks, link) { + p += print_lock_stat(p, lock); + } + list_for_each_entry(lock, &g_active_partial_wake_locks, link) { + p += print_lock_stat(p, lock); + } + list_for_each_entry(lock, &g_active_full_wake_locks, link) { + p += print_lock_stat(p, lock); + } + spin_unlock_irqrestore(&g_list_lock, irqflags); + + + *start = page + off; + + len = p - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static void android_unlock_suspend_stat_locked(android_suspend_lock_t *lock) +{ + if(lock->flags & ANDROID_SUSPEND_LOCK_ACTIVE) { + ktime_t duration; + lock->flags &= ~ANDROID_SUSPEND_LOCK_ACTIVE; + lock->stat.count++; + duration = ktime_sub(ktime_get(), lock->stat.last_time); + lock->stat.total_time = ktime_add(lock->stat.total_time, duration); + if(ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time)) + lock->stat.max_time = duration; + lock->stat.last_time = ktime_get(); + } +} +#endif + +void android_unlock_suspend(android_suspend_lock_t *lock) +{ + unsigned long irqflags; + //printk("android_unlock_suspend name=%s\n", lock->name); + spin_lock_irqsave(&g_list_lock, irqflags); +#ifdef CONFIG_ANDROID_POWER_STAT + android_unlock_suspend_stat_locked(lock); +#endif + lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE; + list_del(&lock->link); + list_add(&lock->link, &g_inactive_locks); + wake_up(&g_wait_queue); + spin_unlock_irqrestore(&g_list_lock, irqflags); +} + +static void android_power_update_timer(void) +{ + ktime_t next_off_time = ktime_set(0, 0); + if(g_charging_state < ANDROID_CHARGING_STATE_MAINTAIN && + ktime_to_ns(g_auto_off_timeout) > 0 && + ktime_to_ns(g_last_user_activity) >= ktime_to_ns(g_user_suspend_state_changed)) { + if(g_battery_level_low == 2) + next_off_time = ktime_add(g_last_user_activity, g_notification_timeout); + else + next_off_time = ktime_add(g_last_user_activity, g_auto_off_timeout); + } + if(ktime_to_ns(g_last_notification) >= ktime_to_ns(g_user_suspend_state_changed)) { + ktime_t notification_timeout = ktime_add(g_last_notification, g_notification_timeout); + if(!list_empty(&g_active_full_wake_locks)) + notification_timeout = ktime_add(notification_timeout, g_auto_off_timeout); // TODO: calculate more accurate off time + if((g_user_suspend_state == USER_NOTIFICATION + || ktime_to_ns(next_off_time) != 0) + && ktime_to_ns(notification_timeout) > ktime_to_ns(next_off_time)) + next_off_time = notification_timeout; + } + if(ktime_to_ns(next_off_time) <= ktime_to_ns(g_user_suspend_state_changed)) { + //printk("android_power_update_timer: no active timers\n"); + hrtimer_try_to_cancel(&g_auto_off_timer); + return; + } + //printk("android_power_update_timer: next sleep at %lld\n", ktime_to_ns(next_off_time)); + hrtimer_start(&g_auto_off_timer, next_off_time, HRTIMER_MODE_ABS); +} + +static void android_power_wakeup_locked(int notification, ktime_t time) +{ + int new_state = (notification == 0) ? USER_AWAKE : USER_NOTIFICATION; + if(new_state >= g_user_suspend_state) { + return; + } + printk("android_power_wakeup(%d) %d->%d at %lld\n", notification, g_user_suspend_state, new_state, ktime_to_ns(time)); + if(g_user_suspend_state == USER_SLEEP) + g_user_suspend_state_changed = time; + g_user_suspend_state = new_state; + g_current_event_num++; + wake_up(&g_wait_queue); +} + +void android_power_wakeup(int notification) +{ + unsigned long irqflags; + + ktime_t ktime_now; + + spin_lock_irqsave(&g_list_lock, irqflags); + ktime_now = ktime_get(); + if(notification) + g_last_notification = ktime_now; + else + g_last_user_activity = ktime_now; + android_power_wakeup_locked(notification, ktime_now); + android_power_update_timer(); + spin_unlock_irqrestore(&g_list_lock, irqflags); +} + +void android_register_early_suspend(android_early_suspend_t *handler) +{ + struct list_head *pos; + + mutex_lock(&g_early_suspend_lock); + list_for_each(pos, &g_early_suspend_handlers) { + android_early_suspend_t *e = list_entry(pos, android_early_suspend_t, link); + if(e->level > handler->level) + break; + } + list_add_tail(&handler->link, pos); + mutex_unlock(&g_early_suspend_lock); +} + +void android_unregister_early_suspend(android_early_suspend_t *handler) +{ + mutex_lock(&g_early_suspend_lock); + list_del(&handler->link); + mutex_unlock(&g_early_suspend_lock); +} + +#if ANDROID_POWER_TEST_EARLY_SUSPEND + +typedef struct +{ + android_early_suspend_t h; + const char *string; +} early_suspend_test_t; + +static void early_suspend_test(android_early_suspend_t *h) +{ + early_suspend_test_t *est = container_of(h, early_suspend_test_t, h); + printk("early suspend %s (l %d)\n", est->string, h->level); +} + +static void late_resume_test(android_early_suspend_t *h) +{ + early_suspend_test_t *est = container_of(h, early_suspend_test_t, h); + printk("late resume %s (l %d)\n", est->string, h->level); +} + +#define EARLY_SUSPEND_TEST_ENTRY(ilevel, istring) \ +{ \ + .h = { \ + .level = ilevel, \ + .suspend = early_suspend_test, \ + .resume = late_resume_test \ + }, \ + .string = istring \ +} +static early_suspend_test_t early_suspend_tests[] = { + EARLY_SUSPEND_TEST_ENTRY(10, "1"), + EARLY_SUSPEND_TEST_ENTRY(5, "2"), + EARLY_SUSPEND_TEST_ENTRY(10, "3"), + EARLY_SUSPEND_TEST_ENTRY(15, "4"), + EARLY_SUSPEND_TEST_ENTRY(8, "5") +}; + +#endif + +static int get_wait_timeout(int print_locks) +{ + unsigned long irqflags; + android_suspend_lock_t *lock, *next; + int max_timeout = 0; + + spin_lock_irqsave(&g_list_lock, irqflags); + list_for_each_entry_safe(lock, next, &g_active_partial_wake_locks, link) { + if(lock->flags & ANDROID_SUSPEND_LOCK_AUTO_EXPIRE) { + int timeout = lock->expires - (int)jiffies; + if(timeout <= 0) { + lock->flags &= ~ANDROID_SUSPEND_LOCK_AUTO_EXPIRE; +#ifdef CONFIG_ANDROID_POWER_STAT + lock->stat.expire_count++; + android_unlock_suspend_stat_locked(lock); +#endif + list_del(&lock->link); + list_add(&lock->link, &g_inactive_locks); + if(!print_locks) // print wake locks that expire while waiting to enter sleep + printk("expired wake lock %s\n", lock->name); + } + else { + if(timeout > max_timeout) + max_timeout = timeout; + if(print_locks) + printk("active wake lock %s, time left %d\n", lock->name, timeout); + } + } + else { + if(print_locks) + printk("active wake lock %s\n", lock->name); + } + } + if(g_user_suspend_state != USER_SLEEP || list_empty(&g_active_partial_wake_locks)) + max_timeout = -1; + spin_unlock_irqrestore(&g_list_lock, irqflags); + return max_timeout; +} + +static int android_power_class_suspend(struct sys_device *sdev, pm_message_t state) +{ + int rv = 0; + unsigned long irqflags; + + printk("android_power_suspend: enter\n"); + spin_lock_irqsave(&g_list_lock, irqflags); + if(!list_empty(&g_active_partial_wake_locks)) { + printk("android_power_suspend: abort for partial wakeup\n"); + rv = -EAGAIN; + } + if(g_user_suspend_state != USER_SLEEP) { + printk("android_power_suspend: abort for full wakeup\n"); + rv = -EAGAIN; + } + spin_unlock_irqrestore(&g_list_lock, irqflags); + return rv; +} + +static int android_power_device_suspend(struct sys_device *sdev, pm_message_t state) +{ + int rv = 0; + unsigned long irqflags; + + printk("android_power_device_suspend: enter\n"); + spin_lock_irqsave(&g_list_lock, irqflags); + if(!list_empty(&g_active_partial_wake_locks)) { + printk("android_power_device_suspend: abort for partial wakeup\n"); + rv = -EAGAIN; + } + if(g_user_suspend_state != USER_SLEEP) { + printk("android_power_device_suspend: abort for full wakeup\n"); + rv = -EAGAIN; + } + spin_unlock_irqrestore(&g_list_lock, irqflags); + return rv; +} + +int android_power_is_driver_suspended(void) +{ + return (get_wait_timeout(0) < 0) && (g_user_suspend_state == USER_SLEEP); +} + +static void android_power_suspend(struct work_struct *work) +{ + int entry_event_num; + int ret; + unsigned long irqflags; + int wait = 0; + android_early_suspend_t *pos; +// struct list_head *pos; + int already_suspended; + struct timespec now; + int print_locks; + android_suspend_lock_t *lock, *next_lock; + + spin_lock_irqsave(&g_list_lock, irqflags); + already_suspended = g_user_suspend_state == USER_SLEEP; + if(!already_suspended) + g_user_suspend_state = USER_SLEEP; + ktime_get_ts(&now); + g_user_suspend_state_changed = timespec_to_ktime(now); + printk("android_power_suspend: %lld\n", timespec_to_ns(&now)); + + list_for_each_entry_safe(lock, next_lock, &g_active_full_wake_locks, link) { +#ifdef CONFIG_ANDROID_POWER_STAT + android_unlock_suspend_stat_locked(lock); +#endif + list_del(&lock->link); + list_add(&lock->link, &g_inactive_locks); + printk("android_power_suspend: aborted full wake lock %s\n", lock->name); + } + + spin_unlock_irqrestore(&g_list_lock, irqflags); + if(already_suspended) { + printk("android_power_suspend: already suspened, wait for wakeup\n"); + wait_event_interruptible(g_wait_queue, g_user_suspend_state != USER_SLEEP); + return; + } + + mutex_lock(&g_early_suspend_lock); + //printk("android_power_suspend: call early suspend handlers\n"); + list_for_each_entry(pos, &g_early_suspend_handlers, link) { + if(pos->suspend != NULL) + pos->suspend(pos); + } + //printk("android_power_suspend: call early suspend handlers\n"); + + //printk("android_power_suspend: enter\n"); + + sys_sync(); + + while(g_user_suspend_state == USER_SLEEP) { + //printk("android_power_suspend: enter wait (%d)\n", wait); + if(wait) { + wait_event_interruptible_timeout(g_wait_queue, g_user_suspend_state != USER_SLEEP, wait); + wait = 0; + } + print_locks = 1; + while(1) { + wait = get_wait_timeout(print_locks); + print_locks = 0; + if(wait < 0) + break; + if(wait) + wait_event_interruptible_timeout(g_wait_queue, get_wait_timeout(0) != wait, wait); + else + wait_event_interruptible(g_wait_queue, get_wait_timeout(0) != wait); + } + wait = 0; + //printk("android_power_suspend: exit wait\n"); + entry_event_num = g_current_event_num; + if(g_user_suspend_state != USER_SLEEP) + break; + sys_sync(); + printk("android_power_suspend: enter suspend\n"); + ret = pm_suspend(PM_SUSPEND_MEM); + printk("android_power_suspend: exit suspend, ret = %d\n", ret); + if(g_current_event_num == entry_event_num) { + printk("android_power_suspend: pm_suspend returned with no event\n"); + wait = HZ / 2; + } + } + printk("android_power_suspend: done\n"); + //printk("android_power_suspend: call late resume handlers\n"); + list_for_each_entry_reverse(pos, &g_early_suspend_handlers, link) { + if(pos->resume != NULL) + pos->resume(pos); + } + //printk("android_power_suspend: call late resume handlers\n"); + mutex_unlock(&g_early_suspend_lock); +} + +static enum hrtimer_restart android_power_auto_off_timer(struct hrtimer *data) +{ + //static struct execute_work ew; +// static struct work_struct work; + printk("android_power_auto_off_timer\n"); +// INIT_WORK(&work, android_power_suspend, NULL); + queue_work(g_suspend_work_queue, &g_suspend_work); +// schedule_work(&g_suspend_work); + //execute_in_process_context(android_power_suspend, NULL, &ew); + return HRTIMER_NORESTART; +} + + +#if 0 +struct sysdev_class android_power_sysclass = { + set_kset_name("android_power"), + .suspend = android_power_class_suspend +}; +static struct sysdev_class *g_android_power_sysclass = NULL; + +static struct { + struct sys_device sysdev; +// omap_csmi_gsm_image_info_t *pdata; +} android_power_device = { + .sysdev = { + .id = 0, + .cls = &android_power_sysclass, +// .suspend = android_power_device_suspend + }, +// .pdata = &g_gsm_image_info +}; + +struct sysdev_class *android_power_get_sysclass(void) +{ + return g_android_power_sysclass; +} +#endif + +decl_subsys(android_power,NULL,NULL); + +static ssize_t state_show(struct kset *kset, char * buf) +{ + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + s += sprintf(s, "%d-%d-%d\n", g_user_suspend_state, list_empty(&g_active_full_wake_locks), list_empty(&g_active_partial_wake_locks)); + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t state_store(struct kset *kset, const char * buf, size_t n) +{ + if(n >= strlen("standby") && + strncmp(buf, "standby", strlen("standby")) == 0) { + android_power_suspend(NULL); + return n; + } + if(n >= strlen("wake") && + strncmp(buf, "wake", strlen("wake")) == 0) { + android_power_wakeup(0); + return n; + } + printk("android_power state_store: invalid argument\n"); + return -EINVAL; +} + +static ssize_t auto_off_timeout_show(struct kset *kset, char * buf) +{ + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + s += sprintf(s, "%ld\n", ktime_to_timespec(g_auto_off_timeout).tv_sec); + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t auto_off_timeout_store(struct kset *kset, const char * buf, size_t n) +{ + unsigned long irqflags; + long seconds; + struct timespec now; + + if(sscanf(buf, "%ld", &seconds) != 1) + return -EINVAL; + + spin_lock_irqsave(&g_list_lock, irqflags); + ktime_get_ts(&now); + g_last_user_activity = timespec_to_ktime(now); + g_auto_off_timeout = ktime_set(seconds, 0); + android_power_update_timer(); + spin_unlock_irqrestore(&g_list_lock, irqflags); + printk("android_power: auto off timeout set to %ld seconds\n", ktime_to_timespec(g_auto_off_timeout).tv_sec); + return (n); +} + +static ssize_t notification_timeout_show(struct kset *kset, char * buf) +{ + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + s += sprintf(s, "%ld\n", ktime_to_timespec(g_notification_timeout).tv_sec); + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t notification_timeout_store(struct kset *kset, const char * buf, size_t n) +{ + unsigned long irqflags; + long seconds; + struct timespec now; + + if(sscanf(buf, "%ld", &seconds) != 1) + return -EINVAL; + + spin_lock_irqsave(&g_list_lock, irqflags); + ktime_get_ts(&now); + g_last_notification = timespec_to_ktime(now); + g_notification_timeout = ktime_set(seconds, 0); + android_power_update_timer(); + spin_unlock_irqrestore(&g_list_lock, irqflags); + printk("android_power: notification timeout set to %ld seconds\n", ktime_to_timespec(g_auto_off_timeout).tv_sec); + return (n); +} + + +static ssize_t show_ktime_t(struct kset *kset, char * buf, ktime_t *ptr) +{ + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + s += sprintf(s, "%lld\n", ktime_to_ns(*ptr)); + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t store_ktime_t(struct kset *kset, const char * buf, size_t n, ktime_t *time_ptr, const char *name, int notification) +{ + //const char *p; + unsigned long irqflags; + u64 nanoseconds; + ktime_t new_time; + struct timespec now; + + if(sscanf(buf, "%llu", &nanoseconds) != 1) + return -EINVAL; + + ktime_get_ts(&now); + + if(nanoseconds == 0) + nanoseconds = timespec_to_ns(&now); + + if(nanoseconds > timespec_to_ns(&now)) { + printk("android_power: invalid %s time %lld, now = %lld\n", + name, nanoseconds, timespec_to_ns(&now)); + return -EINVAL; + } + new_time = ktime_add_ns(ktime_set(0, 0), nanoseconds); + + spin_lock_irqsave(&g_list_lock, irqflags); + if(nanoseconds > ktime_to_ns(*time_ptr)) { + *time_ptr = new_time; + //printk("android_power: %s time set to %lld\n", + // name, ktime_to_ns(*time_ptr)); + if(notification == 0) { + if(g_user_suspend_state != USER_AWAKE) + android_power_wakeup_locked(0, new_time); + } + else { + if(g_user_suspend_state == USER_SLEEP) + android_power_wakeup_locked(notification, new_time); + } + android_power_update_timer(); + } + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (n); +} + +static ssize_t last_user_activity_show(struct kset *kset, char * buf) +{ + return show_ktime_t(kset, buf, &g_last_user_activity); +} + +static ssize_t last_user_activity_store(struct kset *kset, const char * buf, size_t n) +{ + return store_ktime_t(kset, buf, n, &g_last_user_activity, "user activity", 0); +} + +static ssize_t last_notification_show(struct kset *kset, char * buf) +{ + return show_ktime_t(kset, buf, &g_last_notification); +} + +static ssize_t last_notification_store(struct kset *kset, const char * buf, size_t n) +{ + return store_ktime_t(kset, buf, n, &g_last_notification, "notification", 1); +} + +static ssize_t request_sleep_show(struct kset *kset, char * buf) +{ + return show_ktime_t(kset, buf, &g_user_suspend_state_changed); +} + +static ssize_t request_sleep_store(struct kset *kset, const char * buf, size_t n) +{ + u64 nanoseconds; + u64 last; + struct timespec now; + + if(sscanf(buf, "%llu", &nanoseconds) != 1) + return -EINVAL; + + ktime_get_ts(&now); + + if(nanoseconds > timespec_to_ns(&now)) { + printk("android_power: invalid sleep time %lld, now = %lld\n", + nanoseconds, timespec_to_ns(&now)); + return -EINVAL; + } + last = ktime_to_ns(g_user_suspend_state_changed); + if(nanoseconds <= last) { + printk("android_power: outdated sleep time %lld, last %lld, now = %lld\n", + nanoseconds, last, timespec_to_ns(&now)); + return n; + } + queue_work(g_suspend_work_queue, &g_suspend_work); + //android_power_suspend(NULL); + return n; +} + +static int lookup_wake_lock_name(const char *buf, size_t n, int allocate) +{ + int i; + int free_index = -1; + int inactive_index = -1; + int expires_index = -1; + int expires_time = INT_MAX; + char name[32]; + + if(n <= 0) + return -EINVAL; + if(n >= sizeof(g_user_wake_locks[0].name_buffer)) + return -EOVERFLOW; + if(n == sizeof(g_user_wake_locks[0].name_buffer) - 1 && buf[n - 1] != '\0') + return -EOVERFLOW; + + memcpy(name, buf, n); + if(name[n - 1] != '\0') + name[n] = '\0'; + + for(i = 0; i < g_max_user_lockouts; i++) { + if(strcmp(g_user_wake_locks[i].name_buffer, name) == 0) + return i; + if(g_user_wake_locks[i].name_buffer[0] == '\0') + free_index = i; + else if(g_user_wake_locks[i].state == USER_WAKE_LOCK_INACTIVE) + inactive_index = i; + else if(g_user_wake_locks[i].suspend_lock.expires < expires_time) + expires_index = i; + } + if(allocate) { + if(free_index >= 0) + i = free_index; + else if(inactive_index >= 0) + i = inactive_index; + else if(expires_index >= 0) { + i = expires_index; + printk("lookup_wake_lock_name: overwriting expired lock, %s\n", g_user_wake_locks[i].name_buffer); + } + else { + i = 0; + printk("lookup_wake_lock_name: overwriting active lock, %s\n", g_user_wake_locks[i].name_buffer); + } + strcpy(g_user_wake_locks[i].name_buffer, name); + return i; + } +#if ANDROID_POWER_PRINT_USER_WAKE_LOCKS + printk("lookup_wake_lock_name: %s not found\n", name); +#endif + return -EINVAL; +} + +static ssize_t acquire_full_wake_lock_show(struct kset *kset, char * buf) +{ + int i; + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + for(i = 0; i < g_max_user_lockouts; i++) { + if(g_user_wake_locks[i].name_buffer[0] != '\0' && g_user_wake_locks[i].state == USER_WAKE_LOCK_FULL) + s += sprintf(s, "%s ", g_user_wake_locks[i].name_buffer); + } + s += sprintf(s, "\n"); + + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t acquire_full_wake_lock_store(struct kset *kset, const char * buf, size_t n) +{ + int i; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + i = lookup_wake_lock_name(buf, n, 1); + if(i >= 0) + g_user_wake_locks[i].state = USER_WAKE_LOCK_FULL; + spin_unlock_irqrestore(&g_list_lock, irqflags); + if(i < 0) + return i; + +#if ANDROID_POWER_PRINT_USER_WAKE_LOCKS + printk("acquire_full_wake_lock_store: %s, size %d\n", g_user_wake_locks[i].name_buffer, n); +#endif + + android_lock_partial_suspend_auto_expire(&g_user_wake_locks[i].suspend_lock, ktime_to_timespec(g_auto_off_timeout).tv_sec * HZ); + android_power_wakeup(1); + + return n; +} + +static ssize_t acquire_partial_wake_lock_show(struct kset *kset, char * buf) +{ + int i; + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + for(i = 0; i < g_max_user_lockouts; i++) { + if(g_user_wake_locks[i].name_buffer[0] != '\0' && g_user_wake_locks[i].state == USER_WAKE_LOCK_PARTIAL) + s += sprintf(s, "%s ", g_user_wake_locks[i].name_buffer); + } + s += sprintf(s, "\n"); + + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t acquire_partial_wake_lock_store(struct kset *kset, const char * buf, size_t n) +{ + int i; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + i = lookup_wake_lock_name(buf, n, 1); + if(i >= 0) + g_user_wake_locks[i].state = USER_WAKE_LOCK_PARTIAL; + spin_unlock_irqrestore(&g_list_lock, irqflags); + if(i < 0) + return 0; + +#if ANDROID_POWER_PRINT_USER_WAKE_LOCKS + printk("acquire_partial_wake_lock_store: %s, size %d\n", g_user_wake_locks[i].name_buffer, n); +#endif + + android_lock_suspend_auto_expire(&g_user_wake_locks[i].suspend_lock, ktime_to_timespec(g_auto_off_timeout).tv_sec * HZ); + + return n; +} + + +static ssize_t release_wake_lock_show(struct kset *kset, char * buf) +{ + int i; + char * s = buf; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + for(i = 0; i < g_max_user_lockouts; i++) { + if(g_user_wake_locks[i].name_buffer[0] != '\0' && g_user_wake_locks[i].state == USER_WAKE_LOCK_INACTIVE) + s += sprintf(s, "%s ", g_user_wake_locks[i].name_buffer); + } + s += sprintf(s, "\n"); + + spin_unlock_irqrestore(&g_list_lock, irqflags); + return (s - buf); +} + +static ssize_t release_wake_lock_store(struct kset *kset, const char * buf, size_t n) +{ + int i; + unsigned long irqflags; + int was_full_wake = 0; + + spin_lock_irqsave(&g_list_lock, irqflags); + i = lookup_wake_lock_name(buf, n, 1); + if(i >= 0) { + was_full_wake = g_user_wake_locks[i].state == USER_WAKE_LOCK_FULL; + g_user_wake_locks[i].state = USER_WAKE_LOCK_INACTIVE; + } + spin_unlock_irqrestore(&g_list_lock, irqflags); + + if(i < 0) + return i; + +#if ANDROID_POWER_PRINT_USER_WAKE_LOCKS + printk("release_wake_lock_store: %s, size %d\n", g_user_wake_locks[i].name_buffer, n); +#endif + + android_unlock_suspend(&g_user_wake_locks[i].suspend_lock); + if(was_full_wake) + android_power_wakeup(1); + + return n; +} + +static int scale_battery_level(int level, int scale) +{ + return (level * scale + 50) / 100; +} + +static ssize_t charging_state_show(struct kset *kset, char * buf) +{ + const char *state_str[] = { + [ANDROID_CHARGING_STATE_UNKNOWN] = "Unknown", + [ANDROID_CHARGING_STATE_DISCHARGE] = "Discharging", + [ANDROID_CHARGING_STATE_MAINTAIN] = "Maintaining", + [ANDROID_CHARGING_STATE_SLOW] = "Slow", + [ANDROID_CHARGING_STATE_NORMAL] = "Normal", + [ANDROID_CHARGING_STATE_FAST] = "Fast", + [ANDROID_CHARGING_STATE_OVERHEAT] = "Overheat" + }; + char * s = buf; + s += sprintf(s, "%s\n", state_str[g_charging_state]); + return (s - buf); +} + +#define android_power_attr(_name) \ +static struct subsys_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ + .store = _name##_store, \ +} + +#define android_power_attr_ro(_name) \ +static struct subsys_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ +} + +#define android_power_attr_int_ro(_name, _var) \ +static ssize_t _name##_show(struct kset *kset, char * buf) \ +{ \ + char * s = buf; \ + s += sprintf(s, "%d\n", _var); \ + return (s - buf); \ +} \ +android_power_attr_ro(_name) + +#define android_power_attr_int_rw(_name, _var, _refresh) \ +static ssize_t _name##_show(struct kset *kset, char * buf) \ +{ \ + char * s = buf; \ + s += sprintf(s, "%d\n", _var); \ + return (s - buf); \ +} \ +static ssize_t _name##_store(struct kset *kset, const char * buf, size_t n) \ +{ \ + int new_val; \ + if(sscanf(buf, "%d", &new_val) != 1) \ + return -EINVAL; \ + _var = new_val; \ + _refresh; \ + return 0; \ +} \ +android_power_attr(_name) + +android_power_attr(state); +android_power_attr(auto_off_timeout); +android_power_attr(notification_timeout); +android_power_attr(last_user_activity); +android_power_attr(last_notification); +android_power_attr(request_sleep); +android_power_attr(acquire_full_wake_lock); +android_power_attr(acquire_partial_wake_lock); +android_power_attr(release_wake_lock); +android_power_attr_int_ro(battery_level_raw, g_battery_level); +android_power_attr_int_ro(battery_level, scale_battery_level(g_battery_level, g_battery_level_scale)); +android_power_attr_int_rw(battery_level_scale, g_battery_level_scale, ); +android_power_attr_int_ro(battery_level_low, g_battery_level_low); +android_power_attr_int_rw(battery_low_level, g_battery_low_level, update_battery_low_level()); +android_power_attr_int_rw(battery_shutdown_level, g_battery_shutdown_level, update_battery_low_level()); +android_power_attr_ro(charging_state); + +static struct attribute * g[] = { + &state_attr.attr, + &auto_off_timeout_attr.attr, + ¬ification_timeout_attr.attr, + &last_user_activity_attr.attr, + &last_notification_attr.attr, + &request_sleep_attr.attr, + &acquire_full_wake_lock_attr.attr, + &acquire_partial_wake_lock_attr.attr, + &release_wake_lock_attr.attr, + &battery_level_raw_attr.attr, + &battery_level_attr.attr, + &battery_level_scale_attr.attr, + &battery_level_low_attr.attr, + &battery_low_level_attr.attr, + &battery_shutdown_level_attr.attr, + &charging_state_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = g, +}; + +static void set_set_battery_level_low(int state) +{ + int ret; + int old_state = g_battery_level_low; + if(old_state == state) + return; + g_battery_level_low = state; + printk("android_power_set_battery_level low state %d -> %d\n", old_state, state); + ret = sysfs_update_file(&android_power_subsys.kobj, &battery_level_low_attr.attr); + if(ret) + printk("android_power_set_battery_level low state sysfs_update_file failed\n"); + android_power_update_timer(); +} + +static void update_battery_low_level(void) +{ + if(g_battery_level < g_battery_shutdown_level) + set_set_battery_level_low(2); + else if(g_battery_level < g_battery_low_level) + set_set_battery_level_low(1); + else + set_set_battery_level_low(0); +} + +void android_power_set_battery_level(int level) +{ + int ret; + int old_level; + int scale; + unsigned long irqflags; + + spin_lock_irqsave(&g_list_lock, irqflags); + old_level = g_battery_level; + g_battery_level = level; + scale = g_battery_level_scale; + spin_unlock_irqrestore(&g_list_lock, irqflags); + if(level != old_level) { + //printk("android_power_set_battery_level raw %d -> %d\n", old_level, level); + ret = sysfs_update_file(&android_power_subsys.kobj, &battery_level_raw_attr.attr); + if(ret) + printk("android_power_set_battery_level raw sysfs_update_file failed\n"); + } + if(scale_battery_level(level, scale) != scale_battery_level(old_level, scale)) { + printk("android_power_set_battery_level scaled %d -> %d (scale %d)\n", scale_battery_level(old_level, scale), scale_battery_level(level, scale), scale); + ret = sysfs_update_file(&android_power_subsys.kobj, &battery_level_attr.attr); + if(ret) + printk("android_power_set_battery_level scaled sysfs_update_file failed\n"); + } +} + +void android_power_set_charging_state(android_charging_state_t state) +{ + int ret; + android_charging_state_t old_state; + old_state = g_charging_state; + if(old_state == state) + return; + g_charging_state = state; + + printk("android_power_set_charging_state %d > %d\n", old_state, state); + ret = sysfs_update_file(&android_power_subsys.kobj, &charging_state_attr.attr); + if(ret) + printk("android_power_set_charging_state sysfs_update_file failed\n"); + android_power_update_timer(); +} + +#if 0 +// test code when there is no platform suspend + +static android_suspend_lock_t test_pm_ops_suspend_lock = { + .name = "test_pm_ops" +}; + +int test_pm_op_enter(suspend_state_t state) +{ + printk("test_pm_op_enter reached\n"); + android_lock_suspend(&test_pm_ops_suspend_lock); + printk("test_pm_op_enter returned\n"); + return 0; +} + +void test_pm_ops_late_resume_handler(android_early_suspend_t *h) +{ + printk("test_pm_ops_late_resume_handler reached\n"); + android_unlock_suspend(&test_pm_ops_suspend_lock); + printk("test_pm_ops_late_resume_handler returned\n"); +} + +static struct pm_ops test_pm_ops = { + .enter = test_pm_op_enter +}; + +static android_early_suspend_t test_pm_ops_early_suspend_handler = { + .resume = test_pm_ops_late_resume_handler +}; +#endif + +static int __init android_power_init(void) +{ + int ret; + int i; + + printk("android_power_init\n"); + +#if 0 + if(pm_ops == NULL) { + printk("android_power_init no pm_ops, installing test code\n"); + pm_set_ops(&test_pm_ops); + android_init_suspend_lock(&test_pm_ops_suspend_lock); + android_register_early_suspend(&test_pm_ops_early_suspend_handler); + } +#endif + +#ifdef CONFIG_ANDROID_POWER_STAT + g_deleted_wake_locks.stat.count = 0; +#endif + init_waitqueue_head(&g_wait_queue); + + g_user_wake_locks = kzalloc(sizeof(*g_user_wake_locks) * g_max_user_lockouts, GFP_KERNEL); + if(g_user_wake_locks == NULL) { + ret = -ENOMEM; + goto err1; + } + for(i = 0; i < g_max_user_lockouts; i++) { + g_user_wake_locks[i].suspend_lock.name = g_user_wake_locks[i].name_buffer; + android_init_suspend_lock(&g_user_wake_locks[i].suspend_lock); + } + + g_notification_timeout = ktime_set(5, 0); + + g_suspend_work_queue = create_workqueue("suspend"); + if(g_suspend_work_queue == NULL) { + ret = -ENOMEM; + goto err2; + } + + + hrtimer_init(&g_auto_off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + g_auto_off_timer.function = android_power_auto_off_timer; + + ret = subsystem_register(&android_power_subsys); + if(ret) { + printk("android_power_init: subsystem_register failed\n"); + goto err3; + } + ret = sysfs_create_group(&android_power_subsys.kobj,&attr_group); + if(ret) { + printk("android_power_init: sysfs_create_group failed\n"); + goto err4; + } +#ifdef CONFIG_ANDROID_POWER_STAT + create_proc_read_entry("wakelocks", S_IRUGO, NULL, wakelocks_read_proc, NULL); +#endif + +#if ANDROID_POWER_TEST_EARLY_SUSPEND + { + int i; + for(i = 0; i < sizeof(early_suspend_tests) / sizeof(early_suspend_tests[0]); i++) + android_register_early_suspend(&early_suspend_tests[i].h); + } +#endif + +#if 0 + ret = sysdev_class_register(&android_power_sysclass); + if(ret) { + printk("android_power_init: sysdev_class_register failed\n"); + goto err1; + } + ret = sysdev_register(&android_power_device.sysdev); + if(ret < 0) + goto err2; + + g_android_power_sysclass = &android_power_sysclass; +#endif + printk("android_power_init done\n"); + + return 0; + +//err2: +// sysdev_class_unregister(&android_power_sysclass); +err4: + subsystem_unregister(&android_power_subsys); +err3: + destroy_workqueue(g_suspend_work_queue); +err2: + for(i = 0; i < g_max_user_lockouts; i++) { + android_uninit_suspend_lock(&g_user_wake_locks[i].suspend_lock); + } + kfree(g_user_wake_locks); +err1: + return ret; +} + +static void __exit android_power_exit(void) +{ + int i; +// g_android_power_sysclass = NULL; +// sysdev_unregister(&android_power_device.sysdev); +// sysdev_class_unregister(&android_power_sysclass); +#ifdef CONFIG_ANDROID_POWER_STAT + remove_proc_entry("wakelocks", NULL); +#endif + sysfs_remove_group(&android_power_subsys.kobj, &attr_group); + subsystem_unregister(&android_power_subsys); + hrtimer_cancel(&g_auto_off_timer); + destroy_workqueue(g_suspend_work_queue); + for(i = 0; i < g_max_user_lockouts; i++) { + android_uninit_suspend_lock(&g_user_wake_locks[i].suspend_lock); + } + kfree(g_user_wake_locks); +} + +core_initcall(android_power_init); +module_exit(android_power_exit); + +//EXPORT_SYMBOL(android_power_get_sysclass); +EXPORT_SYMBOL(android_init_suspend_lock); +EXPORT_SYMBOL(android_uninit_suspend_lock); +EXPORT_SYMBOL(android_lock_suspend); +EXPORT_SYMBOL(android_lock_suspend_auto_expire); +EXPORT_SYMBOL(android_unlock_suspend); +EXPORT_SYMBOL(android_power_wakeup); +EXPORT_SYMBOL(android_register_early_suspend); +EXPORT_SYMBOL(android_unregister_early_suspend); +EXPORT_SYMBOL(android_power_set_battery_level); +EXPORT_SYMBOL(android_power_set_charging_state); + + diff -Nru linux-2.6.23/drivers/android/ram_console.c kernel.android/drivers/android/ram_console.c --- linux-2.6.23/drivers/android/ram_console.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/android/ram_console.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,213 @@ +/* drivers/android/ram_console.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +struct ram_console_buffer { + uint32_t sig; + uint32_t start; + uint32_t size; + uint8_t data[0]; +}; + +#define RAM_CONSOLE_SIG (43474244) //DBGC + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT +static char __initdata ram_console_old_log_init_buffer[CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE]; +#endif +static char *ram_console_old_log; +static size_t ram_console_old_log_size; + +static struct ram_console_buffer *ram_console_buffer; +static size_t ram_console_buffer_size; + +static void ram_console_write(struct console *console, const char *s, unsigned int count) +{ + int rem; + struct ram_console_buffer *buffer = ram_console_buffer; + + if(count > ram_console_buffer_size) { + s += count - ram_console_buffer_size; + count = ram_console_buffer_size; + } + rem = ram_console_buffer_size - buffer->start; + if(rem < count) { + memcpy(buffer->data + buffer->start, s, rem); + s += rem; + count -= rem; + buffer->start = 0; + buffer->size = ram_console_buffer_size; + } + memcpy(buffer->data + buffer->start, s, count); + buffer->start += count; + if(buffer->size < ram_console_buffer_size) + buffer->size += count; +} + +static struct console ram_console = { + .name = "ram", + .write = ram_console_write, +// .setup = ram_console_setup, + .flags = CON_PRINTBUFFER | CON_ENABLED, + .index = -1, +}; + +static void __init ram_console_save_old(struct ram_console_buffer *buffer, char *dest) +{ + if(dest == NULL) { + dest = kmalloc(buffer->size, GFP_KERNEL); + if(dest == NULL) { + printk("ram_console_save_old: failed to allocate buffer\n"); + return; + } + } + + ram_console_old_log = dest; + ram_console_old_log_size = buffer->size; + memcpy(ram_console_old_log, &buffer->data[buffer->start], buffer->size - buffer->start); + memcpy(ram_console_old_log + buffer->size - buffer->start, &buffer->data[0], buffer->start); +} + +static int __init ram_console_init(struct ram_console_buffer *buffer, size_t buffer_size, char *old_buf) +{ + ram_console_buffer = buffer; + ram_console_buffer_size = buffer_size - sizeof(struct ram_console_buffer); + + if(buffer->sig == RAM_CONSOLE_SIG) { + if(buffer->size > buffer_size || buffer->start >= buffer_size) { + printk("ram_console_init: found existing invalid buffer, size %d, start %d\n", buffer->size, buffer->start); + } + else { + printk("ram_console_init: found exising buffer, size %d, start %d\n", buffer->size, buffer->start); + ram_console_save_old(buffer, old_buf); + } + } + else { + printk("ram_console_init: no valid data in buffer\n"); + } + + buffer->sig = RAM_CONSOLE_SIG; + buffer->start = 0; + buffer->size = 0; + + register_console(&ram_console); +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE + console_verbose(); +#endif + printk("ram_console_init done\n"); + return 0; +} + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT +static int __init ram_console_early_init(void) +{ + return ram_console_init((struct ram_console_buffer *)CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR, CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE, ram_console_old_log_init_buffer); +} +#else +static int ram_console_driver_probe(struct platform_device *pdev) +{ + struct resource *res = pdev->resource; + size_t buffer_size; + + if(res == NULL || pdev->num_resources != 1 || !(res->flags & IORESOURCE_MEM)) { + printk("ram_console_driver_probe: invalid resource, %p %d flags %lx\n", res, pdev->num_resources, res ? res->flags : 0); + return -ENXIO; + } + buffer_size = res->end - res->start; + printk("ram_console_driver_probe: got buffer at %x, size %x\n", res->start, buffer_size); + + return ram_console_init((struct ram_console_buffer *)res->start, buffer_size, NULL /* allocate if needed*/); +} + +static struct platform_driver ram_console_driver = { + .probe = ram_console_driver_probe, + .driver = { + .name = "ram_console", + }, +}; + +static int __init ram_console_module_init(void) +{ + int err; + err = platform_driver_register(&ram_console_driver); + printk("ram_console_init: platform_driver_register returned %d\n", err); + return err; +} +#endif + +static ssize_t +ram_console_read_old(struct file *file, char __user *buf, + size_t len, loff_t * offset) +{ + loff_t pos = *offset; + ssize_t count; + + if (pos >= ram_console_old_log_size) + return 0; + + count = min(len, (size_t)(ram_console_old_log_size - pos)); + if (copy_to_user(buf, ram_console_old_log + pos, count)) + return -EFAULT; + + *offset += count; + return count; +} + +static struct file_operations ram_console_file_ops = { + .owner = THIS_MODULE, + .read = ram_console_read_old, +}; + +static int __init ram_console_late_init(void) +{ + struct proc_dir_entry *entry; + + if(ram_console_old_log == NULL) + return 0; +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT + ram_console_old_log = kmalloc(ram_console_old_log_size, GFP_KERNEL); + if(ram_console_old_log == NULL) { + printk("ram_console_late_init: failed to allocate buffer for old log\n"); + ram_console_old_log_size = 0; + return 0; + } + memcpy(ram_console_old_log, ram_console_old_log_init_buffer, ram_console_old_log_size); +#endif + entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL); + if(!entry) { + printk("ram_console_late_init: failed to create proc entry\n"); + kfree(ram_console_old_log); + ram_console_old_log = NULL; + return 0; + } + + entry->proc_fops = &ram_console_file_ops; + entry->size = ram_console_old_log_size; + return 0; +} + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT +console_initcall(ram_console_early_init); +#else +module_init(ram_console_module_init); +#endif +late_initcall(ram_console_late_init); + diff -Nru linux-2.6.23/drivers/binder/LICENSE kernel.android/drivers/binder/LICENSE --- linux-2.6.23/drivers/binder/LICENSE 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/LICENSE 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,281 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + \ No newline at end of file diff -Nru linux-2.6.23/drivers/binder/Makefile kernel.android/drivers/binder/Makefile --- linux-2.6.23/drivers/binder/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,4 @@ + +obj-$(CONFIG_BINDER) = binderdev.o + +binderdev-objs := iobuffer.o binder.o binder_node.o binder_proc.o binder_thread.o binder_transaction.o \ No newline at end of file diff -Nru linux-2.6.23/drivers/binder/binder.c kernel.android/drivers/binder/binder.c --- linux-2.6.23/drivers/binder/binder.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,691 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include // includes +#include // for 'current' +#include // for vma, etc. +#include +#include +#include +#include "binder_defs.h" +#include "binder_proc.h" +#include "binder_thread.h" +#include "binder_node.h" +#include "binder_transaction.h" +#include "iobuffer.h" + +MODULE_LICENSE("GPL"); // class_* symbols get exported GPL +MODULE_AUTHOR("PalmSource, Inc."); +MODULE_DESCRIPTION("Capability-based IPC"); + +#define BINDER_MINOR 0 +#define BINDER_NUM_DEVS 1 +#define BINDER_NAME "binder" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +#define CLASS_SIMPLE class_simple +#define CLASS_SIMPLE_CREATE class_simple_create +#define CLASS_SIMPLE_DEVICE_ADD class_simple_device_add +#define CLASS_SIMPLE_DESTROY class_simple_destroy +#define CLASS_SIMPLE_DEVICE_REMOVE class_simple_device_remove +#else +#define CLASS_SIMPLE class +#define CLASS_SIMPLE_CREATE class_create +#define CLASS_SIMPLE_DEVICE_ADD class_device_create +#define CLASS_SIMPLE_DESTROY class_destroy +#define CLASS_SIMPLE_DEVICE_REMOVE(a) class_device_destroy(binder_class, a) +#endif + +/* + * Prototypes + */ + +struct binder_thread* find_thread(pid_t pid, binder_proc_t *proc, bool remove); + +#if HAVE_UNLOCKED_IOCTL +#define USE_UNLOCKED_IOCTL 1 +#else +#define USE_UNLOCKED_IOCTL 0 +#endif +#if USE_UNLOCKED_IOCTL +static long binder_unlocked_ioctl(struct file *, unsigned int, unsigned long); +#else +static int binder_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +#endif +static int binder_open(struct inode *, struct file *); +static int binder_release(struct inode *, struct file *); +static int binder_mmap(struct file *, struct vm_area_struct *); + +/* + * Globals + */ + +struct binder_dev { + struct cdev cdev; +}; + +static int binder_major = 0; +static char const * const binder_name = BINDER_NAME; +static struct binder_dev binder_device; +static struct CLASS_SIMPLE *binder_class; + +static struct file_operations binder_fops = { + .owner = THIS_MODULE, +#if USE_UNLOCKED_IOCTL + .unlocked_ioctl = binder_unlocked_ioctl, +#else + .ioctl = binder_ioctl, +#endif + .mmap = binder_mmap, + .open = binder_open, + .release = binder_release +}; + +static void binder_vma_open(struct vm_area_struct * area); +static void binder_vma_close(struct vm_area_struct * area); +static struct page * binder_vma_nopage(struct vm_area_struct * area, unsigned long address, int *type); + +static struct vm_operations_struct binder_vm_ops = { + .open = binder_vma_open, + .close = binder_vma_close, + .nopage = binder_vma_nopage +}; + +struct kmem_cache *transaction_cache = NULL; +struct kmem_cache *thread_cache = NULL; +struct kmem_cache *node_cache = NULL; +struct kmem_cache *local_mapping_cache = NULL; +struct kmem_cache *reverse_mapping_cache = NULL; +struct kmem_cache *range_map_cache = NULL; + +spinlock_t cmpxchg32_spinner = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(maps_lock); + +/* + * The kernel sizes its process hash table based up on the amount of RAM, with + * a lower limit of 4 bits and an upper limit of 12 bits. We probably don't + * need 8 bits worth of entries on PDAs, but it make it very likely we will + * have chain lengths of one. + */ + +#define PID_HASH_BITS (8) +static int pid_hash_bits = PID_HASH_BITS; +#define hash_proc_id(pid) hash_long(pid, pid_hash_bits) + +static struct hlist_head *pid_table = NULL; + +static inline binder_thread_t * +binder_thread_alloc(pid_t pid, binder_proc_t *proc, int index) +{ + binder_thread_t *thread = binder_thread_init(pid, proc); + if (thread) { + if (proc) { + if(!binder_proc_AddThread(proc, thread)) + return NULL; // binder_proc_AddThread will cause the thread to be deleted if the process is dying + } + hlist_add_head(&(thread->node), pid_table + index); + } + DPRINTF(5, (KERN_WARNING "%s(%u, %p, %d): %p\n", __func__, pid, proc, index, thread)); + return thread; +} + +struct binder_thread * +core_find_thread(pid_t pid, binder_proc_t *proc, bool remove) +{ + binder_thread_t *thread; + struct hlist_node *_p; + const int index = hash_proc_id(pid); + + DPRINTF(5, (KERN_WARNING "%s(%u, %p, %s): index %d\n", __func__, pid, proc, remove ? "TRUE" : "FALSE", index)); + hlist_for_each_entry(thread, _p, pid_table + index, node) { + DPRINTF(5, (KERN_WARNING "thread: %p, thread->m_thid: %u\n", thread, thread->m_thid)); + if (thread->m_thid == pid) { + DPRINTF(5, (KERN_WARNING "found thread %p, proc=%p\n", thread, thread->m_team)); + if (remove) { + thread->attachedToThread = FALSE; + hlist_del(&thread->node); + } else if (proc) { + if (thread->m_team == NULL) { + binder_thread_AttachProcess(thread, proc); + } else { + BND_ASSERT(thread->m_team == proc, "proc changed"); + } + } + return thread; + } + } + + return NULL; +} + +binder_thread_t * +find_thread(pid_t pid, binder_proc_t *proc, bool remove) +{ + binder_thread_t *thread; + + DPRINTF(5, (KERN_WARNING "%s(%u, %p, %s)\n", __func__, pid, proc, remove ? "TRUE" : "FALSE")); + thread = core_find_thread(pid, proc, remove); + + /* binder_thread_alloc() fails for -ENOMEM only */ + if (thread == NULL && remove == FALSE) thread = binder_thread_alloc(pid, proc, hash_proc_id(pid)); + return thread; +} + +struct binder_thread * +check_for_thread(pid_t pid, bool create) +{ + binder_thread_t *thread; + int rv; + + rv = down_interruptible(&maps_lock); + if(rv != 0) + return NULL; + if (create) + thread = find_thread(pid, NULL, FALSE); + else + thread = core_find_thread(pid, NULL, FALSE); + if(thread != NULL) + BND_FIRST_ACQUIRE(binder_thread, thread, STRONG, thread); + up(&maps_lock); + + return thread; +} + +binder_thread_t * +attach_child_thread(pid_t child_pid, binder_thread_t *parent) +{ + binder_thread_t *thread; + int rv; + bool failed = FALSE; + + rv = down_interruptible(&maps_lock); + if(rv != 0) + return NULL; + thread = find_thread(child_pid, NULL, FALSE); + if(thread != NULL) { + BND_FIRST_ACQUIRE(binder_thread, thread, STRONG, parent); + // Note: it is important this be done with the lock + // held. See binder_thread_WaitForParent(). + failed = !binder_thread_SetParentThread(thread, parent); + } + up(&maps_lock); + + if (failed) { + forget_thread(thread); + thread = NULL; + } + + return thread; +} + +void +forget_thread(struct binder_thread *thread) +{ + pid_t pid; + bool attached; + int rv; + + rv = down_interruptible(&maps_lock); + if(rv != 0) + return; + pid = thread->m_thid; + attached = thread->attachedToThread; + if(BND_RELEASE(binder_thread, thread, STRONG, thread) == 1) { + // Remove it if not yet accessed by user space... + if (!attached) { + find_thread(pid, NULL, TRUE); + } + } + up(&maps_lock); +} + +#if BND_MEM_DEBUG +typedef struct dbg_mem_header_s { + unsigned long state; + kmem_cache_t *slab; + struct dbg_mem_header_s *next; + struct dbg_mem_header_s *prev; +} dbg_mem_header_t ; +static dbg_mem_header_t *dbg_active_memory; +#endif + +void generic_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ +#if BND_MEM_DEBUG + dbg_mem_header_t *h = p; + if(flags & SLAB_CTOR_CONSTRUCTOR) { + h->state = 0; + h->slab = slab; + h->next = dbg_active_memory; + if(h->next) + h->next->prev = h; + h->prev = NULL; + dbg_active_memory = h; + } + else { + BND_ASSERT(h->state == 0 || h->state == 0x22222222, "memory still in use"); + if(h->next) + h->next->prev = h->prev; + if(h->prev) + h->prev->next = h->next; + else + dbg_active_memory = h->next; + } +#endif +} + +void transaction_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ + DIPRINTF(10, (KERN_WARNING "%s(%p, %p, %08lx)\n", __func__, p, slab, flags)); + generic_slab_xtor(p, slab, flags); +} + +void thread_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ + DIPRINTF(10, (KERN_WARNING "%s(%p, %p, %08lx)\n", __func__, p, slab, flags)); + generic_slab_xtor(p, slab, flags); +} + +void node_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ + DIPRINTF(10, (KERN_WARNING "%s(%p, %p, %08lx)\n", __func__, p, slab, flags)); + generic_slab_xtor(p, slab, flags); +} + +void local_mapping_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ + DIPRINTF(10, (KERN_WARNING "%s(%p, %p, %08lx)\n", __func__, p, slab, flags)); + generic_slab_xtor(p, slab, flags); +} + +void reverse_mapping_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ + DIPRINTF(10, (KERN_WARNING "%s(%p, %p, %08lx)\n", __func__, p, slab, flags)); + generic_slab_xtor(p, slab, flags); +} + +void range_map_slab_xtor(void *p, struct kmem_cache *slab, unsigned long flags) +{ + DIPRINTF(10, (KERN_WARNING "%s(%p, %p, %08lx)\n", __func__, p, slab, flags)); + generic_slab_xtor(p, slab, flags); +} + +static int /*__init*/ create_pools(void) +{ + //long cache_flags = /*SLAB_DEBUG_FREE | SLAB_DEBUG_INITIAL | SLAB_RED_ZONE |*/ SLAB_POISON; + //long cache_flags = SLAB_RECLAIM_ACCOUNT | SLAB_NO_REAP; + long cache_flags = 0; +#if BND_MEM_DEBUG + size_t pad = sizeof(dbg_mem_header_t); +#else + size_t pad = 0; +#endif + DPRINTF(4, (KERN_WARNING "%s()\n", __func__)); + + // small object pools + transaction_cache = kmem_cache_create("binder_transaction_t", sizeof(binder_transaction_t)+pad, 0, cache_flags, transaction_slab_xtor); + if (!transaction_cache) return -ENOMEM; + thread_cache = kmem_cache_create("binder_thread_t", sizeof(binder_thread_t)+pad, 0, cache_flags, thread_slab_xtor); + if (!thread_cache) return -ENOMEM; + node_cache = kmem_cache_create("binder_node_t", sizeof(binder_node_t)+pad, 0, cache_flags, node_slab_xtor); + if (!node_cache) return -ENOMEM; + local_mapping_cache = kmem_cache_create("local_mapping_t", sizeof(local_mapping_t)+pad, 0, cache_flags, local_mapping_slab_xtor); + if (!local_mapping_cache) return -ENOMEM; + reverse_mapping_cache = kmem_cache_create("reverse_mapping_t", sizeof(reverse_mapping_t)+pad, 0, cache_flags, reverse_mapping_slab_xtor); + if (!reverse_mapping_cache) return -ENOMEM; + range_map_cache = kmem_cache_create("range_map_t", sizeof(range_map_t)+pad, 0, cache_flags, range_map_slab_xtor); + if (!range_map_cache) return -ENOMEM; + + // hash tables + pid_table = kmalloc(sizeof(void *) << PID_HASH_BITS, GFP_KERNEL); + if (!pid_table) return -ENOMEM; + memset(pid_table, 0, sizeof(void *) << PID_HASH_BITS); + return 0; +} + +static int destroy_pools(void) +{ + int res = 0; +#if BND_MEM_DEBUG + dbg_mem_header_t *m, *mn; +#endif + DPRINTF(4, (KERN_WARNING "%s()\n", __func__)); + + /* + * These can fail if we haven't free'd all of the objects we've allocated. + */ + +#if BND_MEM_DEBUG + + + DPRINTF(4, (KERN_WARNING "%s() dbg_active_memory = %p\n", __func__, dbg_active_memory)); + m = dbg_active_memory; + while(m) { + mn = m->next; + if(m->state == 0x11111111) { + printk(KERN_WARNING "%s() memory still in use: %p slab %p\n", __func__, m + 1, m->slab); + dbg_kmem_cache_free(m->slab, m + 1); + } + m = mn; + } +#endif + + kmem_cache_destroy(transaction_cache); + kmem_cache_destroy(thread_cache); + kmem_cache_destroy(node_cache); + kmem_cache_destroy(local_mapping_cache); + kmem_cache_destroy(reverse_mapping_cache); + kmem_cache_destroy(range_map_cache); + if (pid_table) kfree(pid_table); + return res; +} + +static int __init init_binder(void) +{ + struct class_device *simple; + int result; + dev_t dev = 0; + + result = create_pools(); + if (result) { + goto free_pools; + } + + result = alloc_chrdev_region(&dev, BINDER_MINOR, BINDER_NUM_DEVS, binder_name); + if (result < 0) { + printk(KERN_WARNING "init_binder: alloc_chrdev_region() failed: %d\n", result); + return result; + } + + binder_major = MAJOR(dev); + binder_class = CLASS_SIMPLE_CREATE(THIS_MODULE, "binderipc"); + if (IS_ERR(binder_class)) { + result = PTR_ERR(binder_class); + printk(KERN_WARNING "init_binder: CLASS_SIMPLE_CREATE() failed: %d\n", result); + goto unalloc; + } + + memset(&binder_device, 0, sizeof(binder_device)); // overkill, but we don't care + cdev_init(&binder_device.cdev, &binder_fops); + binder_device.cdev.owner = THIS_MODULE; + result = cdev_add(&binder_device.cdev, dev, BINDER_NUM_DEVS); + if (result < 0) { + printk(KERN_WARNING "init_binder: cdev_add() failed: %d\n", result); + goto unregister_class; + } + + void* mem = kzalloc(sizeof(*simple), GFP_KERNEL); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) + simple = CLASS_SIMPLE_DEVICE_ADD(binder_class, dev, NULL, "%s", BINDER_NAME); +#else + // ARGH API CHANGE!!! + simple = CLASS_SIMPLE_DEVICE_ADD(binder_class, NULL, dev, NULL, "%s", BINDER_NAME); +#endif + if (IS_ERR(simple)) { + result = PTR_ERR(simple); + goto unadd_cdev; + } + + goto exit0; + +unadd_cdev: + cdev_del(&binder_device.cdev); +unregister_class: + CLASS_SIMPLE_DESTROY(binder_class); +unalloc: + unregister_chrdev_region(binder_major, BINDER_NUM_DEVS); +free_pools: + destroy_pools(); +exit0: + return result; +} + +static void __exit cleanup_binder(void) +{ + CLASS_SIMPLE_DEVICE_REMOVE(MKDEV(binder_major, 0)); + cdev_del(&binder_device.cdev); + CLASS_SIMPLE_DESTROY(binder_class); + unregister_chrdev_region(binder_major, BINDER_NUM_DEVS); + destroy_pools(); +} + + +module_init(init_binder); +module_exit(cleanup_binder); + +static int binder_open(struct inode *nodp, struct file *filp) +{ + binder_proc_t *proc; + + //printk(KERN_WARNING "%s(%p %p) (pid %d)\n", __func__, nodp, filp, current->pid); + // We only have one device, so we don't have to dig into the inode for it. + + down(&maps_lock); + proc = new_binder_proc(); + filp->private_data = proc; + up(&maps_lock); + printk(KERN_WARNING "%s(%p %p) (pid %d) got %p\n", __func__, nodp, filp, current->pid, proc); + if(proc == NULL) + return -ENOMEM; + return 0; +} + +static int binder_release(struct inode *nodp, struct file *filp) +{ + binder_proc_t *that; + binder_thread_t *thread; + struct hlist_node *_p, *_pp; + int index; + printk(KERN_WARNING "%s(%p %p) (pid %d) pd %p\n", __func__, nodp, filp, current->pid, filp->private_data); + that = filp->private_data; + if (that) { + filp->private_data = NULL; + + // ensure the process stays around until we can verify termination + index = 1 << pid_hash_bits; + + DPRINTF(5, (KERN_WARNING "%s(%p) freeing threads\n", __func__, that)); + + down(&maps_lock); + while (index--) { + hlist_for_each_entry_safe(thread, _p, _pp, pid_table + index, node) { + if (thread->m_team == that) { + DPRINTF(5, (KERN_WARNING "%s(%p) freeing thread %d\n", __func__, that, thread->m_thid)); + hlist_del(&thread->node); + BND_RELEASE(binder_thread, thread, STRONG, that); + //BND_RELEASE(binder_thread, thread, WEAK, that); + } + } + } + DPRINTF(5, (KERN_WARNING "%s(%p) done freeing threads\n", __func__, that)); + up(&maps_lock); + + binder_proc_Die(that, FALSE); + BND_RELEASE(binder_proc, that, STRONG, that); + } + else printk(KERN_WARNING "%s(pid %d): couldn't find binder_proc to Die()\n", __func__, current->pid); + return 0; +} + +#if USE_UNLOCKED_IOCTL +static long binder_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int binder_ioctl(struct inode *nodp, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + binder_thread_t *thread; + struct binder_proc *proc; + int rv; + + if (_IOC_TYPE(cmd) != BINDER_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > BINDER_IOC_MAXNR) return -ENOTTY; + + DPRINTF(5, (KERN_WARNING "%s: %08x\n", __func__, cmd)); + + // find the thread tracking structure + rv = down_interruptible(&maps_lock); + if(rv != 0) + return rv; + proc = filp->private_data; + BND_ASSERT(proc != NULL, "ioctl called after release"); + if(proc == NULL || !binder_proc_IsAlive(proc)) + thread = NULL; + else + thread = find_thread(current->pid, proc, cmd == BINDER_THREAD_EXIT); + if(thread != NULL) { + BND_ACQUIRE(binder_thread, thread, WEAK, thread); + thread->attachedToThread = TRUE; + } + + up(&maps_lock); + if(proc == NULL || !binder_proc_IsAlive(proc)) + return -ECONNREFUSED; + if (thread == NULL) + return -ENOMEM; + + //BND_ASSERT(thread->m_team == proc, "bad thread process ptr"); + if(thread->m_team != proc) { + printk( KERN_WARNING "%s: cmd %08x process ptr mismatch, " + "thread has %p, expected %p\n", + __func__, cmd, thread->m_team, proc ); + return -EIO; + } + + rv = binder_thread_Control(thread, cmd, (void*)arg); + BND_RELEASE(binder_thread, thread, WEAK, thread); + return rv; +} + +static int binder_mmap(struct file * filp, struct vm_area_struct * vma) +{ + // FIXME: Unil we see a device with ZONE_HIGH memory (currently, greater + // than 896MB RAM) we don't need to worry about alloc_page. + vma->vm_ops = &binder_vm_ops; + vma->vm_flags |= VM_RESERVED | VM_READ | VM_RAND_READ | VM_IO | VM_DONTCOPY | VM_DONTEXPAND; + vma->vm_flags &= ~(VM_SHARED); + vma->vm_private_data = filp->private_data; + binder_vma_open(vma); + return 0; +} + +static void binder_vma_open(struct vm_area_struct * area) +{ + binder_proc_t *that; + DPRINTF(5, (KERN_WARNING "binder_vma_open()\n")); + // Do we have to watch for clone()'d processes and hunt down the + // appropriate binder_proc_t? + + that = area->vm_private_data; + // initialize our free space map + if (that->m_freeMap.rb_node == NULL) { + range_map_t *rm = kmem_cache_alloc(range_map_cache, GFP_KERNEL); + that->m_mmap_start = rm->start = area->vm_start; + rm->end = area->vm_end; + rm->page = NULL; + rm->team = that; + BND_LOCK(that->m_map_pool_lock); + binder_proc_free_map_insert(that, rm); + BND_UNLOCK(that->m_map_pool_lock); + DPRINTF(5, (KERN_WARNING "vma(%08lx, %08lx) for %08x\n", rm->start, rm->end, (unsigned int)that)); + } +#if 0 + else printk(KERN_WARNING " --- didn't reconstruct the initial free-map\n"); +#endif +} + +static void binder_vma_close(struct vm_area_struct * area) +{ + // Uh, what? + DPRINTF(5, (KERN_WARNING "binder_vma_close() for %08x\n", (unsigned int)area->vm_private_data)); +} + +static struct page * binder_vma_nopage(struct vm_area_struct * area, unsigned long address, int *type) +{ + struct page *pageptr = NULL; + // the private data holds a pointer to owning binder_proc + binder_proc_t *bp = (binder_proc_t *)area->vm_private_data; + DPRINTF(5, ("binder_vma_nopage(%p, %08lx)\n", bp, address)); + // make sure this address corresponds to a valid transaction + if (!binder_proc_ValidTransactionAddress(bp, address, &pageptr)) + return NOPAGE_SIGBUS; + // bump the kernel reference counts + get_page(pageptr); + // record the fault type + if (type) *type = VM_FAULT_MINOR; + // return the page + return pageptr; +} + +void my_dump_stack(void) { printk(KERN_WARNING ""); dump_stack(); } + +void soft_yield() +{ + static int i = 0; + i++; + if(i < 10) + return; + i = 0; + yield(); +} + +#if BND_MEM_DEBUG + +#undef kmem_cache_alloc +#undef kmem_cache_free + +void *dbg_kmem_cache_alloc(struct kmem_cache *a, unsigned int b) +{ + dbg_mem_header_t *p; + p = kmem_cache_alloc(a, b); + BND_ASSERT(p != NULL, "memory allocation failed"); + if(p == NULL) + return NULL; + if(p->state != 0x00000000) { + if(p->state != 0x22222222) + DPRINTF(5, (KERN_WARNING "%s: kmem_cache_alloc(%p, %d) BAD PTR %p = 0x%08lx\n", __func__, a, b, p, p->state)); + else + DPRINTF(6, (KERN_WARNING "%s: kmem_cache_alloc(%p, %d) NEW PTR %p = 0x%08lx\n", __func__, a, b, p, p->state)); + } + p->state = 0x11111111; + p++; + DPRINTF(6, (KERN_WARNING "%s: kmem_cache_alloc(%p, %d) returned %p\n", __func__, a, b, p)); + return p; +} + +void dbg_kmem_cache_free(struct kmem_cache *a, void *b) +{ + dbg_mem_header_t *p = b; + DPRINTF(6, (KERN_WARNING "%s: kmem_cache_free(%p, %p)\n", __func__, a, p)); + p--; + if(p->state != 0x11111111) { + printk(KERN_WARNING "%s: kmem_cache_free(%p, %p) BAD ARG 0x%08lx\n", __func__, a, p, p->state); + dump_stack(); + return; + } + + p->state = 0x22222222; + kmem_cache_free(a, p); +} + +#endif diff -Nru linux-2.6.23/drivers/binder/binder_defs.h kernel.android/drivers/binder/binder_defs.h --- linux-2.6.23/drivers/binder/binder_defs.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_defs.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,340 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef BINDER_DEFS_H +#define BINDER_DEFS_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(CONFIG_ARM) +/* Define this if you want to use the linux threads hack on ARM */ +#define USE_LINUXTHREADS +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) +#define assert_spin_locked(x) +#endif + +extern struct kmem_cache *transaction_cache; +extern struct kmem_cache *thread_cache; +extern struct kmem_cache *node_cache; +extern struct kmem_cache *local_mapping_cache; +extern struct kmem_cache *reverse_mapping_cache; +extern struct kmem_cache *range_map_cache; + +#define HASH_BITS 8 +#define HASH_SIZE (1 << HASH_BITS) + +enum ref_count_type { + STRONG = 1, + WEAK = 2 +}; + +/* ------------------------------------------------------------------ */ +/* --------------------- COMPILING AS A DRIVER ---------------------- */ +/* ------------------------------------------------------------------ */ + +void soft_yield(void); + +#define STOP_ON_ASSERT //msleep_interruptible(1000*60*60*24*7) + +#define BND_MEM_DEBUG 0 // slab destructors are no longer available + +#if 0 + +#define BINDER_DEBUG 1 +#define VALIDATES_BINDER 0 +#define DIPRINTF(level,a) do { if (level <= 9) printk a; } while(0) +#define DPRINTF(level,a) do { if (level <= 9) { printk a; soft_yield();} } while(0) +#define BND_FAIL(msg) +#define BND_ASSERT(cond, msg) do { if (!(cond)) { printk(KERN_WARNING "BND_ASSERT file %s line %d: %s\n", __FILE__, __LINE__, msg); dump_stack(); STOP_ON_ASSERT;} } while (FALSE) +#define DBTRANSACT(x) printk x +#define DBSHUTDOWN(x) printk x +#define DBSPAWN(x) printk x +#define DBSTACK(x) printk x +#define DBLOCK(x) printk x +#define DBREFS(x) printk x +#define DBREAD(x) printk x +#define DBDEATH(x) printk x + +#else +#define BINDER_DEBUG 0 +#define DIPRINTF(level,a) +#define DPRINTF(level,a) +#define BND_FAIL(msg) +//#define BND_ASSERT(cond, msg) if (!(cond)) printk(KERN_WARNING "BND_ASSERT file %s line %d: %s\n", __FILE__, __LINE__, msg) +#define BND_ASSERT(cond, msg) do { if (!(cond)) { printk(KERN_WARNING "BND_ASSERT file %s line %d: %s\n", __FILE__, __LINE__, msg); dump_stack(); } } while (FALSE) +#define DBTRANSACT(x) +#define DBSHUTDOWN(x) //printk x +#define DBSPAWN(x) +#define DBSTACK(x) +#define DBLOCK(x) +#define DBREFS(x) +#define DBREAD(x) +#define DBDEATH(x) +#endif + +// errors triggered by userspace bugs +#define UPRINTF(a) do { printk a; } while(0) +#define BND_UASSERT(cond, msg) if (!(cond)) printk(KERN_WARNING "BND_UASSERT file %s line %d: %s\n", __FILE__, __LINE__, msg) + +#if BND_MEM_DEBUG +void *dbg_kmem_cache_alloc(struct kmem_cache *a, unsigned int b); +void dbg_kmem_cache_free(struct kmem_cache *a, void *b); + +#define kmem_cache_alloc dbg_kmem_cache_alloc +#define kmem_cache_free dbg_kmem_cache_free +#endif + +struct binder_thread; + +typedef ssize_t status_t; + +//typedef unsigned int bool; +#define FALSE (0) +#define TRUE (~FALSE) + +/* Special function, implemented in binder.c, to try to find + a binder_thread structure for a pid. If 'create' is TRUE, + a new structure will be created for you (unattached to + a process) if it doesn't already exist; otherwise it will + return NULL. Returns with a strong reference held on the + thread. + + *** NOTE: Must not call this while holding a thread or + process lock! */ +struct binder_thread * check_for_thread(pid_t thread_pid, bool create); + +/* Special function, implemented in binder.c, for a parent to + lookup (or pre-create) the state for main thread of a child + process it is spawning. This function calls + binder_thread_SetParentThread() for you on the child thread, + and returns with a strong reference held on the thread. + + *** NOTE: Must not call this while holding a thread or + process lock! */ +struct binder_thread * attach_child_thread(pid_t child_pid, struct binder_thread *parent); + +/* Special function, implemented in binder.c, to remove a + thread structure from the global list. This needs to be + called when using the above two functions to create such + a structure, to remove it from the list when it is no + longer used. A strong reference is removed from the thread + and, if the strong count goes to zero AND the structure has + not yet been accessed by its user space thread, then the + thread structure will be removed from the list. + + *** NOTE: Must not call this while holding a thread or + process lock! */ +void forget_thread(struct binder_thread *thread); + +// Perform an accuire/release on an object. +#define BND_ACQUIRE(cname, that, type, id) cname##_Acquire(that, type) +#define BND_ATTEMPT_ACQUIRE(cname, that, type, id) cname##_AttemptAcquire(that, type) +#define BND_FIRST_ACQUIRE(cname, that, type, id) cname##_ForceAcquire(that, type) +#define BND_FORCE_ACQUIRE(cname, that, id) cname##_ForceAcquire(that, STRONG) +#define BND_RELEASE(cname, that, type, id) cname##_Release(that, type) + +// Declare acquire/release methods for a class. +#define BND_DECLARE_ACQUIRE_RELEASE(cname) \ + void cname##_Acquire(cname##_t *that, s32 type); \ + int cname##_ForceAcquire(cname##_t *that, s32 type); \ + int cname##_Release(cname##_t *that, s32 type); \ +/**/ + +// Declare attempt acquire method for a class. +#define BND_DECLARE_ATTEMPT_ACQUIRE(cname) \ + int cname##_AttemptAcquire(cname##_t *that, s32 type); \ +/**/ + +extern void dump_stack(void); +// Implement acquire/release methods for a class. +#define BND_IMPLEMENT_ACQUIRE_RELEASE(cname) \ +void \ +cname##_Acquire(cname##_t *that, s32 type) \ +{ \ + int res; \ + if (type == STRONG) { \ + res = atomic_inc_return(&that->m_primaryRefs); \ + BND_ASSERT(res > 1, "STRONG Acquire without strong ref"); \ + } \ + res = atomic_inc_return(&that->m_secondaryRefs); \ + if (type == STRONG) { \ + BND_ASSERT(res > 1, "STRONG Acquire without weak ref"); \ + } \ + else { \ + BND_ASSERT(res > 1, "WEAK Acquire without weak ref"); \ + } \ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) s:%d w:%d\n", __func__, that, type == STRONG ? "STRONG" : "WEAK", atomic_read(&that->m_primaryRefs), atomic_read(&that->m_secondaryRefs)));\ + /*dump_stack()*/;\ +} \ +int \ +cname##_ForceAcquire(cname##_t *that, s32 type) \ +{ \ + int res; \ + res = atomic_inc_return(&that->m_secondaryRefs); \ + if (type == STRONG) { \ + res = atomic_inc_return(&that->m_primaryRefs); \ + } \ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) s:%d w:%d\n", __func__, that, type == STRONG ? "STRONG" : "WEAK", atomic_read(&that->m_primaryRefs), atomic_read(&that->m_secondaryRefs)));\ + return res-1; \ +} \ +int \ +cname##_Release(cname##_t *that, s32 type) \ +{ \ + int rv1=-2, rv2=-2; \ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) s:%d w:%d\n", __func__, that, type == STRONG ? "STRONG" : "WEAK", atomic_read(&that->m_primaryRefs), atomic_read(&that->m_secondaryRefs)));\ + if(type == STRONG) { \ + BND_ASSERT(atomic_read(&that->m_primaryRefs) > 0, "Strong reference underflow");\ + } \ + BND_ASSERT(atomic_read(&that->m_secondaryRefs) > 0, "Weak reference underflow");\ + /*dump_stack()*/;\ + switch (type) { \ + case STRONG: \ + if ((rv1 = atomic_dec_return(&that->m_primaryRefs)) == 0) { \ + cname##_Released(that); \ + } \ + case WEAK: \ + if ((rv2 = atomic_dec_return(&that->m_secondaryRefs)) == 0) {\ + cname##_destroy(that); \ + } \ + } \ + return ((type == STRONG) ? rv1 : rv2) + 1; \ +} \ +/**/ + +// Implement attempt acquire method for a class. +#define BND_IMPLEMENT_ATTEMPT_ACQUIRE(cname) \ +int \ +cname##_AttemptAcquire(cname##_t *that, s32 type) \ +{ \ + int cur; \ + switch (type) { \ + case STRONG: \ + cur = atomic_read(&that->m_primaryRefs); \ + while (cur > 0 && \ + !cmpxchg32( &that->m_primaryRefs.counter, \ + &cur, cur+1)); \ + if (cur <= 0) {\ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) FAILED!\n", __func__, that, type == STRONG ? "STRONG" : "WEAK"));\ + /*dump_stack()*/;\ + return FALSE; \ + }\ + cur = atomic_inc_return(&that->m_secondaryRefs); \ + BND_ASSERT(cur > 1, "ATTEMPT ACQUIRE STONG without WEAK ref"); \ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) s:%d w:%d\n", __func__, that, type == STRONG ? "STRONG" : "WEAK", atomic_read(&that->m_primaryRefs), atomic_read(&that->m_secondaryRefs)));\ + /*dump_stack()*/;\ + return TRUE; \ + case WEAK: \ + cur = atomic_read(&that->m_secondaryRefs); \ + while (cur > 0 && \ + !cmpxchg32( &that->m_secondaryRefs.counter, \ + &cur, cur+1)); \ + if (cur <= 0) {\ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) FAILED!\n", __func__, that, type == STRONG ? "STRONG" : "WEAK"));\ + /*dump_stack()*/;\ + return FALSE; \ + }\ + DPRINTF(5, (KERN_WARNING "%s(%p, %s) s:%d w:%d\n", __func__, that, type == STRONG ? "STRONG" : "WEAK", atomic_read(&that->m_primaryRefs), atomic_read(&that->m_secondaryRefs)));\ + /*dump_stack()*/;\ + return TRUE; \ + } \ + return FALSE; \ +} \ +/**/ + +extern spinlock_t cmpxchg32_spinner; + +// Quick hack -- should be checking for x86, not ARM. + +#if defined(CONFIG_ARM) + +static __inline__ int cmpxchg32(volatile int *atom, int *val, int newVal) { + unsigned long flags; + spin_lock_irqsave(&cmpxchg32_spinner, flags); + if (*atom == *val) { + *atom = newVal; + spin_unlock_irqrestore(&cmpxchg32_spinner, flags); + return 1; + } + *val = *atom; + spin_unlock_irqrestore(&cmpxchg32_spinner, flags); + return 0; +}; + +#else + +static __inline__ int compare_and_swap32(volatile int *location, int oldValue, int newValue) +{ + int success; + asm volatile("lock; cmpxchg %%ecx, (%%edx); sete %%al; andl $1, %%eax" + : "=a" (success) : "a" (oldValue), "c" (newValue), "d" (location)); + return success; +} + +static __inline__ bool cmpxchg32(volatile int *atom, int *value, int newValue) +{ + int success = compare_and_swap32(atom, *value, newValue); + if (!success) + *value = *atom; + + return success; +}; + +#endif + +#define BND_LOCK(x) do { down(&(x)); \ + BND_ASSERT(atomic_read(&((x).count)) <= 0, "BND_LOCK() lock still free"); } while (0) +#define BND_UNLOCK(x) do { \ + BND_ASSERT(atomic_read(&((x).count)) <= 0, "BND_UNLOCK() lock already free"); \ + up(&(x)); } while (0) + +#if defined(CONFIG_ARM) +// __cpuc_flush_user_range is arm specific, but the generic function need a +// vm_area_struct and will flush the entire page. +#define BND_FLUSH_CACHE(start, end) do { \ + __cpuc_flush_user_range((size_t)start & ~(L1_CACHE_BYTES-1), L1_CACHE_ALIGN((size_t)end), 0); \ + } while(0) +#else +#define BND_FLUSH_CACHE(start, end) +#endif + +#define B_CAN_INTERRUPT (1) + +#define B_INFINITE_TIMEOUT ((~(0ULL))>>1) +#define B_ABSOLUTE_TIMEOUT (1) + +#define B_BAD_THREAD_ID ((pid_t)0) +#define B_REAL_TIME_PRIORITY (10) +#define B_NORMAL_PRIORITY (80) +#define B_LOW_PRIORITY (100) + +#define B_MIN_PRIORITY_VAL (5) +#define B_MAX_PRIORITY_VAL (100) + +#endif // BINDER_DEFS_H diff -Nru linux-2.6.23/drivers/binder/binder_node.c kernel.android/drivers/binder/binder_node.c --- linux-2.6.23/drivers/binder/binder_node.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_node.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,140 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "binder_node.h" +#include "binder_proc.h" +#include "binder_transaction.h" + +#define PURGATORY 0 +#if PURGATORY +static DECLARE_MUTEX(sem); +static binder_node_t* head = NULL; +static binder_node_t** tail = &head; +static int count = 0; + +static void my_free_node(binder_node_t *t) +{ + down(&sem); + *tail = t; + tail = (binder_node_t**)&t->m_ptr; + count++; + if (count > 20) { + t = head; + head = (binder_node_t*)head->m_ptr; + kmem_cache_free(node_cache, t); + count--; + } + up(&sem); +} +#define ALLOC_NODE kmem_cache_alloc(node_cache, GFP_KERNEL) +#define FREE_NODE(x) my_free_node(x) +#else +#define ALLOC_NODE kmem_cache_alloc(node_cache, GFP_KERNEL) +#define FREE_NODE(x) kmem_cache_free(node_cache, x) +#endif + +static atomic_t g_count = ATOMIC_INIT(0); + +int +binder_node_GlobalCount() +{ + return atomic_read(&g_count); +} + +BND_IMPLEMENT_ACQUIRE_RELEASE(binder_node); +BND_IMPLEMENT_ATTEMPT_ACQUIRE(binder_node); +// BND_IMPLEMENT_FORCE_ACQUIRE(binder_node); + +/* + * For the process which manages the contexts, we treat ptr == NULL specially. + * In particular, all transactions with a target descriptor of 0 get routed to + * the manager process and the target pointer the process receives gets set to + * NULL. We don't permit any team to send a binder with a NULL ptr, so we can + * never confuse the mappings. + */ +binder_node_t *binder_node_init(binder_proc_t *team, void *ptr, void *cookie) +{ + binder_node_t *that = ALLOC_NODE; + atomic_inc(&g_count); + DPRINTF(5, (KERN_WARNING "%s(team=%p, ptr=%p, cookie=%p): %p\n", __func__, + team, ptr, cookie, that)); + atomic_set(&that->m_primaryRefs, 0); + atomic_set(&that->m_secondaryRefs, 0); + that->m_ptr = ptr; + that->m_cookie = cookie; + that->m_home = team; + if (that->m_home) BND_ACQUIRE(binder_proc, that->m_home, WEAK, that); + return that; +} + +void binder_node_destroy(binder_node_t *that) +{ + atomic_dec(&g_count); + DPRINTF(4, (KERN_WARNING "%s(%p): ptr=%p, cookie=%p\n", __func__, that, + that->m_ptr, that->m_cookie)); + if (that->m_home) { + if (that->m_ptr) { + binder_proc_t* proc = binder_node_AcquireHome(that, that); + if (proc) { + binder_proc_Transact(proc, binder_transaction_CreateRef(tfDecRefs, that->m_ptr, that->m_cookie, proc)); + binder_proc_RemoveLocalMapping(proc, that->m_ptr, that); + BND_RELEASE(binder_proc, proc, STRONG, that); + } + } + BND_RELEASE(binder_proc, that->m_home, WEAK, that); + } + FREE_NODE(that); +} + +void +binder_node_Released(binder_node_t *that) +{ + binder_proc_t* proc = binder_node_AcquireHome(that, that); + DPRINTF(4, (KERN_WARNING "%s(%p): ptr=%p\n", __func__, that, that->m_ptr)); + if (proc) { + DPRINTF(5, (KERN_WARNING " -- m_secondaryRefs=%d\n",atomic_read(&that->m_secondaryRefs))); + binder_proc_Transact(proc, binder_transaction_CreateRef(tfRelease,that->m_ptr,that->m_cookie,proc)); + binder_proc_RemoveLocalStrongRef(proc, that); + BND_RELEASE(binder_proc, proc, STRONG, that); + } +} + +binder_proc_t* +binder_node_AcquireHome(binder_node_t *that, const void *id) +{ + if (that->m_home && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_home, STRONG, id)) { + return that->m_home; + } + return NULL; +} + +status_t +binder_node_Send(binder_node_t *that, struct binder_transaction *t) +{ + binder_proc_t* proc = binder_node_AcquireHome(that, that); + if (proc) { + status_t res = binder_proc_Transact(proc, t); + BND_RELEASE(binder_proc, proc, STRONG, that); + return res; + } + + if (t->sender) binder_thread_ReplyDead(t->sender); + binder_transaction_Destroy(t); + return 0; +} + diff -Nru linux-2.6.23/drivers/binder/binder_node.h kernel.android/drivers/binder/binder_node.h --- linux-2.6.23/drivers/binder/binder_node.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_node.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,70 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef BINDER2_NODE_H +#define BINDER2_NODE_H + +#include "binder_defs.h" +#include "binder_proc.h" + +typedef struct binder_node { + atomic_t m_primaryRefs; + atomic_t m_secondaryRefs; + void * m_ptr; + void * m_cookie; + binder_proc_t * m_home; +} binder_node_t; + +int binder_node_GlobalCount(void); + +binder_node_t * binder_node_init(binder_proc_t *team, void *ptr, void *cookie); +void binder_node_destroy(binder_node_t *that); + +void binder_node_Released(binder_node_t *that); + +// Return a new strong reference on the node's home team, or NULL +// if the team no longer exists. Be sure to release the reference +// (via BND_RELEASE(binder_proc, team, STRONG, id)) if the return is non-NULL. +binder_proc_t* binder_node_AcquireHome(binder_node_t *that, const void *id); + +// Dispatch a transaction to the node's process. +status_t binder_node_Send(binder_node_t *that, struct binder_transaction *t); + +BND_DECLARE_ACQUIRE_RELEASE(binder_node); +// BND_DECLARE_FORCE_ACQUIRE(binder_node); + +/* Super-special AttemptAcquire() that also lets you attempt + to acquire a secondary ref. But note that binder_proc_t is + the ONLY one who can attempt a secondary, ONLY while holding + its lock, for the simple reason that binder_node's destructor + unregisters itself from the team. In other words, it's a + dihrty hawck. +*/ +BND_DECLARE_ATTEMPT_ACQUIRE(binder_node); + +/* Send a transaction to this node. */ +// void binder_node_Send(struct binder_transaction *t); +// void * binder_node_Ptr(binder_node_t *that); +// binder_proc_t * binder_node_Home(binder_node_t *that); + +#define binder_node_Ptr(that) ((that)->m_ptr) +#define binder_node_Cookie(that) ((that)->m_cookie) +#define binder_node_IsAlive(that) (binder_proc_IsAlive((that)->m_home)) +#define binder_node_IsRoot(that) ((that)->m_isRoot) + +#endif // BINDER2_NODE_H diff -Nru linux-2.6.23/drivers/binder/binder_proc.c kernel.android/drivers/binder/binder_proc.c --- linux-2.6.23/drivers/binder/binder_proc.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_proc.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,2215 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +// #include +#include +#include +#include + +#include "binder_defs.h" +#include "binder_proc.h" +#include "binder_thread.h" +#include "binder_node.h" +#include "binder_transaction.h" +#include "iobuffer.h" + +#define BND_PROC_MAX_IDLE_THREADS 3 + +static inline unsigned long calc_order_from_size(unsigned long size) +{ +#if 0 + unsigned long order = 0; + if (size) { + size -= 1; + size *= 2; + } + size >>= PAGE_SHIFT+1; + while (size) { + order++; + size >>= 1; + } + return order; +#else + return size ? get_order(size) : 0; +#endif +} + +static void binder_proc_init(binder_proc_t *that); +static void binder_proc_spawn_looper(binder_proc_t *that); +static void binder_proc_wakeup_timer(unsigned long); +static void binder_proc_idle_timer(unsigned long); +static void binder_proc_send_death_notification(binder_proc_t *that, death_notification_t *death); +static void binder_proc_death_notification_dec_ref(binder_proc_t *that, death_notification_t *death, bool locked); +static void binder_proc_RemoveThreadFromWaitStack(binder_proc_t *that, binder_thread_t *thread); + +static void set_thread_priority(pid_t thread, int priority) +{ + int nice; + + // The following must match SysThreadChangePriority in libbinder. + if(priority >= 80) + { + // Normal to low priority + // map 80..100 to 0..19 + nice = priority - 80; + if(nice > 19) + nice = 19; + } + else + { + // Normal priority or better + // map 0..79 to -20..-1 + nice = priority-3 - 80; + nice /= 4; + } + //printk("set_thread_priority tid %d pri %d == nice %d\n", thread, priority, nice); + set_user_nice(find_task_by_pid(thread), nice); +} + + +void binder_proc_init(binder_proc_t *that) +{ + int i; + atomic_set(&that->m_primaryRefs, 0); + atomic_set(&that->m_secondaryRefs, 0); + init_MUTEX(&that->m_lock); + spin_lock_init(&that->m_spin_lock); + init_MUTEX(&that->m_map_pool_lock); + that->m_threads = NULL; + INIT_LIST_HEAD(&that->m_waitStack); + that->m_waitStackCount = 0; + that->m_wakeThreadMask = 0; + that->m_wakeupTime = B_INFINITE_TIMEOUT; + that->m_wakeupPriority = 10; + init_timer(&that->m_wakeupTimer); + that->m_wakeupTimer.function = &binder_proc_wakeup_timer; + that->m_wakeupTimer.data = (unsigned long)that; + init_timer(&that->m_idleTimer); + that->m_idleTimer.function = &binder_proc_idle_timer; + that->m_idleTimer.data = (unsigned long)that; + that->m_idleTimeout = 5*HZ; + that->m_replyTimeout = 5*HZ; + //that->m_idleTimeout = 5*60*HZ; + //that->m_replyTimeout = 5*60*HZ; + that->m_syncCount = 0; + that->m_freeCount = 0; + that->m_head = NULL; + that->m_tail = &that->m_head; + that->m_needFree = NULL; + that->m_state = 0; + for (i=0;im_localHash[i] = NULL; + that->m_reverseHash[i] = NULL; + } + that->m_numRemoteStrongRefs = 0; + that->m_rootObject = NULL; + that->m_rootStopsProcess = 0; + that->m_descriptors = NULL; + that->m_descriptorCount = 0; + that->m_waitingThreads = 0; + that->m_nonblockedThreads = 0; + that->m_maxThreads = 5; + //that->m_idlePriority = B_REAL_TIME_PRIORITY; + that->m_idlePriority = B_NORMAL_PRIORITY; + atomic_set(&that->m_loopingThreads, 0); +#if 0 + that->m_spawningThreads = 0; +#endif + that->m_rangeMap = RB_ROOT; + that->m_freeMap = RB_ROOT; + BND_FIRST_ACQUIRE(binder_proc, that, STRONG, that); + that->m_eventTransaction = binder_transaction_CreateEmpty(); + binder_transaction_SetEvent(that->m_eventTransaction, TRUE); + that->m_pool = NULL; + that->m_pool_active = 0; + INIT_HLIST_HEAD(&that->m_incoming_death_notifications); + INIT_HLIST_HEAD(&that->m_outgoing_death_notifications); + INIT_HLIST_HEAD(&that->m_pending_death_notifications); + INIT_HLIST_HEAD(&that->m_active_death_notifications); + INIT_HLIST_HEAD(&that->m_deleted_death_notifications); +} + +binder_proc_t * +new_binder_proc() +{ + // allocate a binder_proc_t from the slab allocator + binder_proc_t *that = (binder_proc_t*)kmalloc(sizeof(binder_proc_t), GFP_KERNEL); + BND_ASSERT(that != NULL, "failed to allocate binder_proc"); + if(that == NULL) + return NULL; + binder_proc_init(that); + DPRINTF(2, (KERN_WARNING "************* Creating binder_proc %p *************\n", that)); + return that; +} + +void +binder_proc_destroy(binder_proc_t *that) +{ + local_mapping_t *lm; + reverse_mapping_t *rm; + local_mapping_t *localMappings; + reverse_mapping_t *reverseMappings; + range_map_t *r; + struct rb_node *n; + int i; + bool first; + + DPRINTF(2, (KERN_WARNING "************* Destroying binder_proc %p *************\n", that)); + + BND_ASSERT(that->m_state & btCleaned, "binder_proc_Die wns not done"); + BND_ASSERT(!(that->m_state & btFreed), "already free"); + if(that->m_state & btFreed) + return; + + //DPRINTF(5, (KERN_WARNING "Binder team %p: collecting mappings.\n", that)); + lm = localMappings = NULL; + rm = reverseMappings = NULL; + for (i=0;im_localHash[i]) { + // mark the front of the list + if (!localMappings) lm = localMappings = that->m_localHash[i]; + // or tack this chain on the end + else lm->next = that->m_localHash[i]; + // run to the end of the chain + while (lm->next) lm = lm->next; + // mark this chain handled + that->m_localHash[i] = NULL; + } + if (that->m_reverseHash[i]) { + // ditto for reverse mappings + if (!reverseMappings) rm = reverseMappings = that->m_reverseHash[i]; + else rm->next = that->m_reverseHash[i]; + while (rm->next) rm = rm->next; + that->m_reverseHash[i] = NULL; + } + } + + first = TRUE; + while ((lm = localMappings)) { + if (first) { + first = FALSE; + DBSHUTDOWN((KERN_WARNING "Binder team %p: cleaning up local mappings.\n", that)); + } + localMappings = lm->next; + // FIXME: send death notification + kmem_cache_free(local_mapping_cache, lm); + } + + first = TRUE; + while ((rm = reverseMappings)) { + if (first) { + first = FALSE; + DBSHUTDOWN((KERN_WARNING "Binder team %p: cleaning up reverse mappings.\n", that)); + } + reverseMappings = rm->next; + DBSHUTDOWN((KERN_WARNING "Removed reverse mapping from node %p to descriptor %d\n", + rm->node, rm->descriptor+1)); + // FIXME: decrement use count and possibly notify owner. It seems like we do this below. + kmem_cache_free(reverse_mapping_cache, rm); + } + + /* + for (i=0; im_localHash[i] == NULL, "Leaking some local mappings!"); + BND_ASSERT(that->m_reverseHash[i] == NULL, "Leaking some reverse mappings!"); + } + */ + + // Free up any items in the transaction data pool. + BND_LOCK(that->m_map_pool_lock); + n = rb_first(&that->m_rangeMap); + while (n) { + r = rb_entry(n, range_map_t, rm_rb); + n = rb_next(n); + + rb_erase(&r->rm_rb, &that->m_rangeMap); + //__free_pages(r->page, calc_order_from_size(r->end - r->start)); + kmem_cache_free(range_map_cache, r); + } + n = rb_first(&that->m_freeMap); + while (n) { + r = rb_entry(n, range_map_t, rm_rb); + n = rb_next(n); + rb_erase(&r->rm_rb, &that->m_rangeMap); + kmem_cache_free(range_map_cache, r); + } + BND_UNLOCK(that->m_map_pool_lock); + + // free_lock(&that->m_lock); + that->m_state |= btFreed; + kfree(that); +} + +void +binder_proc_SetRootObject(binder_proc_t *that, struct binder_node *node) +{ + BND_LOCK(that->m_lock); + if (that->m_rootObject == NULL) that->m_rootObject = node; + BND_UNLOCK(that->m_lock); +} + +void +binder_proc_Stop(binder_proc_t *that, bool now) +{ + bool goodbye; + + DBLOCK((KERN_WARNING "binder_proc_Stop() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + that->m_rootStopsProcess = TRUE; + goodbye = now || that->m_rootObject == (binder_node_t*)-1; + + BND_UNLOCK(that->m_lock); + + if (goodbye) binder_proc_Die(that, FALSE); +} + +bool +binder_proc_AddThread(binder_proc_t *that, binder_thread_t *t) +{ + BND_FIRST_ACQUIRE(binder_thread, t, STRONG, 0); + BND_LOCK(that->m_lock); + if (binder_proc_IsAlive(that)) { + t->next = that->m_threads; + that->m_threads = t; + BND_UNLOCK(that->m_lock); + } else { + BND_UNLOCK(that->m_lock); + BND_RELEASE(binder_thread, t, STRONG, that); + t = NULL; + } + DBSHUTDOWN((KERN_WARNING "%s(%p): %p\n", __func__, that, t)); + return t != NULL; +} + +void +binder_proc_RemoveThread(binder_proc_t *that, binder_thread_t *t) +{ + binder_thread_t **thread; + DBSHUTDOWN((KERN_WARNING "%s(%p): %p\n", __func__, that, t)); + DBLOCK((KERN_WARNING "RemoveThread() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + for (thread = &that->m_threads; *thread && *thread != t; thread = &(*thread)->next) + ; + if (*thread) { + *thread = (*thread)->next; + } else { + DPRINTF(5, (KERN_WARNING "binder_team %p: RemoveThread of %d does not exist\n", that, t->m_thid)); + } + + // If this is the last thread, the team is dead. + if (!that->m_threads) binder_proc_Die(that, TRUE); + else BND_UNLOCK(that->m_lock); +} + +void +binder_proc_Released(binder_proc_t *that) +{ + DBSHUTDOWN((KERN_WARNING "%s(%p)\n", __func__, that)); + binder_proc_Die(that, FALSE); +} + +void +binder_proc_Die(binder_proc_t *that, bool locked) +{ + binder_transaction_t *cmd; + binder_node_t *n; + binder_thread_t *thr; + descriptor_t *descriptors; + bool dying; + bool first; + binder_transaction_t *cmdHead; + binder_transaction_t *freeCmdHead; + s32 descriptorCount; + binder_thread_t *threads; + bool acquired; + struct hlist_node *_p, *_p2; + death_notification_t *death; + + DBSHUTDOWN((KERN_WARNING "*****************************************\n")); + DBSHUTDOWN((KERN_WARNING "**** %s(%p, %s)\n", __func__, that, locked ? "locked" : "unlocked")); + + // Make sure our destructor doesn't get called until Die() is done. + BND_ACQUIRE(binder_proc, that, WEAK, that); + + // Make sure that Released() doesn't get called if we are dying + // before all primary references have been removed. + acquired = BND_ATTEMPT_ACQUIRE(binder_proc, that, STRONG, that); + + if (!locked) { + DBLOCK((KERN_WARNING "%s() going to lock %p in %d\n", __func__, that, current->pid)); + BND_LOCK(that->m_lock); + } + dying = that->m_state&btDying; + that->m_state |= btDying; + BND_UNLOCK(that->m_lock); + + if (dying) { + DBSHUTDOWN((KERN_WARNING "racing to kill %p\n", that)); + while (!(that->m_state&btDead)) msleep(10); + BND_RELEASE(binder_proc, that, WEAK, that); + if (acquired) BND_RELEASE(binder_proc, that, STRONG, that); + DBSHUTDOWN((KERN_WARNING "race finished\n")); + return; + } + + /* + DPRINTF(5, (KERN_WARNING "Binder team %p: removing from driver.\n", that)); + remove_team(that->tgid); + delete_sem(that->m_spawnerSem); + that->m_spawnerSem = B_BAD_SEM_ID; + */ + + DBLOCK((KERN_WARNING "%s() #2 going to lock %p in %d\n", __func__, that, current->pid)); + BND_LOCK(that->m_lock); + + while(!hlist_empty(&that->m_outgoing_death_notifications)) { + binder_proc_t *observer_proc; + death = hlist_entry(that->m_outgoing_death_notifications.first, typeof(*death), observed_or_active); + hlist_del(&death->observed_or_active); + DBDEATH((KERN_WARNING "DeathNot %p: removed from proc %p m_outgoing_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + death->observed_proc = NULL; + observer_proc = death->observer_proc; + BND_UNLOCK(that->m_lock); + binder_proc_send_death_notification(observer_proc, death); + binder_proc_death_notification_dec_ref(observer_proc, death, FALSE); + BND_LOCK(that->m_lock); + } + + while(!hlist_empty(&that->m_incoming_death_notifications)) { + binder_proc_t *observed_proc; + death = hlist_entry(that->m_incoming_death_notifications.first, typeof(*death), observer); + DBDEATH((KERN_WARNING "DeathNot %p: removing from proc %p m_incoming_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + observed_proc = death->observed_proc; + if(observed_proc != NULL) { + if(observed_proc != that) { + // We need to grab the observed process' lock since the record + // is on the outgoing list on that process. + BND_UNLOCK(that->m_lock); + BND_LOCK(observed_proc->m_lock); + } + if(death->observed_proc != NULL) { + // If we are removing the record from the outgoing list it may + // have already been removed by the time we get the lock. + hlist_del(&death->observed_or_active); + DBDEATH((KERN_WARNING "DeathNot %p: removed from proc %p observed_or_active, refcnt=%d\n", + death, death->observed_proc, atomic_read(&death->ref_count))); + } + if(observed_proc != that) { + // Reacquire our own process lock. + BND_UNLOCK(observed_proc->m_lock); + BND_LOCK(that->m_lock); + } + if(death->observed_proc != NULL) { + // Release the reference we got from the list before we + // switched the locks back. + death->observed_proc = NULL; + binder_proc_death_notification_dec_ref(that, death, TRUE); + } + } + DBDEATH((KERN_WARNING "DeathNot %p: finishing remove from proc %p m_incoming_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + hlist_del(&death->observer); + binder_proc_death_notification_dec_ref(that, death, TRUE); + } + hlist_for_each_entry_safe(death, _p, _p2, &that->m_pending_death_notifications, observed_or_active) { + DBDEATH((KERN_WARNING "DeathNot %p: removing from proc %p m_pending_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + hlist_del(&death->observed_or_active); + binder_proc_death_notification_dec_ref(that, death, TRUE); + } + hlist_for_each_entry_safe(death, _p, _p2, &that->m_active_death_notifications, observed_or_active) { + DBDEATH((KERN_WARNING "DeathNot %p: removing from proc %p m_active_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + hlist_del(&death->observed_or_active); + binder_proc_death_notification_dec_ref(that, death, TRUE); + } + hlist_for_each_entry_safe(death, _p, _p2, &that->m_deleted_death_notifications, observed_or_active) { + DBDEATH((KERN_WARNING "DeathNot %p: removing from proc %p m_deleted_death_notifications and freeing, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + hlist_del(&death->observed_or_active); + kfree(death); + } + + // Now collect everything we have to clean up. We don't want to + // do stuff on these until after our own lock is released, to avoid + // various horrible deadlock situations. + + del_timer_sync(&that->m_wakeupTimer); + del_timer_sync(&that->m_idleTimer); + + freeCmdHead = that->m_needFree; + that->m_needFree = NULL; + + cmdHead = that->m_head; + that->m_head = NULL; + that->m_tail = &that->m_head; + cmd = cmdHead; + while (cmd) { + // If a pending transaction is the event transaction, remove + // our global pointer so that nobody else tries to use it. + if (cmd == that->m_eventTransaction) that->m_eventTransaction = NULL; + cmd = cmd->next; + } + + descriptors = that->m_descriptors; + descriptorCount = that->m_descriptorCount; + that->m_descriptors = NULL; + that->m_descriptorCount = 0; + + threads = that->m_threads; + that->m_threads = NULL; + for (thr = threads; thr != NULL; thr = thr->next) BND_ACQUIRE(binder_thread, thr, WEAK, that); + + that->m_state |= btDead; + + BND_UNLOCK(that->m_lock); + + // Now do all the cleanup! + + first = TRUE; + while ((thr = threads)) { + if (first) { + first = FALSE; + DBSHUTDOWN((KERN_WARNING "Binder team %p: removing remaining threads.\n", that)); + } + threads = thr->next; + DBSHUTDOWN((KERN_WARNING "Killing thread %p (%d)\n", thr, binder_thread_Thid(thr))); + binder_thread_Die(thr); + BND_RELEASE(binder_thread, thr, WEAK, that); + } + + first = TRUE; + while ((cmd=freeCmdHead)) { + if (first) { + first = FALSE; + DBSHUTDOWN((KERN_WARNING "Binder team %p: detaching free transactions.\n", that)); + } + DBSHUTDOWN((KERN_WARNING "Detaching transaction %p from thread %p (%d) to thread %p (%d) node %p\n", + cmd, cmd->sender, cmd->sender ? binder_thread_Thid(cmd->sender) : -1, + cmd->receiver, cmd->receiver ? binder_thread_Thid(cmd->receiver) : -1, + cmd->target)); + + // XXX The old implementation of this would call ReleaseTeam() + // here to keep the transaction around so that user space could + // hold on to it after replying. For some reason this would + // cause leaks (if the process never got destroyed), and this + // system doesn't use this feature, so now we just destroy it. + freeCmdHead = cmd->next; + binder_transaction_Destroy(cmd); + /* + binder_transaction_ReleaseTeam(cmd); + cmd = cmd->next; + */ + } + + first = TRUE; + while ((cmd = cmdHead)) { + if (first) { + first = FALSE; + DBSHUTDOWN((KERN_WARNING "Binder team %p: cleaning up pending commands.\n", that)); + } + if (cmd->sender) { + DBSHUTDOWN((KERN_WARNING "Returning transaction %p to thread %p (%d)\n", cmd, cmd->sender, binder_thread_Thid(cmd->sender))); + binder_thread_ReplyDead(cmd->sender); + } + cmdHead = cmd->next; + binder_transaction_Destroy(cmd); + } + + first = TRUE; + if (descriptors) { + int i; + for (i=0;im_eventTransaction) binder_transaction_Destroy(that->m_eventTransaction); + that->m_eventTransaction = NULL; + + DBSHUTDOWN((KERN_WARNING "Binder process %p: DEAD!\n", that)); + + BND_ASSERT(that->m_head == NULL, "that->m_head != NULL"); + + that->m_state |= btCleaned; + BND_RELEASE(binder_proc, that, WEAK, that); + if (acquired) BND_RELEASE(binder_proc, that, STRONG, that); + + DBSHUTDOWN((KERN_WARNING "**** %s(%p, %s) done dying!\n", __func__, that, locked ? "locked" : "unlocked")); + DBSHUTDOWN((KERN_WARNING "*****************************************\n")); +} + +status_t +binder_proc_RequestDeathNotification(binder_proc_t *that, binder_proc_t *client, void *cookie) +{ + bool already_dead = FALSE; + death_notification_t *death = kmalloc(sizeof(death_notification_t), GFP_KERNEL); + if(death == NULL) + return -ENOMEM; + DBDEATH((KERN_WARNING "DeathNot %p: RequestDeathNotification created proc %p watching proc %p\n", + death, client, that)); + atomic_set(&death->ref_count, 1); + death->observer_proc = client; + death->observed_proc = NULL; + death->cookie = cookie; + BND_LOCK(that->m_lock); + if(binder_proc_IsAlive(that)) { + atomic_inc(&death->ref_count); + death->observed_proc = that; + hlist_add_head(&death->observed_or_active, &that->m_outgoing_death_notifications); + DBDEATH((KERN_WARNING "DeathNot %p: added to proc %p m_outgoing_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + } + else { + DBDEATH((KERN_WARNING "DeathNot %p: already dead!\n", death)); + already_dead = TRUE; + } + BND_UNLOCK(that->m_lock); + BND_LOCK(client->m_lock); + if(binder_proc_IsAlive(client)) { + atomic_inc(&death->ref_count); + hlist_add_head(&death->observer, &client->m_incoming_death_notifications); + DBDEATH((KERN_WARNING "DeathNot %p: added to proc %p m_incoming_death_notifications, refcnt=%d\n", + death, client, atomic_read(&death->ref_count))); + } + BND_UNLOCK(client->m_lock); + + if(already_dead) + binder_proc_send_death_notification(client, death); + binder_proc_death_notification_dec_ref(client, death, FALSE); + return 0; +} + +status_t +binder_proc_ClearDeathNotification(binder_proc_t *that, binder_proc_t *client, void *cookie) +{ + struct hlist_node *_p; + death_notification_t *death = NULL; + + BND_LOCK(client->m_lock); + hlist_for_each_entry(death, _p, &client->m_incoming_death_notifications, observer) { + if(death->cookie == cookie) { + hlist_del(&death->observer); + break; + } + } + BND_UNLOCK(client->m_lock); + + DBDEATH((KERN_WARNING "DeathNot %p: ClearDeathNotification for cookie %p\n", death, cookie)); + if(death == NULL) + return -ENOENT; + BND_LOCK(that->m_lock); + if(death->observed_proc == that) { + hlist_del(&death->observed_or_active); + binder_proc_death_notification_dec_ref(client, death, FALSE); // this is holding the wrong lock, but we have a second reference + DBDEATH((KERN_WARNING "DeathNot %p: removed from proc %p m_incoming_death_notifications, refcnt=%d\n", + death, client, atomic_read(&death->ref_count))); + death->observed_proc = NULL; + } + else { + DBDEATH((KERN_WARNING "DeathNot %p ClearDeathNotification: already pending or sent!\n", death)); + } + BND_UNLOCK(that->m_lock); + binder_proc_death_notification_dec_ref(client, death, FALSE); // from hlist_del(&death->observer); + return 0; +} + +status_t +binder_proc_DeadBinderDone(binder_proc_t *that, void *cookie) +{ + struct hlist_node *_p; + death_notification_t *death = NULL; + BND_LOCK(that->m_lock); + hlist_for_each_entry(death, _p, &that->m_active_death_notifications, observed_or_active) { + if(death->cookie == cookie) { + DBDEATH((KERN_WARNING "DeathNot %p DeadBinderDone: removing from proc %p m_active_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + hlist_del(&death->observed_or_active); + death->observed_proc = NULL; + binder_proc_death_notification_dec_ref(that, death, TRUE); + break; + } + } + BND_UNLOCK(that->m_lock); + DBDEATH((KERN_WARNING "DeathNot %p: DeadBinderDone completed on cookie %p\n", death, cookie)); + if(death == NULL) + return -ENOENT; + return 0; +} + +static void +binder_proc_activate_death_processing_thread(binder_proc_t *that) +{ + binder_thread_t *thread; + + assert_spin_locked(&that->m_spin_lock); + if(!list_empty(&that->m_waitStack)) { + // TODO: pop thread from wait stack here + thread = list_entry(that->m_waitStack.next, binder_thread_t, waitStackEntry); + DBDEATH((KERN_WARNING "Activating death processing thread pid %d (proc %p)\n", + thread->m_thid, that)); + binder_proc_RemoveThreadFromWaitStack(that, thread); + thread->wakeReason = WAKE_REASON_PROCESS_DEATH; + BND_ASSERT(thread->nextRequest == NULL, "Thread has a request!"); + binder_thread_Wakeup(thread); + } + else { + BND_ASSERT((that->m_wakeThreadMask & WAKE_THREAD_FOR_PROCESS_DEATH) == 0, "WAKE_THREAD_FOR_PROCESS_DEATH already set"); + that->m_wakeThreadMask |= WAKE_THREAD_FOR_PROCESS_DEATH; + DBSPAWN((KERN_WARNING "%s(%p) empty waitstack\n", __func__, that)); + } +} + +void +binder_proc_send_death_notification(binder_proc_t *that, death_notification_t *death) +{ + unsigned long flags; + bool first; + + DIPRINTF(0, (KERN_WARNING "%s(%p)\n", __func__, that)); + + BND_LOCK(that->m_lock); + + DBDEATH((KERN_WARNING "DeathNot %p: Sending death notification to %p (alive=%d)\n", + death, that, binder_proc_IsAlive(that))); + + if(binder_proc_IsAlive(that)) { + spin_lock_irqsave(&that->m_spin_lock, flags); + first = hlist_empty(&that->m_pending_death_notifications) && hlist_empty(&that->m_deleted_death_notifications); + + atomic_inc(&death->ref_count); + hlist_add_head(&death->observed_or_active, &that->m_pending_death_notifications); + DBDEATH((KERN_WARNING "DeathNot %p: adding to proc %p m_pending_death_notifications, refcnt=%d, first=%d\n", + death, that, atomic_read(&death->ref_count), first)); + death->observed_proc = that; + + if(first) { + binder_proc_activate_death_processing_thread(that); + } + spin_unlock_irqrestore(&that->m_spin_lock, flags); + } + + BND_UNLOCK(that->m_lock); +} + +void +binder_proc_death_notification_dec_ref(binder_proc_t *that, death_notification_t *death, bool locked) +{ + DBDEATH((KERN_WARNING "DeathNot %p: decrementing refcnt, cur=%d\n", + death, atomic_read(&death->ref_count))); + if(atomic_dec_return(&death->ref_count) == 0) { + BND_ASSERT(death->observed_proc == NULL, "freeing death_notification_t with observed_proc still set"); + if(!locked) + BND_LOCK(that->m_lock); + if(binder_proc_IsAlive(that)) { + unsigned long flags; + bool first; + spin_lock_irqsave(&that->m_spin_lock, flags); + first = hlist_empty(&that->m_pending_death_notifications) && hlist_empty(&that->m_deleted_death_notifications); +#if BINDER_DEBUG + struct hlist_node *_p, *_p2; + death_notification_t *node; + hlist_for_each_entry_safe(node, _p, _p2, &that->m_outgoing_death_notifications, observed_or_active) { + BND_ASSERT(node != death, "Death ref count reached 0 while still on m_outgoing_death_notifications list"); + } + hlist_for_each_entry_safe(node, _p, _p2, &that->m_incoming_death_notifications, observer) { + BND_ASSERT(node != death, "Death ref count reached 0 while still on m_incoming_death_notifications list"); + } + hlist_for_each_entry_safe(node, _p, _p2, &that->m_pending_death_notifications, observed_or_active) { + BND_ASSERT(node != death, "Death ref count reached 0 while still on m_pending_death_notifications list"); + } + hlist_for_each_entry_safe(node, _p, _p2, &that->m_active_death_notifications, observed_or_active) { + BND_ASSERT(node != death, "Death ref count reached 0 while still on m_active_death_notifications list"); + } + hlist_for_each_entry_safe(node, _p, _p2, &that->m_deleted_death_notifications, observed_or_active) { + BND_ASSERT(node != death, "Death ref count reached 0 while still on m_deleted_death_notifications list"); + } + DBDEATH((KERN_WARNING "DeathNot %p: observer.next=%p, active.next=%p\n", + death, death->observer.next, death->observed_or_active.next)); +#endif + BND_ASSERT(death->observer.next == LIST_POISON1, "death ref count reached 0 while still on observer list"); + BND_ASSERT(death->observed_or_active.next == LIST_POISON1, "death ref count reached 0 while still on observed_or_active list"); + DBDEATH((KERN_WARNING "DeathNot %p: adding to deleted list, first=%d\n", death, first)); + hlist_add_head(&death->observed_or_active, &that->m_deleted_death_notifications); + if(first) + binder_proc_activate_death_processing_thread(that); + spin_unlock_irqrestore(&that->m_spin_lock, flags); + } + else { + kfree(death); + } + if(!locked) + BND_UNLOCK(that->m_lock); + } +} + +void +binder_proc_GetPendingDeathNotifications(binder_proc_t *that, binder_thread_t *thread, iobuffer_t *io) +{ + struct hlist_node *_p, *_p2; + death_notification_t *death; + BND_LOCK(that->m_lock); + + hlist_for_each_entry_safe(death, _p, _p2, &that->m_deleted_death_notifications, observed_or_active) { + if(iobuffer_remaining(io) < 8) + goto buffer_full; + DBDEATH((KERN_WARNING "DeathNot %p: GetPending removing from proc %p m_deleted_death_notifications and freeing\n", + death, that)); + hlist_del(&death->observed_or_active); + iobuffer_write_u32(io, brCLEAR_DEATH_NOTIFICATION_DONE); + iobuffer_write_u32(io, (int32_t)death->cookie); + kfree(death); + } + + hlist_for_each_entry_safe(death, _p, _p2, &that->m_pending_death_notifications, observed_or_active) { + if(iobuffer_remaining(io) < 8) + goto buffer_full; + hlist_del(&death->observed_or_active); + iobuffer_write_u32(io, brDEAD_BINDER); + iobuffer_write_u32(io, (int32_t)death->cookie); + hlist_add_head(&death->observed_or_active, &that->m_active_death_notifications); + DBDEATH((KERN_WARNING "DeathNot %p: moved from proc %p m_pending_death_notifications to m_active_death_notifications, refcnt=%d\n", + death, that, atomic_read(&death->ref_count))); + } + thread->wakeReason = WAKE_REASON_NONE; +buffer_full: + BND_UNLOCK(that->m_lock); +} + +status_t +binder_proc_AddToNeedFreeList(binder_proc_t *that, binder_transaction_t *t) +{ + BND_ACQUIRE(binder_proc, that, WEAK, that); + + binder_transaction_ReleaseTarget(t); + + DBLOCK((KERN_WARNING "AddToNeedFreeList() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + DPRINTF(2, (KERN_WARNING "AddToNeedFreeList %p for team %p\n",t,that)); + if (!binder_proc_IsAlive(that)) { + // Don't call this with lock held -- it could cause all other + // sorts of things to happen. + BND_UNLOCK(that->m_lock); + binder_transaction_ReleaseTeam(t); + BND_LOCK(that->m_lock); + } + t->next = that->m_needFree; + that->m_needFree = t; + that->m_freeCount++; + BND_UNLOCK(that->m_lock); + + BND_RELEASE(binder_proc, that, WEAK, that); + + return 0; +} + +BND_IMPLEMENT_ACQUIRE_RELEASE(binder_proc); +BND_IMPLEMENT_ATTEMPT_ACQUIRE(binder_proc); + +s32 +binder_proc_Node2Descriptor(binder_proc_t *that, binder_node_t *n, bool ref, s32 type) +{ + s32 desc=-2; + reverse_mapping_t **head; + + DPRINTF(4, (KERN_WARNING "%s(%p, %p, %s, %s)\n", __func__, that, n, ref ? "true" : "false", type == STRONG ? "STRONG" : "WEAK")); + BND_LOCK(that->m_lock); + + if (binder_proc_IsAlive(that)) { + u32 bucket = hash_ptr(n, HASH_BITS); + DPRINTF(5, (KERN_WARNING " -- node(%p) mapping to descr bucket %d\n",n,bucket)); + head = &that->m_reverseHash[bucket]; + while (*head && (n < (*head)->node)) head = &(*head)->next; + if (*head && (n == (*head)->node)) { + desc = (*head)->descriptor; + DPRINTF(5, (KERN_WARNING "node(%p) found map to descriptor(%d), strong=%d\n",n,desc+1,that->m_descriptors[desc].priRef)); + if (!ref || type == WEAK || that->m_descriptors[desc].priRef > 0 + || BND_ATTEMPT_ACQUIRE(binder_node, n, STRONG, that)) { + if (ref) { + DPRINTF(5, (KERN_WARNING "Incrementing descriptor %d %s: strong=%d weak=%d in team %p\n", desc+1, type == STRONG ? "STRONG" : "WEAK", that->m_descriptors[desc].priRef, that->m_descriptors[desc].secRef, that)); + if (type == STRONG) that->m_descriptors[desc].priRef++; + else that->m_descriptors[desc].secRef++; + } + DPRINTF(5, (KERN_WARNING "node(%p) mapped to descriptor(%d) in team %p\n",n,desc+1,that)); + } else { + // No longer exists! + desc = -2; + } + } else if (ref && (type != STRONG || BND_ATTEMPT_ACQUIRE(binder_node, n, STRONG, that))) { + reverse_mapping_t *map; + int i; + if (type != STRONG) BND_ACQUIRE(binder_node, n, WEAK, that); + for (i=0;im_descriptorCount;i++) { + if (that->m_descriptors[i].node == NULL) { + that->m_descriptors[i].node = n; + if (type == STRONG) { + that->m_descriptors[i].priRef = 1; + that->m_descriptors[i].secRef = 0; + } else { + that->m_descriptors[i].priRef = 0; + that->m_descriptors[i].secRef = 1; + } + desc = i; + // DPRINTF(5, (KERN_WARNING "Initializing descriptor %d: strong=%d weak=%d in team %p\n", i+1, that->m_descriptors[i].priRef,that->m_descriptors[i].secRef,that)); + DPRINTF(5, (KERN_WARNING "node(%p) mapped to NEW descriptor(%d) in team %p\n",n,desc+1,that)); + break; + } + } + + if (desc < 0) { + int i; + s32 newCount = that->m_descriptorCount*2; + if (!newCount) newCount = 32; + // that->m_descriptors = (descriptor_t*)kernel_realloc(that->m_descriptors,sizeof(descriptor_t)*newCount,"descriptors"); + { + descriptor_t *d = kmalloc(sizeof(descriptor_t)*newCount, GFP_KERNEL); + // FIXME: BeOS code did not deal with allocation failures + memcpy(d, that->m_descriptors, that->m_descriptorCount*sizeof(descriptor_t)); + kfree(that->m_descriptors); + that->m_descriptors = d; + } + for (i=newCount-1;i>=that->m_descriptorCount;i--) that->m_descriptors[i].node = NULL; + desc = that->m_descriptorCount; + DPRINTF(5, (KERN_WARNING "Initializing descriptor %d: strong=%d weak=%d in team %p\n", desc+1, that->m_descriptors[desc].priRef,that->m_descriptors[desc].secRef,that)); + that->m_descriptors[desc].node = n; + if (type == STRONG) { + that->m_descriptors[desc].priRef = 1; + that->m_descriptors[desc].secRef = 0; + } else { + that->m_descriptors[desc].priRef = 0; + that->m_descriptors[desc].secRef = 1; + } + that->m_descriptorCount = newCount; + DPRINTF(5, (KERN_WARNING "node(%p) mapped to NEW descriptor(%d) in team %p\n",n,desc+1,that)); + } + + map = (reverse_mapping_t*)kmem_cache_alloc(reverse_mapping_cache, GFP_KERNEL); + map->node = n; + map->descriptor = desc; + map->next = *head; + *head = map; + } + } + + BND_UNLOCK(that->m_lock); + return desc+1; +} + +binder_node_t * +binder_proc_Descriptor2Node(binder_proc_t *that, s32 descriptor, const void* id, s32 type) +{ + binder_node_t *n; + (void)id; + + descriptor--; + + DBLOCK((KERN_WARNING "Descriptor2Node() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + n = NULL; + if (binder_proc_IsAlive(that)) { + if ((descriptor >= 0) && + (descriptor < that->m_descriptorCount) && + (that->m_descriptors[descriptor].node != NULL)) { + if (type == STRONG) { + if (that->m_descriptors[descriptor].priRef > 0) { + n = that->m_descriptors[descriptor].node; + BND_ACQUIRE(binder_node, n, STRONG, id); + } else { + UPRINTF((KERN_WARNING "Descriptor2Node failed primary: desc=%d, max=%d, node=%p, strong=%d\n", + descriptor+1, that->m_descriptorCount, + that->m_descriptors[descriptor].node, + that->m_descriptors[descriptor].priRef)); + } + } else { + if (that->m_descriptors[descriptor].secRef > 0) { + n = that->m_descriptors[descriptor].node; + BND_ACQUIRE(binder_node, n, WEAK, id); + } else { + UPRINTF((KERN_WARNING "Descriptor2Node failed secondary: desc=%d, max=%d, node=%p, weak=%d\n", + descriptor+1, that->m_descriptorCount, + that->m_descriptors[descriptor].node , + that->m_descriptors[descriptor].secRef)); + } + } + } else { + UPRINTF((KERN_WARNING "Descriptor2Node failed: desc=%d, max=%d, node=%p, strong=%d\n", + descriptor+1, that->m_descriptorCount, + (descriptor >= 0 && descriptor < that->m_descriptorCount) ? that->m_descriptors[descriptor].node : NULL, + (descriptor >= 0 && descriptor < that->m_descriptorCount) ? that->m_descriptors[descriptor].priRef : 0)); + } + } + + BND_UNLOCK(that->m_lock); + return n; +} + +status_t +binder_proc_Ptr2Node(binder_proc_t *that, void *ptr, void *cookie, binder_node_t **n, iobuffer_t *io, const void* id, s32 type) +{ + u32 bucket; + local_mapping_t **head; + local_mapping_t *newMapping; + (void)id; + + if (ptr == NULL) { + DPRINTF(5, (KERN_WARNING "ptr(%p) mapping to NULL node in team %p\n",ptr,that)); + *n = NULL; + return 0; + } + + DBLOCK((KERN_WARNING "Ptr2Node() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + bucket = hash_ptr(ptr, HASH_BITS); + DPRINTF(9, (KERN_WARNING "ptr(%p) mapping to ptr bucket %u (value %p) in team %p\n",ptr,bucket,that->m_localHash[bucket],that)); + head = &that->m_localHash[bucket]; + while (*head && (ptr < (*head)->ptr)) head = &(*head)->next; + if (*head && (ptr == (*head)->ptr)) { + if ((type == STRONG) && BND_ATTEMPT_ACQUIRE(binder_node, (*head)->node, STRONG, id)) { + *n = (*head)->node; + DPRINTF(4, (KERN_WARNING "%s(%p, %p, %s): %p (OLD)\n", __func__, that, ptr, type == STRONG ? "STRONG" : "WEAK", *n)); + BND_UNLOCK(that->m_lock); + return 0; + } else if (BND_ATTEMPT_ACQUIRE(binder_node, (*head)->node, WEAK, id)) { + if((*head)->next) + BND_ASSERT(io || (*head)->next->ptr != ptr || atomic_read(&((*head)->next->node->m_secondaryRefs)) == 0, "May remove wrong node"); + + *n = (*head)->node; + DPRINTF(4, (KERN_WARNING "%s(%p, %p, %s): %p (OLD)\n", __func__, that, ptr, type == STRONG ? "STRONG" : "WEAK", *n)); + if (type == STRONG) { + /* Other teams have a secondary reference on this node, but no + primary reference. We need to make the node alive again, and + tell the calling team that the driver now has a primary + reference on it. The two calls below will force a new primary + reference on the node, and remove the secondary reference we + just acquired above. All the trickery with the secondary reference + is protection against a race condition where another team removes + the last secondary reference on the object, while we are here + trying to add one. + */ + int count; + DPRINTF(9, (KERN_WARNING "Apply a new primary reference to node (%p) in team %p\n",*n,that)); + count = BND_FORCE_ACQUIRE(binder_node, *n, id); + BND_RELEASE(binder_node, *n, WEAK, id); + + BND_ASSERT(io != NULL, "Acquiring new strong reference without io"); + if (count == 0) { + that->m_numRemoteStrongRefs++; + if (io) { + BND_ACQUIRE(binder_node, *n, STRONG, that); // add a second reference to avoid the node being released before the aquire has finished + iobuffer_write_u32(io, brACQUIRE); + iobuffer_write_void(io, ptr); + iobuffer_write_void(io, (*head)->cookie); + DPRINTF(5, (KERN_WARNING " -- wrote brACQUIRE: %p\n", ptr)); + } + } + else { + printk(KERN_WARNING "%s(%p, %p, %s): %p Reaquired strong reference, but someone beat us to it\n", __func__, that, ptr, type == STRONG ? "STRONG" : "WEAK", (*head)->node); + } + } + BND_UNLOCK(that->m_lock); + return 0; + } +#if 1 + else { + DPRINTF(4, (KERN_WARNING "%s(%p, %p, %s): %p (OLD) FAILED AttempAcquire!\n", __func__, that, ptr, type == STRONG ? "STRONG" : "WEAK", (*head)->node)); + } +#endif + } + + { + local_mapping_t **thead; + thead = &that->m_localHash[hash_ptr(ptr, HASH_BITS)]; + while (*thead) { + if((*thead)->ptr == ptr) { + BND_ASSERT(atomic_read(&((*head)->node->m_primaryRefs)) == 0, "Creating new node when a node with strong refs already exists"); + BND_ASSERT(atomic_read(&((*head)->node->m_secondaryRefs)) == 0, "Creating new node when a node with weak refs already exists"); + } + thead = &(*thead)->next; + } + } + + if (io && (iobuffer_remaining(io) < 8)) { + BND_UNLOCK(that->m_lock); + return -EINVAL; + } + + if (!binder_proc_IsAlive(that)) { + BND_UNLOCK(that->m_lock); + return -ENOENT; + } + + newMapping = (local_mapping_t*)kmem_cache_alloc(local_mapping_cache, GFP_KERNEL); + newMapping->ptr = ptr; + newMapping->cookie = cookie; + newMapping->node = binder_node_init(that,ptr,cookie); + *n = newMapping->node; + DPRINTF(4, (KERN_WARNING "%s(%p, %p, %s): %p (NEW)\n", __func__, that, ptr, type == STRONG ? "STRONG" : "WEAK", *n)); + BND_FIRST_ACQUIRE(binder_node, *n, type, id); + newMapping->next = *head; + *head = newMapping; + + if (io) { + if (type == STRONG) { + BND_ACQUIRE(binder_node, *n, STRONG, that); // add a second reference to avoid the node being released before the aquire has finished + that->m_numRemoteStrongRefs++; + iobuffer_write_u32(io, brACQUIRE); + iobuffer_write_void(io, ptr); + iobuffer_write_void(io, cookie); + DPRINTF(5, (KERN_WARNING " -- wrote brACQUIRE: %p\n", ptr)); + } + BND_ACQUIRE(binder_node, *n, WEAK, that); // add a second reference to avoid the node being released before the aquire has finished + iobuffer_write_u32(io, brINCREFS); + iobuffer_write_void(io, ptr); + iobuffer_write_void(io, cookie); + DPRINTF(5, (KERN_WARNING " -- wrote brINCREFS: %p\n", ptr)); + } + else { + if (type == STRONG) + printk(KERN_WARNING "%s() creating new node without brACQUIRE\n", __func__); + else + printk(KERN_WARNING "%s() creating new node without brINCREFS\n", __func__); + } + + BND_UNLOCK(that->m_lock); + return 0; +} + +bool +binder_proc_RefDescriptor(binder_proc_t *that, s32 descriptor, s32 type) +{ + bool r=FALSE; + + descriptor--; + + DBLOCK((KERN_WARNING "RefDescriptor() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + if (binder_proc_IsAlive(that)) { + descriptor_t *d; + if ((descriptor >= 0) && + (descriptor < that->m_descriptorCount) && + ((d=&that->m_descriptors[descriptor])->node != NULL)) { + r = TRUE; + DPRINTF(5, (KERN_WARNING "Incrementing descriptor %d %s: strong=%d weak=%d in team %p\n", descriptor+1, type == STRONG ? "STRONG" : "WEAK", d->priRef,d->secRef,that)); + if (type == STRONG) { + if (d->priRef > 0) d->priRef++; + else { + UPRINTF((KERN_WARNING "No strong references exist for descriptor: desc=%d, max=%d, node=%p, weak=%d\n", + descriptor+1, that->m_descriptorCount, + (descriptor >= 0 && descriptor < that->m_descriptorCount) ? that->m_descriptors[descriptor].node : NULL, + (descriptor >= 0 && descriptor < that->m_descriptorCount) ? that->m_descriptors[descriptor].secRef : 0)); + r = FALSE; + } + } else if (type == WEAK) { + if (d->secRef > 0) d->secRef++; + else if (d->priRef > 0) { + // Note that we allow the acquisition of a weak reference if only holding + // a strong because for transactions we only increment the strong ref + // count when sending a strong reference... so we need to be able to recover + // weak reference here. + d->secRef++; BND_ACQUIRE(binder_node, d->node, WEAK, that); + } else { + UPRINTF((KERN_WARNING "No weak references exist for descriptor: desc=%d, max=%d, node=%p, strong=%d\n", + descriptor+1, that->m_descriptorCount, + (descriptor >= 0 && descriptor < that->m_descriptorCount) ? that->m_descriptors[descriptor].node : NULL, + (descriptor >= 0 && descriptor < that->m_descriptorCount) ? that->m_descriptors[descriptor].priRef : 0)); + r = FALSE; + } + } + } + } + + BND_UNLOCK(that->m_lock); + return r; +} + +bool +binder_proc_UnrefDescriptor(binder_proc_t *that, s32 descriptor, s32 type) +{ + binder_node_t *n = NULL; + bool r=FALSE; + + descriptor--; + + DPRINTF(4, (KERN_WARNING "%s(%p, %d, %s)\n", __func__, that, descriptor, type == STRONG ? "STRONG" : "WEAK")); + + BND_LOCK(that->m_lock); + + if (binder_proc_IsAlive(that)) { + descriptor_t *d; + bool remove = FALSE; + if ((descriptor >= 0) && + (descriptor < that->m_descriptorCount) && + ((d=&that->m_descriptors[descriptor])->node != NULL)) { + r = TRUE; + DPRINTF(5, (KERN_WARNING "Decrementing descriptor %d %s: strong=%d weak=%d in team %p\n", descriptor+1, type == STRONG ? "STRONG" : "WEAK", d->priRef,d->secRef,that)); + if (type == STRONG) { + if (--d->priRef == 0) n = d->node; + } else { + if (--d->secRef == 0) n = d->node; + } + DPRINTF(5, (KERN_WARNING "Descriptor %d is now: strong=%d weak=%d in team %p\n", descriptor+1, d->priRef,d->secRef,that)); + if (n && d->priRef <= 0 && d->secRef <= 0) { + d->node = NULL; + remove = TRUE; + } + } + + if (remove) { + reverse_mapping_t *entry,**head = &that->m_reverseHash[hash_ptr(n, HASH_BITS)]; + while (*head && (n < (*head)->node)) head = &(*head)->next; + if (*head && (n == (*head)->node)) { + entry = *head; + *head = entry->next; + kmem_cache_free(reverse_mapping_cache, entry); + } + } + } + + BND_UNLOCK(that->m_lock); + if (n) BND_RELEASE(binder_node, n, type, that); + return r; +} + +bool +binder_proc_RemoveLocalMapping(binder_proc_t *that, void *ptr, struct binder_node *node) +{ + local_mapping_t *entry=NULL; + + DBLOCK((KERN_WARNING "RemoveLocalMapping() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + if (binder_proc_IsAlive(that)) { + local_mapping_t **head; + DPRINTF(5, (KERN_WARNING "RemoveLocalMapping %p in team %p\n", ptr, that)); + head = &that->m_localHash[hash_ptr(ptr, HASH_BITS)]; + while (*head) { +// (KERN_WARNING "RemoveLocalMapping %08x %08x\n",ptr,(*head)->ptr); + if (ptr >= (*head)->ptr && ((*head)->node == node || ptr > (*head)->ptr)) + break; + head = &(*head)->next; + } + +// while (*head && (ptr <= (*head)->ptr)) head = &(*head)->next; + if (*head && (ptr == (*head)->ptr)) { + entry = *head; + *head = entry->next; + } + BND_ASSERT(entry != NULL, "RemoveLocalMapping failed for live process"); + if(entry == NULL) { + head = &that->m_localHash[hash_ptr(ptr, HASH_BITS)]; + while (*head) { + if((*head)->node == node) + break; + head = &(*head)->next; + } + if(*head != NULL) + printk(KERN_WARNING "RemoveLocalMapping failed, but exists in the wrong place, ptr = %p node = %p node->ptr = %p\n", ptr, node, (*head)->ptr); + } + } + + BND_UNLOCK(that->m_lock); + + if (entry) { + kmem_cache_free(local_mapping_cache, entry); +// (KERN_WARNING "RemoveLocalMapping success\n"); + return TRUE; + } + + DPRINTF(0, (KERN_WARNING "RemoveLocalMapping failed for %p in team %p\n", ptr, that)); + return FALSE; +} + +void +binder_proc_RemoveLocalStrongRef(binder_proc_t *that, binder_node_t *node) +{ + bool goodbye; + + DBLOCK((KERN_WARNING "RemoveLocalStrongRef() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + // It is time for this process to go away if: + // (a) This is the last strong reference on it, and + // (b) The process published a root object. (If it didn't publish + // a root object, then someone else is responsible for managing its lifetime.) + goodbye = --that->m_numRemoteStrongRefs == 0 ? (that->m_rootObject != NULL) : FALSE; + + // Oh, and also, if the object being released -is- the root object, well that... + if (that->m_rootObject == node) { + that->m_rootObject = (binder_node_t*)-1; // something we know isn't a valid address. + if (that->m_rootStopsProcess) goodbye = TRUE; + } + + BND_UNLOCK(that->m_lock); + + if (goodbye) binder_proc_Die(that, FALSE); +} + +void +binder_proc_AddLocalStrongRef(binder_proc_t *that, binder_node_t *node) +{ + DBLOCK((KERN_WARNING "AddLocalStrongRef() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + that->m_numRemoteStrongRefs++; + BND_UNLOCK(that->m_lock); +} + +bool +binder_proc_AttemptRefDescriptor(binder_proc_t *that, s32 descriptor, binder_node_t **out_target) +{ + binder_node_t *n = NULL; + bool r=FALSE; + + descriptor--; + + DBLOCK((KERN_WARNING "AttemptRefDescriptor() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + if (binder_proc_IsAlive(that)) { + descriptor_t *d; + if ((descriptor >= 0) && + (descriptor < that->m_descriptorCount) && + ((d=&that->m_descriptors[descriptor])->node != NULL)) { + r = TRUE; + DPRINTF(5, (KERN_WARNING "Attempt incrementing descriptor %d primary: strong=%d weak=%d in team %p\n", descriptor+1, d->priRef,d->secRef,that)); + if (d->priRef > 0 || (d->node && BND_ATTEMPT_ACQUIRE(binder_node, d->node, STRONG, that))) { + d->priRef++; + } else { + // If no strong references currently exist, we can't + // succeed. Instead return the node this attempt was + // made on. + r = FALSE; + if ((n=d->node) != NULL) BND_ACQUIRE(binder_node, n, WEAK, that); + } + } + } + + BND_UNLOCK(that->m_lock); + + *out_target = n; + return r; +} + +void +binder_proc_ForceRefNode(binder_proc_t *that, binder_node_t *node, iobuffer_t *io) +{ + bool recovered = FALSE; + const s32 descriptor = binder_proc_Node2Descriptor(that, node, FALSE, STRONG) - 1; + + DBLOCK((KERN_WARNING "ForceRefNode() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + if (binder_proc_IsAlive(that)) { + descriptor_t *d; + if ((descriptor >= 0) && + (descriptor < that->m_descriptorCount) && + ((d=&that->m_descriptors[descriptor])->node != NULL)) { + DPRINTF(5, (KERN_WARNING "Force incrementing descriptor %d: strong=%d weak=%d in team %p\n", descriptor+1, d->priRef, d->secRef,that)); + if (d->priRef == 0) { + if (BND_FORCE_ACQUIRE(binder_node, node, that) == 0) { + recovered = TRUE; + } + } + d->priRef++; + } else { + BND_ASSERT(FALSE, "ForceRefNode() got invalid descriptor!"); + } + } + + BND_UNLOCK(that->m_lock); + + // If this operation recovered a strong reference on the object, we + // need to tell its owning process for proper bookkeeping; + if (recovered) { + binder_proc_t* proc = binder_node_AcquireHome(node, that); + if (proc != NULL) { + binder_proc_AddLocalStrongRef(proc, node); + BND_RELEASE(binder_proc, proc, STRONG, that); + } + } else { + iobuffer_write_u32(io, brRELEASE); + iobuffer_write_void(io, binder_node_Ptr(node)); // binder object token + iobuffer_write_void(io, binder_node_Cookie(node)); // binder object cookie + } +} + +status_t +binder_proc_FreeBuffer(binder_proc_t *that, void *ptr) +{ + binder_transaction_t **p,*t; + DBLOCK((KERN_WARNING "FreeBuffer() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + for (p = &that->m_needFree; *p && (binder_transaction_UserData(*p) != ptr); p = &(*p)->next); + if ((t = *p)) *p = t->next; + if (t) that->m_freeCount--; + BND_UNLOCK(that->m_lock); + + if (t) { + DPRINTF(5, (KERN_WARNING "FreeBuffer %p in team %p, now have %d\n",ptr,that,that->m_freeCount)); + + binder_transaction_Destroy(t); + return 0; + } else { + BND_ASSERT(!binder_proc_IsAlive(that), "FreeBuffer failed"); + } + return -EINVAL; +} + +static void +binder_proc_RemoveThreadFromWaitStack(binder_proc_t *that, binder_thread_t *thread) +{ + assert_spin_locked(&that->m_spin_lock); + BND_ASSERT(!list_empty(&thread->waitStackEntry), "thread not on waitstack"); + + list_del_init(&thread->waitStackEntry); + that->m_waitStackCount--; + DIPRINTF(0, (KERN_WARNING "%s(%p) popped thread %p from waitStack %d threads left\n", __func__, that, thread, that->m_waitStackCount)); + if(thread->wakeReason == WAKE_REASON_IDLE && that->m_waitStackCount > BND_PROC_MAX_IDLE_THREADS) + mod_timer(&that->m_idleTimer, that->m_idleTimeout + jiffies); + else if(that->m_waitStackCount == BND_PROC_MAX_IDLE_THREADS) + del_timer(&that->m_idleTimer); +} + +static void +binder_proc_DeliverTransacton(binder_proc_t *that, binder_transaction_t *t) +{ + binder_thread_t *thread; + + assert_spin_locked(&that->m_spin_lock); + + if(!list_empty(&that->m_waitStack)) { + // TODO: pop thread from wait stack here + thread = list_entry(that->m_waitStack.next, binder_thread_t, waitStackEntry); + binder_proc_RemoveThreadFromWaitStack(that, thread); + BND_ASSERT(thread->nextRequest == NULL, "Thread already has a request!"); + //DBTRANSACT((KERN_WARNING "Delivering transaction %p to thread %d from thread %d!\n", + // t, binder_thread_Thid(thread), current->pid)); + thread->nextRequest = t; + set_thread_priority(binder_thread_Thid(thread), binder_transaction_Priority(t)); + binder_thread_Wakeup(thread); + } + else { + DBSPAWN((KERN_WARNING "%s(%p) empty waitstack\n", __func__, that)); + *that->m_tail = t; + that->m_tail = &t->next; + } +} + +status_t +binder_proc_Transact(binder_proc_t *that, binder_transaction_t *t) +{ + binder_thread_t *thread; + unsigned long flags; + + DBLOCK((KERN_WARNING "Transact() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + + DBTRANSACT((KERN_WARNING "Thread %d transacting %p to team %p, vthid=%d\n", + current->pid, t, that, t->sender ? binder_thread_VirtualThid(t->sender) : -1)); + + if (!binder_proc_IsAlive(that)) { + BND_UNLOCK(that->m_lock); + if (t->sender) binder_thread_ReplyDead(t->sender); + binder_transaction_Destroy(t); + return 0; + } + + BND_ASSERT(t->next == NULL, "Transaction not correctly initialized"); + + /* First check if the target team is already waiting on a reply from + this thread. If so, we must reflect this transaction directly + into the thread that is waiting for us. + */ + if (t->sender && binder_thread_VirtualThid(t->sender)) { + for (thread = that->m_threads; + thread && + (binder_thread_VirtualThid(thread) != binder_thread_VirtualThid(t->sender)) && + (binder_thread_Thid(thread) != binder_thread_VirtualThid(t->sender)); + thread = thread->next); + + if (thread) { + /* Make sure this thread starts out at the correct priority. + Its user-space looper will restore the old priority when done. */ + set_thread_priority(binder_thread_Thid(thread), binder_transaction_Priority(t)); + BND_UNLOCK(that->m_lock); + DBTRANSACT((KERN_WARNING "Thread %d reflecting %p!\n", current->pid, t)); + binder_thread_Reflect(thread, t); + return 0; + } + } + + spin_lock_irqsave(&that->m_spin_lock, flags); + /* Enqueue or deliver this transaction */ + binder_proc_DeliverTransacton(that, t); + that->m_syncCount++; + + BND_ASSERT(that->m_syncCount > 0, "Synchronous transaction count is bad!"); + // that->m_syncCount++; + + // DBTRANSACT((KERN_WARNING "Added to team %p queue -- needNewThread=%d, that->m_nonblockedThreads=%d\n", that, needNewThread, that->m_nonblockedThreads)); + + spin_unlock_irqrestore(&that->m_spin_lock, flags); + + if (that->m_nonblockedThreads <= 0) { + DBSPAWN((KERN_WARNING "*** TRANSACT NEEDS TO SPAWN NEW THREAD!\n")); + binder_proc_spawn_looper(that); + } + + BND_UNLOCK(that->m_lock); + + return 0; +} + +status_t +binder_proc_TakeMeOffYourList(binder_proc_t *that) +{ + DBLOCK((KERN_WARNING "binder_proc_TakeMeOffYourList() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + that->m_nonblockedThreads--; + DBSPAWN((KERN_WARNING "*** TAKE-ME-OFF-YOUR-LIST %p -- now have %d nonblocked\n", that, that->m_nonblockedThreads)); + BND_ASSERT(that->m_nonblockedThreads >= 0, "Nonblocked thread count is bad!"); + if ((that->m_nonblockedThreads <= 0) && that->m_syncCount) { + /* Spawn a thread if all blocked and synchronous transaction pending */ + DBSPAWN((KERN_WARNING "*** TAKE-ME-OFF-YOUR-LIST NEEDS TO SPAWN NEW THREAD!\n")); + binder_proc_spawn_looper(that); + } + BND_UNLOCK(that->m_lock); + return 0; +} + +status_t +binder_proc_PutMeBackInTheGameCoach(binder_proc_t *that) +{ + DBLOCK((KERN_WARNING "binder_proc_PutMeBackInTheGameCoach() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + BND_ASSERT(that->m_nonblockedThreads >= 0, "Nonblocked thread count is bad!"); + that->m_nonblockedThreads++; + DBSPAWN((KERN_WARNING "*** PUT-ME-BACK-IN-THE-GAME-COACH %p -- now have %d nonblocked\n", that, that->m_nonblockedThreads)); + BND_UNLOCK(that->m_lock); + return 0; +} + +status_t +binder_proc_WaitForRequest(binder_proc_t *that, binder_thread_t* who, binder_transaction_t **t) +{ + status_t err = 0; + unsigned long flags; + + if(that->m_wakeThreadMask) { + spin_lock_irqsave(&that->m_spin_lock, flags); + if(that->m_wakeThreadMask & WAKE_THREAD_FOR_PROCESS_DEATH) { + that->m_wakeThreadMask &= ~WAKE_THREAD_FOR_PROCESS_DEATH; + who->wakeReason = WAKE_REASON_PROCESS_DEATH; + } + spin_unlock_irqrestore(&that->m_spin_lock, flags); + } + if(who->wakeReason == WAKE_REASON_PROCESS_DEATH) { + BND_LOCK(that->m_lock); + if(hlist_empty(&that->m_pending_death_notifications) && hlist_empty(&that->m_deleted_death_notifications)) { + printk(KERN_WARNING "%s() thread->wakeReason == WAKE_REASON_PROCESS_DEATH with no pending notifications\n", __func__); + who->wakeReason = WAKE_REASON_NONE; + } + BND_UNLOCK(that->m_lock); + return DEATH_NOTIFICATION_READY; + } + + DBLOCK((KERN_WARNING "WaitForRequest() going to lock %p in %d\n", that, binder_thread_Thid(who))); + BND_LOCK(that->m_lock); + + BND_ASSERT(atomic_read(&that->m_lock.count) <= 0, "WaitForRequest() lock still free after BND_LOCK"); + + if (who->m_isSpawned && who->m_firstLoop) { + /* This is a new thread that is waiting for its first time. */ +#if 0 + DPRINTF(0, (KERN_WARNING "*** ENTERING SPAWNED THREAD! Now looping %d, spawning %d\n", + atomic_read(&that->m_loopingThreads), that->m_spawningThreads)); + that->m_spawningThreads--; +#else + DPRINTF(0, (KERN_WARNING "*** ENTERING SPAWNED THREAD! Now looping %d\n", atomic_read(&that->m_loopingThreads))); +#endif + who->m_firstLoop = FALSE; + } else { + /* This is an existing thread that is going to go back to waiting. */ + that->m_waitingThreads++; + } + + BND_ASSERT(who->nextRequest == NULL, "Thread already has a request!"); + BND_ASSERT(list_empty(&who->waitStackEntry), "Thread on wait stack!"); + + /* Look for a pending request to service. Only do this if we are not + yet on the wait stack, or are at the top of the stack -- otherwise, + we need to wait for the thread on top of us to execute. */ + spin_lock_irqsave(&that->m_spin_lock, flags); + if((*t = that->m_head) != NULL) { + DIPRINTF(5, (KERN_WARNING "Processing transaction %p, next is %p\n", *t, (*t)->next)); + that->m_head = (*t)->next; + if (that->m_tail == &(*t)->next) that->m_tail = &that->m_head; + (*t)->next = NULL; + set_thread_priority(binder_thread_Thid(who), binder_transaction_Priority(*t)); + } + else { + /* If there are no pending transactions, unlock the team state and + wait for next thing to do. */ + + // Add to wait stack. + DIPRINTF(5, (KERN_WARNING "Pushing thread %d on to wait stack.\n", binder_thread_Thid(who))); + #if VALIDATES_BINDER + binder_thread_t* pos; + list_for_each_entry(pos, &that->m_waitStack, waitStackEntry) { + DBSTACK((KERN_WARNING "Thread %ld looking through wait stack: %p (%ld)\n", + current, pos, binder_thread_Thid(pos))); + BND_ASSERT(pos != who, "Pushing thread already on wait stack!"); + } + #endif + list_add(&who->waitStackEntry, &that->m_waitStack); + that->m_waitStackCount++; + DIPRINTF(0, (KERN_WARNING "%s(%p) added thread %p to waitStack %d threads now waiting\n", __func__, that, who, that->m_waitStackCount)); + if(that->m_waitStackCount == BND_PROC_MAX_IDLE_THREADS + 1) { + mod_timer(&that->m_idleTimer, that->m_idleTimeout + jiffies); + } + set_thread_priority(binder_thread_Thid(who), that->m_idlePriority); + spin_unlock_irqrestore(&that->m_spin_lock, flags); + + BND_UNLOCK(that->m_lock); + err = binder_thread_AcquireIOSem(who); + DBLOCK((KERN_WARNING "WaitForRequest() #2 going to lock %p in %d\n", that, binder_thread_Thid(who))); + BND_LOCK(that->m_lock); + + //DPRINTF(5, (KERN_WARNING "Thread %d: err=0x%08x, wakeupTime=%Ld\n", binder_thread_Thid(who), err, who->wakeupTime)); + + spin_lock_irqsave(&that->m_spin_lock, flags); + if(err != 0) { + // wakeup or idle timer may have released the thread + atomic_set(&who->m_wake_count, 0); + } + if ((*t=who->nextRequest) != NULL) { + /* A request has been delivered directly to us. In this + case the thread has already been removed from the wait + stack. */ + DIPRINTF(1, (KERN_WARNING "Thread %d received transaction %p, err=0x%08x\n", binder_thread_Thid(who), *t, err)); + who->nextRequest = NULL; + err = 0; + + } else { + /* The snooze ended without a transaction being returned. + If the thread ends up returning at this point, we will + need to pop it off the wait stack. Make note of that, + find out what happened, and deal with it. + */ + + DBTRANSACT((KERN_WARNING "Thread %d snooze returned with err=0x%08x\n", + binder_thread_Thid(who), err)); + + switch(who->wakeReason) { + case WAKE_REASON_IDLE: + who->wakeReason = WAKE_REASON_NONE; // the main thread may ignore a request to die + err = -ETIMEDOUT; + DBSPAWN((KERN_WARNING "*** TIME TO DIE! waiting=%d, nonblocked=%d\n", + that->m_waitingThreads, that->m_nonblockedThreads)); + break; + + case WAKE_REASON_PROCESS_DEATH: + // the threads stays in this state until the pending list becomes empty + err = DEATH_NOTIFICATION_READY; + break; + + default: + BND_ASSERT(err < 0 || !binder_proc_IsAlive(that), "thread woke up without a reason"); + /* If this thread is still on the wait stack, remove it. */ + DBTRANSACT((KERN_WARNING "Popping thread %d from wait stack.\n", + binder_thread_Thid(who))); + binder_proc_RemoveThreadFromWaitStack(that, who); + } + } + } + spin_unlock_irqrestore(&that->m_spin_lock, flags); + + //DBTRANSACT(if ((*t) != NULL) (KERN_WARNING "*** EXECUTING TRANSACTION %p FROM %ld IN %ld\n", *t, (*t)->sender ? binder_thread_Thid((*t)->sender) : -1, binder_thread_Thid(who))); + + if ((*t) != NULL) { + if (!binder_transaction_IsEvent(*t)) { + /* Removing a synchronous transaction from the queue */ + BND_ASSERT(that->m_syncCount >= 0, "Count of synchronous transactions is bad!"); + that->m_syncCount--; + } else { + BND_ASSERT(*t == that->m_eventTransaction, "Event thread is not the expected instance!"); + + /* Tell caller to process an event. */ + who->returnedEventPriority = binder_transaction_Priority(*t); + err = REQUEST_EVENT_READY; + *t = NULL; + + /* Clear out current event information. */ + that->m_state &= ~btEventInQueue; + } + } else { + if(err == -ERESTARTSYS) { + DBTRANSACT((KERN_WARNING "*** NON-TRANSACTION IN %d! Error=-ERESTARTSYS\n", binder_thread_Thid(who))); + } + else { + DBTRANSACT((KERN_WARNING "*** NON-TRANSACTION IN %d! Error=0x%08x\n", binder_thread_Thid(who), err)); + } + // By default (such as errors) run at normal priority. + set_thread_priority(binder_thread_Thid(who), B_NORMAL_PRIORITY); + } + + #if VALIDATES_BINDER + { + binder_thread_t* pos; + list_for_each_entry(pos, &that->m_waitStack, waitStackEntry) { + DBSTACK((KERN_WARNING "Thread %d looking through wait stack: %p (%d)\n", + current, pos, binder_thread_Thid(pos))); + BND_ASSERT(pos != who, "Thread still on wait stack!"); + } + } + #endif + + that->m_waitingThreads--; + + /* Spawn a new looper thread if there are no more waiting + and we have not yet reached our limit. */ +#if 1 + if ((that->m_waitingThreads <= 0) && (atomic_read(&that->m_loopingThreads) < that->m_maxThreads)) { + DBSPAWN((KERN_WARNING "*** I THINK I WANT TO SPAWN A LOOPER THREAD!\n")); + binder_proc_spawn_looper(that); + } +#endif + + BND_ASSERT(who->nextRequest == NULL, "Thread leaving with a request!"); + BND_ASSERT(list_empty(&who->waitStackEntry), "Thread left on wait stack!"); + + BND_UNLOCK(that->m_lock); + + return err; +} + +void +binder_proc_StartLooper(binder_proc_t *that, bool driver_spawned) +{ + DBLOCK((KERN_WARNING "StartLooper() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + /* When the driver spawns a thread, it incremements the non-blocked + count right away. Otherwise, we must do it now. */ + if (!driver_spawned) that->m_nonblockedThreads++; + atomic_inc(&that->m_loopingThreads); + DPRINTF(0, (KERN_WARNING "*** STARTING A LOOPER FOR %p! Now have %d waiting, %d nonblocked.\n", + that, that->m_waitingThreads, that->m_nonblockedThreads)); + BND_UNLOCK(that->m_lock); +} + +void +binder_proc_FinishLooper(binder_proc_t *that, bool driverSpawned) +{ + DBLOCK((KERN_WARNING "FinishLooper() going to lock %p in %d\n", that, current->pid)); + BND_LOCK(that->m_lock); + that->m_nonblockedThreads--; + DBSPAWN((KERN_WARNING "*** FINISHING A LOOPER FOR %p! Now have %d waiting, %d nonblocked, %d looping.\n", + that, that->m_waitingThreads, that->m_nonblockedThreads, atomic_read(&that->m_loopingThreads))); + if ((that->m_nonblockedThreads <= 1) && that->m_syncCount && binder_proc_IsAlive(that)) { + /* Spawn a thread if all blocked and synchronous transaction pending */ + DBSPAWN((KERN_WARNING "*** FINISH-LOOPER NEEDS TO SPAWN NEW THREAD!\n")); + binder_proc_spawn_looper(that); + } + BND_UNLOCK(that->m_lock); + + if (driverSpawned) { + atomic_dec(&that->m_loopingThreads); + BND_ASSERT(atomic_read(&that->m_loopingThreads) >= 0, "Looping thread count is bad!"); + } +} + +status_t +binder_proc_SetWakeupTime(binder_proc_t *that, bigtime_t time, s32 priority) +{ + unsigned long flags; + bool earlier; + if (time < 0) time = 0; + // convert to jiffies + do_div(time, TICK_NSEC); + time += get_jiffies_64(); + BND_LOCK(that->m_lock); + DPRINTF(4, (KERN_WARNING "%s(%p, %Ld, %d)\n", __func__, that, time, priority)); + spin_lock_irqsave(&that->m_spin_lock, flags); + if (time != that->m_wakeupTime && !(that->m_state & btEventInQueue)) { + DIPRINTF(9, (KERN_WARNING "-- previously %Ld\n", that->m_wakeupTime)); + earlier = time < that->m_wakeupTime; + that->m_wakeupTime = time; + mod_timer(&that->m_wakeupTimer, time); + } + that->m_wakeupPriority = priority; + spin_unlock_irqrestore(&that->m_spin_lock, flags); + BND_UNLOCK(that->m_lock); + return 0; +} + +status_t +binder_proc_SetIdleTimeout(binder_proc_t *that, bigtime_t timeDelta) +{ + DPRINTF(4, (KERN_WARNING "%s(%p, %Ld)\n", __func__, that, timeDelta)); + that->m_idleTimeout = timeDelta; + return 0; +} + +status_t +binder_proc_SetReplyTimeout(binder_proc_t *that, bigtime_t timeDelta) +{ + DPRINTF(4, (KERN_WARNING "%s(%p, %Ld)\n", __func__, that, timeDelta)); + that->m_replyTimeout = timeDelta; + return 0; +} + +status_t +binder_proc_SetMaxThreads(binder_proc_t *that, s32 num) +{ + DPRINTF(4, (KERN_WARNING "%s(%p, %d)\n", __func__, that, num)); + that->m_maxThreads = num; + return 0; +} + +status_t +binder_proc_SetIdlePriority(binder_proc_t *that, s32 pri) +{ + DPRINTF(4, (KERN_WARNING "%s(%p, %d)\n", __func__, that, pri)); + that->m_idlePriority = (pri > B_MIN_PRIORITY_VAL ? + (pri <= B_MAX_PRIORITY_VAL ? pri : B_MAX_PRIORITY_VAL) : + B_MIN_PRIORITY_VAL); + return 0; +} + +#define LARGE_TRANSACTION (64 * 1024) +static range_map_t * binder_proc_free_map_alloc_l(binder_proc_t *that, size_t length) +{ + bool large; + struct rb_node *n; + struct rb_node * (*rbstep)(struct rb_node *); + range_map_t *rm = NULL; + unsigned long avail; + + large = (length > LARGE_TRANSACTION ? TRUE : FALSE); + DPRINTF(5, (KERN_WARNING "%s(%p, %08x) large = %d\n", __func__, that, length, large)); + n = large ? rb_last(&that->m_freeMap) : rb_first(&that->m_freeMap); + rbstep = large ? rb_prev : rb_next; + + while (n) { + rm = rb_entry(n, range_map_t, rm_rb); + avail = rm->end - rm->start; + DPRINTF(5, (KERN_WARNING "%s(%p, %08x) rm = %p [%08lx-%08lx], avail %lu\n", __func__, that, length, rm, rm->start, rm->end, avail)); + if (avail >= length) { + avail -= length; + if (avail) { + range_map_t *newrm = kmem_cache_alloc(range_map_cache, GFP_KERNEL); + // use only part of range + if (large) { + // consume address space from the right + newrm->end = rm->end; + rm->end -= length; + newrm->start = rm->end; + newrm->page = NULL; + } else { + // consume address space from the left + newrm->start = rm->start; + rm->start += length; + newrm->end = rm->start; + } + DPRINTF(5, (KERN_WARNING "%s(%p, %08x) newrm = %p [%08lx-%08lx]\n", __func__, that, length, newrm, newrm->start, newrm->end)); + DPRINTF(5, (KERN_WARNING "%s(%p, %08x) remaining rm = %p [%08lx-%08lx], avail %lu\n", __func__, that, length, rm, rm->start, rm->end, avail)); + newrm->team = that; + rm = newrm; + } else { + // use entire range + rb_erase(n, &that->m_freeMap); + } + break; + } + n = rbstep(n); + rm = NULL; + } + return rm; +} + +range_map_t * binder_proc_free_map_insert(binder_proc_t *that, range_map_t *buffer) +{ + struct rb_node ** p = &that->m_freeMap.rb_node; + struct rb_node * parent = NULL; + range_map_t *rm = NULL; + const unsigned long address = buffer->start; + struct rb_node *next; + struct rb_node *prev; + + DPRINTF(0, (KERN_WARNING "%s(%p, %p) %08lx::%08lx\n", __func__, that, buffer, buffer->start, buffer->end)); + + while (*p) + { + parent = *p; + rm = rb_entry(parent, range_map_t, rm_rb); + + if (address < rm->start) + p = &(*p)->rb_left; + else if (address >= rm->end) + p = &(*p)->rb_right; + else { + DPRINTF(0, (KERN_WARNING "%s found buffer already in the free list!\n", __func__)); + return rm; + } + } + + if (rm) { + if (rm->end == buffer->start) { + DPRINTF(9, (KERN_WARNING "%s: buffer merges to the right\n", __func__)); + // merge to the right + rm->end = buffer->end; + kmem_cache_free(range_map_cache, buffer); + // try merge right again (did we fill up a hole?) + next = rb_next(parent); + if (next) { + range_map_t *rm_next = rb_entry(next, range_map_t, rm_rb); + if (rm->end == rm_next->start) { + DPRINTF(9, (KERN_WARNING "%s: buffer merges to the left, too\n", __func__)); + rm->end = rm_next->end; + rb_erase(next, &that->m_freeMap); + kmem_cache_free(range_map_cache, rm_next); + } + } + return NULL; + } else if (buffer->end == rm->start) { + DPRINTF(9, (KERN_WARNING "%s: buffer merges to the left\n", __func__)); + // merge to the left + rm->start = buffer->start; + kmem_cache_free(range_map_cache, buffer); + // try merge left again (did we fill up a hole?) + prev = rb_prev(parent); + if (prev) { + range_map_t *rm_prev = rb_entry(prev, range_map_t, rm_rb); + if (rm_prev->end == rm->start) { + DPRINTF(9, (KERN_WARNING "%s: buffer merges to the right, too\n", __func__)); + rm->start = rm_prev->start; + rb_erase(prev, &that->m_freeMap); + kmem_cache_free(range_map_cache, rm_prev); + } + } + return NULL; + } + } + DPRINTF(9, (KERN_WARNING "%s: buffer stands alone\n", __func__)); + + // default case: insert in the middle of nowhere + rb_link_node(&buffer->rm_rb, parent, p); + rb_insert_color(&buffer->rm_rb, &that->m_freeMap); + + return NULL; +} + +static inline range_map_t * binder_proc_range_map_insert(binder_proc_t *that, range_map_t *buffer) +{ + struct rb_node ** p = &that->m_rangeMap.rb_node; + struct rb_node * parent = NULL; + range_map_t *rm; + const unsigned long address = buffer->start; + + while (*p) + { + parent = *p; + rm = rb_entry(parent, range_map_t, rm_rb); + + if (address < rm->start) + p = &(*p)->rb_left; + else if (address >= rm->end) + p = &(*p)->rb_right; + else { + DPRINTF(1, (KERN_WARNING "%s: %p (%08lx::%08lx) overlaps with " + "existing entry %p (%08lx::%08lx)\n", + __func__, buffer, buffer->start, buffer->end, + rm, rm->start, rm->end)); + return rm; + } + } + + rb_link_node(&buffer->rm_rb, parent, p); + rb_insert_color(&buffer->rm_rb, &that->m_rangeMap); + + return NULL; +} + +static inline range_map_t * binder_proc_range_map_search(binder_proc_t *that, unsigned long address) +{ + struct rb_node * n = that->m_rangeMap.rb_node; + range_map_t *rm; + DPRINTF(0, (KERN_WARNING "%s(%p, %lu)\n", __func__, that, address)); + + while (n) + { + rm = rb_entry(n, range_map_t, rm_rb); + // range_map covers [start, end) + DPRINTF(9, (KERN_WARNING " -- trying %08lx::%08lx\n", rm->start, rm->end)); + if (address < rm->start) + n = n->rb_left; + else if (address >= rm->end) + n = n->rb_right; + else { + DPRINTF(9, (KERN_WARNING " -- found it!\n")); + return rm; + } + } + DPRINTF(0, (KERN_WARNING " -- failed to find containing range\n")); + return NULL; +} + +#if 0 +// Remove the buffer containing address from the tree. The caller owns the returned memory. +static inline range_map_t * binder_proc_range_map_remove(binder_proc_t *that, unsigned long address) +{ + range_map_t *rm = binder_proc_range_map_search(that, address); + if (rm) rb_erase(&rm->rm_rb, &that->m_rangeMap); + return rm; +} +#endif + +bool +binder_proc_ValidTransactionAddress(binder_proc_t *that, unsigned long address, struct page **pageptr) +{ + // Find the struct page* containing address in the process specified by + // that. Return FALSE and leave *pageptr unchanged if address doesn't + // represent a valid buffer. + + range_map_t *rm; + + BND_LOCK(that->m_map_pool_lock); + rm = binder_proc_range_map_search(that, address); + BND_UNLOCK(that->m_map_pool_lock); + + if (rm) { + unsigned int index = (address - rm->start) >> PAGE_SHIFT; + *pageptr = rm->page + index; + BND_ASSERT(rm->next == NULL, "binder_proc_ValidTransactionAddress found page in free pool"); + return TRUE; + } + return FALSE; +} + +// Alternatively, 2x number of active threads? +#define POOL_THRESHOLD 16 +// POOL_BUFFER_LIMIT should never exceed LARGE_TRANSACTION size, or things will get ugly +#define POOL_BUFFER_LIMIT LARGE_TRANSACTION +range_map_t * +binder_proc_AllocateTransactionBuffer(binder_proc_t *that, size_t size) +{ + // ensure order-sized allocations + unsigned long order = calc_order_from_size(size); + + range_map_t *rm; + unsigned long avail = ~0; + range_map_t **prev; + + BND_LOCK(that->m_map_pool_lock); + + rm = that->m_pool; + prev = &that->m_pool; + + size = (1 << order) << PAGE_SHIFT; + + DPRINTF(0, (KERN_WARNING "%s(%p, %u)\n", __func__, that, size)); + DPRINTF(9, (KERN_WARNING " -- order %lu produces size %u\n", order, size)); + // don't bother checking the pool for large buffers + //if (size < POOL_BUFFER_LIMIT) { + DPRINTF(9, (KERN_WARNING " -- searching the pool\n")); + while (rm && ((avail = rm->end - rm->start) < size)) { + prev = &rm->next; + rm = rm->next; + } + //} + + if (rm && (avail == size)) { + // unlink + *prev = rm->next; + rm->next = NULL; + // un-count + that->m_pool_active--; + DPRINTF(9, (KERN_WARNING " -- reusing transaction buffer\n")); + } else { + DPRINTF(9, (KERN_WARNING " -- allocating a new transaction buffer\n")); + // make a new one + rm = binder_proc_free_map_alloc_l(that, size); + if (rm) { + // allocate RAM for it + rm->page = alloc_pages(GFP_KERNEL | __GFP_REPEAT, order); + if (!rm->page) { + binder_proc_free_map_insert(that, rm); + rm = 0; + DPRINTF(9, (KERN_WARNING " -- allocation failed\n")); + } else { + // add to the valid range maps + rm->next = NULL; + binder_proc_range_map_insert(that, rm); + } + } + } + DPRINTF(9, (KERN_WARNING " -- returning %p\n", rm)); + if (rm) { + DPRINTF(9, (KERN_WARNING " --- %08lx::%08lx\n", rm->start, rm->end)); + } + BND_UNLOCK(that->m_map_pool_lock); + return rm; +} + +void +binder_proc_FreeTransactionBuffer(binder_proc_t *that, range_map_t *buffer) +{ + unsigned long size = buffer->end - buffer->start; + range_map_t *rm; + range_map_t **prev; + + BND_LOCK(that->m_map_pool_lock); + + DPRINTF(5, (KERN_WARNING "%s(%p) m_pool_active: %d, size: %lu\n", __func__, that, that->m_pool_active, size)); + //if ((that->m_pool_active < POOL_THRESHOLD) && (size < POOL_BUFFER_LIMIT)) { + DPRINTF(5, (KERN_WARNING "%d putting %p (%08lx::%08lx) back in the pool\n", current->pid, buffer, buffer->start, buffer->end)); + rm = that->m_pool; + prev = &that->m_pool; + while (rm && ((rm->end - rm->start) < size)) { + prev = &rm->next; + rm = rm->next; + } + buffer->next = rm; + *prev = buffer; + that->m_pool_active++; +#if 0 // This is not safe to enable until we find some way to unmap the page from the userspace + } else { + DPRINTF(5, (KERN_WARNING "%d releasing %p (%08lx::%08lx) for later use\n", current->pid, buffer, buffer->start, buffer->end)); + // unmap the range +#if 0 + // FIXME: use unmap_mapping_range() to unmap pages + // FIXME: "as" always turns up NULL, so unmapping doesn't work + struct address_space *as = page_mapping(buffer->page); + DPRINTF(5, (KERN_WARNING " -- address_space: %p\n", as)); + if (as) unmap_mapping_range(as, buffer->start - that->m_mmap_start, buffer->end - buffer->start, 0); +#endif + // remove from the valid range maps + rb_erase(&buffer->rm_rb, &that->m_rangeMap); + // toss this range + __free_pages(buffer->page, calc_order_from_size(size)); + buffer->page = NULL; + // give back the address space + binder_proc_free_map_insert(that, buffer); + } +#endif + BND_UNLOCK(that->m_map_pool_lock); +} + +/* ALWAYS call this with that->m_lock held */ +void binder_proc_spawn_looper(binder_proc_t *that) +{ + DBSPAWN((KERN_WARNING "%s(%p)\n", __func__, that)); +#if 0 + if ((++that->m_spawningThreads == 1) && binder_proc_IsAlive(that)) { + atomic_inc(&that->m_noop_spawner); + DBSPAWN((KERN_WARNING " -- upped m_noop_spawner to %d\n", atomic_read(&that->m_noop_spawner))); + } +#else + if (binder_proc_IsAlive(that) && (test_and_set_bit(SPAWNING_BIT, &that->m_noop_spawner) == 0)) { + set_bit(DO_SPAWN_BIT, &that->m_noop_spawner); + DBSPAWN((KERN_WARNING " -- upped m_noop_spawner\n")); + ++that->m_waitingThreads; + ++that->m_nonblockedThreads; + } +#endif + DBSPAWN((KERN_WARNING "%s(%p) finished\n", __func__, that)); +} + +void binder_proc_wakeup_timer(unsigned long data) +{ + unsigned long flags; + binder_proc_t *that = (binder_proc_t *)data; + + DIPRINTF(0, (KERN_WARNING "%s(%p) -- Enqueueing handler transaction\n", __func__, that)); + + spin_lock_irqsave(&that->m_spin_lock, flags); + + BND_ASSERT(that->m_eventTransaction != NULL, "m_eventTransaction == NULL"); + + if(!(that->m_state & btEventInQueue)) { + BND_ASSERT(that->m_eventTransaction->next == NULL, "Event transaction already in queue!"); + binder_transaction_SetPriority(that->m_eventTransaction, (s16)that->m_wakeupPriority); + that->m_wakeupTime = B_INFINITE_TIMEOUT; + that->m_wakeupPriority = B_LOW_PRIORITY; // this value should not be used anywhere + that->m_state |= btEventInQueue; + + binder_proc_DeliverTransacton(that, that->m_eventTransaction); + } + else { + BND_ASSERT(0, "event already in queue"); + } + spin_unlock_irqrestore(&that->m_spin_lock, flags); +} + +void binder_proc_idle_timer(unsigned long data) +{ + unsigned long flags; + binder_proc_t *that = (binder_proc_t *)data; + binder_thread_t *thread; + + DIPRINTF(0, (KERN_WARNING "%s(%p) -- Signal idle thread\n", __func__, that)); + + spin_lock_irqsave(&that->m_spin_lock, flags); + + if(that->m_waitStackCount > BND_PROC_MAX_IDLE_THREADS) { + BND_ASSERT(!list_empty(&that->m_waitStack), "bad m_waitStackCount"); + thread = list_entry(that->m_waitStack.prev, binder_thread_t, waitStackEntry); + thread->wakeReason = WAKE_REASON_IDLE; + binder_proc_RemoveThreadFromWaitStack(that, thread); + binder_thread_Wakeup(thread); + } + else { + DBSPAWN((KERN_WARNING "%s(%p) idle timer ignored\n", __func__, that)); + } + spin_unlock_irqrestore(&that->m_spin_lock, flags); +} + diff -Nru linux-2.6.23/drivers/binder/binder_proc.h kernel.android/drivers/binder/binder_proc.h --- linux-2.6.23/drivers/binder/binder_proc.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_proc.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,226 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef BINDER_PROC_H +#define BINDER_PROC_H + +#include +#include +#include +#include +#include "binder_defs.h" +#include "binder_thread.h" +#include "iobuffer.h" + +// This "error" is returned by WaitForRequest() when a timed event +// is scheduled to happen. +enum { + REQUEST_EVENT_READY = 1, + DEATH_NOTIFICATION_READY = 2 +}; + +typedef struct descriptor { + struct binder_node *node; + s32 priRef; + s32 secRef; +} descriptor_t; + +typedef struct reverse_mapping { + struct reverse_mapping *next; + struct binder_node *node; + s32 descriptor; +} reverse_mapping_t; + +typedef struct local_mapping { + struct local_mapping *next; + void *ptr; // Unique token identifying this object (supplied by user space) + void *cookie; // Arbitrary data for user space to associate with the object/token + struct binder_node *node; +} local_mapping_t; + +typedef struct range_map { + unsigned long start; // inclusive + unsigned long end; // non-inclusive + struct page *page; + struct range_map* next; // next in the chain of free buffers + struct rb_node rm_rb; + struct binder_proc *team; +} range_map_t; + +typedef struct death_notification { + atomic_t ref_count; + struct hlist_node observer; + struct hlist_node observed_or_active; + void *cookie; + struct binder_proc *observer_proc; + struct binder_proc *observed_proc; // or NULL if already sent +} death_notification_t; + +enum { + btEventInQueue = 0x00000002, + btDying = 0x00000004, + btDead = 0x00000008, + btCleaned = 0x00000010, + btFreed = 0x00000020 +}; + +enum { + WAKE_THREAD_FOR_PROCESS_DEATH = 1 +}; + +typedef struct binder_proc { + atomic_t m_primaryRefs; + atomic_t m_secondaryRefs; + volatile unsigned long m_noop_spawner; +#define SPAWNING_BIT 0 +#define DO_SPAWN_BIT 1 + struct semaphore m_lock; + spinlock_t m_spin_lock; + struct semaphore m_map_pool_lock; + u32 m_state; + struct binder_thread * m_threads; + struct list_head m_waitStack; + int m_waitStackCount; + u32 m_wakeThreadMask; + bigtime_t m_wakeupTime; + s32 m_wakeupPriority; + struct timer_list m_wakeupTimer; + struct timer_list m_idleTimer; + bigtime_t m_idleTimeout; + bigtime_t m_replyTimeout; + s32 m_syncCount; + s32 m_freeCount; + struct binder_transaction * m_head; + struct binder_transaction ** m_tail; + struct binder_transaction * m_needFree; + struct binder_transaction * m_eventTransaction; + local_mapping_t * m_localHash[HASH_SIZE]; + struct binder_node * m_rootObject; // only use for comparison!! + s32 m_rootStopsProcess; + s32 m_numRemoteStrongRefs; + reverse_mapping_t * m_reverseHash[HASH_SIZE]; + descriptor_t * m_descriptors; + s32 m_descriptorCount; + s32 m_nonblockedThreads; + s32 m_waitingThreads; + s32 m_maxThreads; + s32 m_idlePriority; + atomic_t m_loopingThreads; + // s32 m_spawningThreads; + unsigned long m_mmap_start; // inclusive + struct rb_root m_rangeMap; + struct rb_root m_freeMap; + range_map_t *m_pool; + size_t m_pool_active; + struct hlist_head m_incoming_death_notifications; + struct hlist_head m_outgoing_death_notifications; + struct hlist_head m_pending_death_notifications; // ready to be sent to user space + struct hlist_head m_active_death_notifications; // already sent to user space + struct hlist_head m_deleted_death_notifications; +} binder_proc_t; + + +binder_proc_t * new_binder_proc(void); +#if 0 +binder_proc_t * new_binder_proc_with_parent(pid_t id, pid_t mainThid, struct binder_thread *parent); +#endif +void binder_proc_destroy(binder_proc_t *that); + +#define binder_proc_IsAlive(that) ((that->m_state&(btDying|btDead)) == 0) +// bool binder_proc_IsAlive(binder_proc_t *that) const; +void binder_proc_Released(binder_proc_t *that); + +void binder_proc_Die(binder_proc_t *that, bool locked /* = false */); + +BND_DECLARE_ACQUIRE_RELEASE(binder_proc); +BND_DECLARE_ATTEMPT_ACQUIRE(binder_proc); + +void binder_proc_SetRootObject(binder_proc_t *that, struct binder_node *node); + +void binder_proc_Stop(binder_proc_t *that, bool now); + +bool binder_proc_AddThread(binder_proc_t *that, binder_thread_t *t); +void binder_proc_RemoveThread(binder_proc_t *that, struct binder_thread *t); + +status_t binder_proc_WaitForRequest(binder_proc_t *that, struct binder_thread* who, struct binder_transaction **t); + +void binder_proc_GetPendingDeathNotifications(binder_proc_t *that, binder_thread_t *thread, iobuffer_t *io); + +/* Call when a thread receives its bcREGISTER_LOOPER command. */ +void binder_proc_StartLooper(binder_proc_t *that, bool driver_spawned); +/* Call when exiting a thread who has been told bcREGISTER_LOOPER. */ +void binder_proc_FinishLooper(binder_proc_t *that, bool driverSpawned); + +status_t binder_proc_SetWakeupTime(binder_proc_t *that, bigtime_t time, s32 priority); +status_t binder_proc_SetIdleTimeout(binder_proc_t *that, bigtime_t timeDelta); +status_t binder_proc_SetReplyTimeout(binder_proc_t *that, bigtime_t timeDelta); +status_t binder_proc_SetMaxThreads(binder_proc_t *that, s32 num); +status_t binder_proc_SetIdlePriority(binder_proc_t *that, s32 pri); + +/* Call to place a transaction in to this team's queue. */ +status_t binder_proc_Transact(binder_proc_t *that, struct binder_transaction *t); + +/* Management of transactions that are waiting to be deallocated. + These are safe to call with only a secondary reference on the + team. +*/ +status_t binder_proc_AddToNeedFreeList(binder_proc_t *that, struct binder_transaction *t); +status_t binder_proc_FreeBuffer(binder_proc_t *that, void *p); + +bool binder_proc_RefDescriptor(binder_proc_t *that, s32 descriptor, s32 type); +bool binder_proc_UnrefDescriptor(binder_proc_t *that, s32 descriptor, s32 type); +bool binder_proc_RemoveLocalMapping(binder_proc_t *that, void *ptr, struct binder_node *node); + +/* Called by binder_node when its last strong reference goes away, for the process to + do the appropriate bookkeeping. */ +void binder_proc_RemoveLocalStrongRef(binder_proc_t *that, struct binder_node *node); + +/* Called by binder_proc_ForceRefNode() if it is restoring the first strong reference + back on to the node. */ +void binder_proc_AddLocalStrongRef(binder_proc_t *that, struct binder_node *node); + +/* Attempt to acquire a primary reference on the given descriptor. + The result will be true if this succeeded, in which case you + can just continue with it. If the result is false, then + 'out_target' may be set to the binder_node_t the you are making + the attempt on. You can execute a transaction to the node + to attempt the acquire on it, and -must- release a SECONDARY + reference on the node which this function acquired. */ +bool binder_proc_AttemptRefDescriptor(binder_proc_t *that, s32 descriptor, struct binder_node **out_target); + +/* Forcibly increment the primary reference count of the given, + in response to a successful binder_proc_AttemptAcquire(). */ +void binder_proc_ForceRefNode(binder_proc_t *that, struct binder_node *node, iobuffer_t *io); + +s32 binder_proc_Node2Descriptor(binder_proc_t *that, struct binder_node *node, bool ref /* = true */, s32 type /* = PRIMARY */); +struct binder_node * binder_proc_Descriptor2Node(binder_proc_t *that, s32 descriptor, const void* id, s32 type /* = PRIMARY */); +status_t binder_proc_Ptr2Node(binder_proc_t *that, void *ptr, void *cookie, struct binder_node **n, iobuffer_t *io, const void* id, s32 type /* = PRIMARY */); + +/* death notifications */ +status_t binder_proc_RequestDeathNotification(binder_proc_t *that, binder_proc_t *client, void *cookie); +status_t binder_proc_ClearDeathNotification(binder_proc_t *that, binder_proc_t *client, void *cookie); +status_t binder_proc_DeadBinderDone(binder_proc_t *that, void *cookie); // called on client proc + +status_t binder_proc_TakeMeOffYourList(binder_proc_t *that); +status_t binder_proc_PutMeBackInTheGameCoach(binder_proc_t *that); + +bool binder_proc_ValidTransactionAddress(binder_proc_t *that, unsigned long address, struct page **pageptr); +range_map_t * binder_proc_AllocateTransactionBuffer(binder_proc_t *that, size_t size); +void binder_proc_FreeTransactionBuffer(binder_proc_t *that, range_map_t *buffer); +range_map_t * binder_proc_free_map_insert(binder_proc_t *that, range_map_t *buffer); +#endif // BINDER_PROC_H diff -Nru linux-2.6.23/drivers/binder/binder_thread.c kernel.android/drivers/binder/binder_thread.c --- linux-2.6.23/drivers/binder/binder_thread.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_thread.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,1575 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "binder_defs.h" +#include "binder_thread.h" +#include "binder_proc.h" +#include "binder_node.h" +#include "binder_transaction.h" +#include "iobuffer.h" + +#include +#include +#include + +static void binder_thread_Cleanup(binder_thread_t *that); + +static status_t binder_thread_WaitForReply(binder_thread_t *that, iobuffer_t *io); +static status_t binder_thread_WaitForRequest(binder_thread_t *that, iobuffer_t *io); +static status_t binder_thread_ReturnTransaction(binder_thread_t *that, iobuffer_t *io, binder_transaction_t *t); + +// static void binder_thread_WriteReturn(binder_thread_t *that, void *buffer, int size); + +// static void binder_thread_EnqueueTransaction(binder_thread_t *that, binder_transaction_t *t); + +// Set non-zero to do the capable(CAP_SYS_ADMIN) check +#define CHECK_CAPS 0 + +static binder_node_t *gContextManagerNode = NULL; +static DECLARE_MUTEX(gContextManagerNodeLock); +static atomic_t g_count = ATOMIC_INIT(0); + +int +binder_thread_GlobalCount() +{ + return atomic_read(&g_count); +} + +binder_thread_t * binder_thread_init(int thid, binder_proc_t *team) +{ + binder_thread_t *that; + + that = (binder_thread_t*)kmem_cache_alloc(thread_cache, GFP_KERNEL); + if (that) { + atomic_inc(&g_count); + that->attachedToThread = FALSE; + that->next = NULL; + INIT_LIST_HEAD(&that->waitStackEntry); + that->pendingChild = NULL; + that->nextRequest = NULL; + that->wakeReason = WAKE_REASON_NONE; + that->virtualThid = 0; + atomic_set(&that->m_primaryRefs, 0); + atomic_set(&that->m_secondaryRefs, 0); + atomic_set(&that->m_wake_count, 0); + that->m_err = 0; + init_MUTEX(&that->m_lock); + init_waitqueue_head(&that->m_wait); + that->m_waitForReply = 0; + that->m_reply = NULL; + that->m_consume = 0; + that->m_thid = thid; + that->m_team = team; + if (team != NULL) + BND_ACQUIRE(binder_proc, that->m_team, WEAK, that); + that->m_pendingReply = NULL; + that->m_pendingRefResolution = NULL; + that->m_teamRefs = 0; + that->m_isSpawned = FALSE; + that->m_isLooping = FALSE; + that->m_firstLoop = TRUE; + that->m_shortAttemptAcquire = FALSE; + that->m_pendingReplyIsRoot = FALSE; + that->m_failedRootReceive = FALSE; + that->m_failedRootReply = FALSE; + DPRINTF(5, (KERN_WARNING "*** CREATING THREAD %p (%p:%d)\n", that, that->m_team, that->m_thid)); + } + DBSHUTDOWN((KERN_WARNING "%s(%u, %p): %p\n", __func__, thid, team, that)); + return that; +} + +void binder_thread_destroy(binder_thread_t *that) +{ + DBSHUTDOWN((KERN_WARNING "binder_thread_destroy(%p, %p):%d\n", that, that->m_team, that->m_thid)); + if (that->m_isLooping && that->m_team && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_FinishLooper(that->m_team, that->m_isSpawned); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + // We don't care about process, here. + //if (find_thread(that->m_thid, 0, TRUE) != that) { + //DPRINTF(1, (KERN_WARNING "binder_thread_destroy(%p): couldn't find ourselves in the thread hash\n", that)); + //} + + binder_thread_Cleanup(that); + + if (that->m_team) { + BND_RELEASE(binder_proc, that->m_team, WEAK, that); + that->m_team = NULL; + } + + atomic_dec(&g_count); + + // free_lock(&that->m_lock); + kmem_cache_free(thread_cache, that); +} + +void +binder_thread_Released(binder_thread_t *that) +{ + DBSHUTDOWN((KERN_WARNING "%s(%p, %p):%d\n", __func__, that, that->m_team, that->m_thid)); + binder_thread_Die(that); +} + +void +binder_thread_Die(binder_thread_t *that) +{ + DBSHUTDOWN((KERN_WARNING "%s(%p) (%p:%d) in %d\n", __func__, that, that->m_team, binder_thread_Thid(that), current->pid)); + + // Always do this, even if all primary references on the team + // are gone. This is the only way the thread list gets cleaned up. + if (that->m_team != NULL) + binder_proc_RemoveThread(that->m_team, that); + + binder_thread_Cleanup(that); + + /* + * Linux doesn't seem to have an equivalent to delet_sem() + * delete_sem(that->m_ioSem); that->m_ioSem = B_BAD_SEM_ID; + */ + + DBSHUTDOWN((KERN_WARNING "Binder thread %p:%d: DEAD!\n", that->m_team, that->m_thid)); +} + +bool binder_thread_SetParentThread(binder_thread_t *that, binder_thread_t *replyTo) +{ + bool success; + + DPRINTF(4, (KERN_WARNING "binder_thread_SetParentThread(%p, %p)\n", that, replyTo)); + + BND_LOCK(that->m_lock); + if ((success = !that->m_failedRootReply)) { + + BND_ASSERT(!that->m_pendingReply, "Attaching to child thread that already has someone waiting for a reply!"); + that->m_pendingReply = binder_transaction_CreateEmpty(); + binder_transaction_SetRootObject(that->m_pendingReply, TRUE); + that->m_pendingReply->sender = replyTo; + that->m_pendingReplyIsRoot = TRUE; + BND_ACQUIRE(binder_thread, replyTo, WEAK, m_pendingReply); + + // The thread now has the reply info, so allow it to wake up and reply. + binder_thread_Wakeup(that); + } + BND_UNLOCK(that->m_lock); + + return success; +} + +void binder_thread_ReleasePendingChild(binder_thread_t *that) +{ + binder_thread_t *child; + BND_LOCK(that->m_lock); + DPRINTF(4, (KERN_WARNING "binder_thread_ReleasePendingChild(%p): child=%p\n", that, that->pendingChild)); + child = that->pendingChild; + that->pendingChild = NULL; + BND_UNLOCK(that->m_lock); + + if (child) { + forget_thread(child); + } +} + +void binder_thread_AttachProcess(binder_thread_t *that, struct binder_proc *team) +{ + bool attached = FALSE; + + DPRINTF(4, (KERN_WARNING "binder_thread_AttachProcess(%p, %p)\n", that, team)); + + BND_LOCK(that->m_lock); + + BND_ASSERT(!that->m_team, "Child thread is already attached to its process!"); + if (that->m_team == NULL) { + attached = TRUE; + that->m_team = team; + BND_ACQUIRE(binder_proc, team, WEAK, that); + } + + BND_UNLOCK(that->m_lock); + + if (attached) { + if(!binder_proc_AddThread(team, that)) { + BND_ASSERT(0, "attached thread to dying process"); + } + } +} + +void +binder_thread_Cleanup(binder_thread_t *that) +{ + binder_transaction_t *cmd, *pendingRef; + binder_transaction_t *pendingReply; + binder_transaction_t *reply; + binder_node_t *contextManagerNode; + int relCount; + bool first; + + BND_LOCK(that->m_lock); + pendingRef = that->m_pendingRefResolution; + that->m_pendingRefResolution = NULL; + pendingReply = that->m_pendingReply; + that->m_pendingReply = NULL; + reply = that->m_reply; + that->m_reply = NULL; + relCount = that->m_teamRefs; + that->m_teamRefs = 0; + DPRINTF(0, (KERN_WARNING "%s(%p):%p,%d strong: %d, weak: %d\n", __func__, that, that->m_team, that->m_thid, that->m_primaryRefs.counter, that->m_secondaryRefs.counter)); + BND_UNLOCK(that->m_lock); + + while (relCount) { + if (that->m_team) + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + relCount--; + } + + first = TRUE; + while ((cmd = pendingRef)) { + if (first) { + first = FALSE; + DPRINTF(5, (KERN_WARNING "Binder thread %p:%d: cleaning up pending ref resolution.\n", that->m_team, that->m_thid)); + } + pendingRef = cmd->next; + DPRINTF(5, (KERN_WARNING "Deleting transaction %p\n", cmd)); + binder_transaction_DestroyNoRefs(cmd); + } + + first = TRUE; + while ((cmd = pendingReply)) { + if (first) { + first = FALSE; + DPRINTF(5, (KERN_WARNING "Binder thread %p:%d: cleaning up pending replies.\n", that->m_team, that->m_thid)); + } + if (cmd->sender) { + DPRINTF(5, (KERN_WARNING "Returning transaction %p to thread %p (%d)\n", + cmd, cmd->sender, binder_thread_Thid(cmd->sender))); + binder_thread_ReplyDead(cmd->sender); + } + pendingReply = cmd->next; + binder_transaction_Destroy(cmd); + } + + first = TRUE; + while ((cmd = reply)) { + if (first) { + first = FALSE; + DPRINTF(5, (KERN_WARNING "Binder thread %p:%d: cleaning up received replies.\n", that->m_team, that->m_thid)); + } + reply = cmd->next; + DPRINTF(5, (KERN_WARNING "Deleting transaction %p\n", cmd)); + binder_transaction_Destroy(cmd); + } + BND_LOCK(gContextManagerNodeLock); + if (gContextManagerNode && (gContextManagerNode->m_home == that->m_team && that->m_team->m_threads == NULL)) { + contextManagerNode = gContextManagerNode; + gContextManagerNode = NULL; + } + else { + contextManagerNode = NULL; + } + BND_UNLOCK(gContextManagerNodeLock); + if(contextManagerNode != NULL) { + DPRINTF(2, (KERN_WARNING "team %08lx is not longer the context manager\n", (unsigned long)that->m_team)); + binder_node_destroy(contextManagerNode); + } + + binder_thread_ReleasePendingChild(that); + + // Make sure this thread returns to user space. + binder_thread_Wakeup(that); +} + +int +binder_thread_Control(binder_thread_t *that, unsigned int cmd, void *buffer) +{ + int result = -EINVAL; + unsigned int size = _IOC_SIZE(cmd); + + //ddprintf("binder -- ioctl %d, size=%d\n", cmd, size); + + DPRINTF(2, (KERN_WARNING "%s(%p, %d, %p): proc=%p\n", __func__, that, cmd, buffer, that->m_team)); + + switch (cmd) { + case BINDER_WRITE_READ: + DPRINTF(2, (KERN_WARNING "BINDER_WRITE_READ: %p:%d\n", that->m_team, that->m_thid)); + if (size >= sizeof(binder_write_read_t)) { + binder_write_read_t bwr; + if (copy_from_user(&bwr, buffer, sizeof(bwr)) == 0) { + DPRINTF(2, (KERN_WARNING " -- write %ld at %08lx\n -- read %ld at %08lx\n", bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer)); + if (bwr.write_size > 0) { + result = binder_thread_Write(that, (void *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); + if (result < 0) { + bwr.read_consumed = 0; + copy_to_user(buffer, &bwr, sizeof(bwr)); + goto getout; + } + } + if (bwr.read_size > 0) { + result = binder_thread_Read(that, (void *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed); + if (result < 0) { + // For ERESTARTSYS, we have to propagate the fact + // that we've already done any writes. + //if (result != -ERESTARTSYS) { + //bwr.read_size = result; // FIXME? + //} + copy_to_user(buffer, &bwr, sizeof(bwr)); + goto getout; + } + } + copy_to_user(buffer, &bwr, sizeof(bwr)); + result = 0; + } + } + break; + case BINDER_SET_WAKEUP_TIME: + if (size >= sizeof(binder_wakeup_time_t) && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_wakeup_time_t *time = (binder_wakeup_time_t*)buffer; + result = binder_proc_SetWakeupTime(that->m_team, time->time, time->priority); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + break; + case BINDER_SET_IDLE_TIMEOUT: + if (size >= 8 && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + result = binder_proc_SetIdleTimeout(that->m_team, *((bigtime_t*)buffer)); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + break; + case BINDER_SET_REPLY_TIMEOUT: + if (size >= 8 && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + result = binder_proc_SetReplyTimeout(that->m_team, *((bigtime_t*)buffer)); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + break; + case BINDER_SET_MAX_THREADS: + if (size >= 4 && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + result = binder_proc_SetMaxThreads(that->m_team, *((int*)buffer)); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + break; + case BINDER_SET_IDLE_PRIORITY: + if (size >= 4 && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + result = binder_proc_SetIdlePriority(that->m_team, *((int*)buffer)); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + break; + case BINDER_SET_CONTEXT_MGR: + if (size >= 4 && BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + DPRINTF(2, (KERN_WARNING "bcSET_CONTEXT_MANAGER attempt by %p\n", that->m_team)); + // LOCK + // check for existing context + BND_LOCK(gContextManagerNodeLock); + if (!gContextManagerNode) { + // check for administration rights +#if CHECK_CAPS + if (capable(CAP_SYS_ADMIN)) { +#endif + gContextManagerNode = binder_node_init(that->m_team, NULL, NULL); + BND_FIRST_ACQUIRE(binder_node, gContextManagerNode, STRONG, that->m_team); + DPRINTF(2, (KERN_WARNING "making team %08lx context manager\n", (unsigned long)that->m_team)); + result = 0; +#if CHECK_CAPS + } else { + DPRINTF(2, (KERN_WARNING "%p doesn't have CAP_SYS_ADMIN rights\n", that->m_team)); + } +#endif + } else { + DPRINTF(2, (KERN_WARNING "gContextManagerNode already set to %p by %08lx", gContextManagerNode, (unsigned long)that->m_team)); + } + BND_UNLOCK(gContextManagerNodeLock); + // UNLOCK + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + break; + case BINDER_THREAD_EXIT: + BND_RELEASE(binder_thread, that, STRONG, 0); + result = 0; + break; + case BINDER_VERSION: + if (size >= sizeof(binder_version_t)) { + binder_version_t *vers = (binder_version_t*)buffer; + vers->protocol_version = BINDER_CURRENT_PROTOCOL_VERSION; + result = 0; + } + break; + default: + break; + } + +getout: + DPRINTF(2, (KERN_WARNING "%s(%p, %d, %p): proc=%p: result=%d\n", __func__, that, cmd, buffer, that->m_team, -result)); + + return result; +} + +int +binder_thread_Write(binder_thread_t *that, void *_buffer, int _size, signed long *consumed) +{ + int result, cmd, target; + binder_node_t *n; + iobuffer_t io; + + DPRINTF(2, (KERN_WARNING "binder_thread_Write(%p, %d)\n", _buffer, _size)); + if (that->m_err) return that->m_err; + if (!binder_proc_IsAlive(that->m_team)) return -ECONNREFUSED; + result = iobuffer_init(&io, (unsigned long)_buffer, _size, *consumed); + if (result) return result; + + while (1) { + if (that->m_consume) { + that->m_consume -= iobuffer_drain(&io, that->m_consume); + iobuffer_mark_consumed(&io); + } + target = -1; + if (iobuffer_read_u32(&io, &cmd)) goto finished; + DPRINTF(5, (KERN_WARNING "cmd: %d\n",cmd)); + switch (cmd) { + case bcINCREFS: { + if (iobuffer_read_u32(&io, &target)) goto finished; + DBREFS((KERN_WARNING "bcINCREFS of %d\n", target)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_RefDescriptor(that->m_team, target, WEAK); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcINCREFS_DONE: { + void *ptr; + void *cookie; + if (iobuffer_read_void(&io, &ptr)) goto finished; + if (iobuffer_read_void(&io, &cookie)) goto finished; + DBREFS((KERN_WARNING "bcINCREFS_DONE of %p\n", ptr)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + if (binder_proc_Ptr2Node(that->m_team, ptr, cookie, &n, NULL, that, WEAK) == 0) { + BND_RELEASE(binder_node, n, WEAK, that->m_team); + BND_RELEASE(binder_node, n, WEAK, that->m_team); + } + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcACQUIRE: { + if (iobuffer_read_u32(&io, &target)) goto finished; + DBREFS((KERN_WARNING "bcACQUIRE of %d\n", target)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_RefDescriptor(that->m_team, target, STRONG); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcACQUIRE_DONE: { + void *ptr; + void *cookie; + if (iobuffer_read_void(&io, &ptr)) goto finished; + if (iobuffer_read_void(&io, &cookie)) goto finished; + DBREFS((KERN_WARNING "bcACQUIRE_DONE of %p\n", ptr)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + if (binder_proc_Ptr2Node(that->m_team, ptr, cookie, &n, NULL, that, STRONG) == 0) { + BND_RELEASE(binder_node, n, STRONG, that->m_team); + BND_RELEASE(binder_node, n, STRONG, that->m_team); + } + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcATTEMPT_ACQUIRE: { + int priority; + if (iobuffer_read_u32(&io, &priority)) goto finished; + if (iobuffer_read_u32(&io, &target)) goto finished; + DBREFS((KERN_WARNING "bcATTEMPT_ACQUIRE of %d\n", target)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_node_t *node; + if (binder_proc_AttemptRefDescriptor(that->m_team, target, &node)) { + DBREFS((KERN_WARNING "Immediate Success!\n")); + BND_ASSERT(!that->m_shortAttemptAcquire, "Already have AttemptAcquire result! (now succeeding)"); + that->m_shortAttemptAcquire = TRUE; + that->m_resultAttemptAcquire = TRUE; + } else if (node) { + binder_transaction_t *t; + // Need to wait for a synchronous acquire attempt + // on the remote node. Note that the transaction has + // special code to understand a tfAttemptAcquire, taking + // ownership of the secondary reference on 'node'. + DBREFS((KERN_WARNING "Sending off to owner!\n")); + t = binder_transaction_CreateRef(tfAttemptAcquire, binder_node_Ptr(node), binder_node_Cookie(node), that->m_team); + binder_transaction_SetPriority(t, (s16)priority); + t->target = node; + binder_transaction_SetInline(t, TRUE); + BND_LOCK(that->m_lock); + t->next = that->m_pendingRefResolution; + that->m_pendingRefResolution = t; + BND_UNLOCK(that->m_lock); + } else { + DBREFS((KERN_WARNING "Immediate Failure!\n")); + BND_ASSERT(!that->m_shortAttemptAcquire, "Already have AttemptAcquire result! (now failing)"); + that->m_shortAttemptAcquire = TRUE; + that->m_resultAttemptAcquire = FALSE; + } + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } else { + DBREFS((KERN_WARNING "Team Failure!\n")); + BND_ASSERT(!that->m_shortAttemptAcquire, "Already have AttemptAcquire result! (now team failing)"); + that->m_shortAttemptAcquire = TRUE; + that->m_resultAttemptAcquire = FALSE; + } + iobuffer_mark_consumed(&io); + } break; + case bcACQUIRE_RESULT: { + int result; + binder_transaction_t *t; + if (iobuffer_read_u32(&io, &result)) goto finished; + iobuffer_mark_consumed(&io); + DBREFS((KERN_WARNING "bcACQUIRE_RESULT: %d\n",result)); + t = binder_transaction_Create(0, 0, 0, 0, NULL); + binder_transaction_SetAcquireReply(t, TRUE); + binder_transaction_SetInline(t, TRUE); + *(int *)t->data = result; + BND_LOCK(that->m_lock); + t->next = that->m_pendingRefResolution; + that->m_pendingRefResolution = t; + BND_UNLOCK(that->m_lock); + } break; + case bcRELEASE: { + if (iobuffer_read_u32(&io, &target)) goto finished; + DBREFS((KERN_WARNING "bcRELEASE of %d\n", target)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_UnrefDescriptor(that->m_team, target, STRONG); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcDECREFS: { + if (iobuffer_read_u32(&io, &target)) goto finished; + DBREFS((KERN_WARNING "bcDECREFS of %d\n", target)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_UnrefDescriptor(that->m_team, target, WEAK); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcFREE_BUFFER: { + void *ptr; + if (iobuffer_read_void(&io, &ptr)) goto finished; + DPRINTF(5, (KERN_WARNING "bcFREE_BUFFER: %p\n",ptr)); + BND_LOCK(that->m_lock); + if (that->m_pendingReply && that->m_pendingReply->map != NULL && binder_transaction_UserData(that->m_pendingReply) == ptr) { + // Data freed before reply sent. Remember this to free + // the transaction when we finally get its reply. + binder_transaction_SetFreePending(that->m_pendingReply, TRUE); + BND_UNLOCK(that->m_lock); + } else { + BND_UNLOCK(that->m_lock); + binder_proc_FreeBuffer(that->m_team, ptr); + } + iobuffer_mark_consumed(&io); + } break; + case bcRETRIEVE_ROOT_OBJECT: { + int pid; + binder_thread_t *child; + if (iobuffer_read_u32(&io, &pid)) goto finished; + DPRINTF(2, (KERN_WARNING "bcRETRIEVE_ROOT_OBJECT: process %d\n", pid)); + child = attach_child_thread((pid_t)pid, that); + DPRINTF(2, (KERN_WARNING "bcRETRIEVE_ROOT_OBJECT: child binder_thread=%p\n", child)); + + BND_LOCK(that->m_lock); + if (child) { + that->pendingChild = child; + that->m_waitForReply++; + } else { + that->m_failedRootReceive = TRUE; + } + BND_UNLOCK(that->m_lock); + + iobuffer_mark_consumed(&io); + } break; + case bcTRANSACTION: + case bcREPLY: { + binder_transaction_data_t tr; + + if(cmd == bcTRANSACTION) { + DPRINTF(5, (KERN_WARNING "bcTRANSACTION\n")); + } + else { + DPRINTF(5, (KERN_WARNING "bcREPLY\n")); + } + + if (iobuffer_read_raw(&io, &tr, sizeof(tr))) goto finished; + if (tr.flags & tfInline) { + // ddprintf("inline transactions not supported yet\n"); + that->m_consume = tr.data_size - sizeof(tr.data); + iobuffer_mark_consumed(&io); + } else if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_transaction_t *t; + iobuffer_mark_consumed(&io); +/* + if (tr.data_size && !is_valid_range(tr.data.ptr.buffer, tr.data_size, PROT_UWR)) { + that->m_err = -EINVAL; + goto finished; + } + if (tr.offsets_size && !is_valid_range(tr.data.ptr.offsets, tr.offsets_size, PROT_UWR)) { + that->m_err = -EINVAL; + goto finished; + } +*/ + t = binder_transaction_Create(tr.code, tr.data_size, tr.data.ptr.buffer, tr.offsets_size, tr.data.ptr.offsets); + binder_transaction_SetUserFlags(t, tr.flags); + binder_transaction_SetPriority(t, (s16)tr.priority); + binder_transaction_SetReply(t, cmd == bcREPLY); + DPRINTF(4, ("Command %s %p: size=%p, first=%p\n", + cmd == bcTRANSACTION ? "transaction" : "reply", t, + tr.data_size, tr.data_size > 0 ? (*(u32*)tr.data.ptr.buffer) : 0)); + if (cmd == bcTRANSACTION) { + target = tr.target.handle; + if(target) { + t->target = binder_proc_Descriptor2Node(that->m_team, target, t, STRONG); + BND_ASSERT(t->target, "Failure converting target descriptor to node"); + } + else { + BND_LOCK(gContextManagerNodeLock); + if (gContextManagerNode && BND_ATTEMPT_ACQUIRE(binder_node, gContextManagerNode, STRONG, t)) { + t->target = gContextManagerNode; + } + else { + DPRINTF(0, (KERN_WARNING "Failed to acquire context manager node\n")); + t->target = NULL; + } + BND_UNLOCK(gContextManagerNodeLock); + } + DPRINTF(4, (KERN_WARNING "Transacting %p to %d(%p) in team %p\n", t, target, t->target, t->target ? t->target->m_home : NULL)); + } + + BND_LOCK(that->m_lock); + t->next = that->m_pendingRefResolution; + that->m_pendingRefResolution = t; + if (that->m_pendingReply && binder_transaction_IsRootObject(that->m_pendingReply)) { + BND_ASSERT(binder_transaction_IsRootObject(t), "EXPECTING ROOT REPLY!"); + } else { + BND_ASSERT(!that->m_pendingReply || !binder_transaction_IsRootObject(t), "UNEXPECTED ROOT REPLY!"); + } + BND_UNLOCK(that->m_lock); + + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + } break; + case bcREGISTER_LOOPER: { + DPRINTF(5, (KERN_WARNING "bcREGISTER_LOOPER for %p (%p:%d)\n", that, that->m_team, that->m_thid)); + BND_ASSERT(that->m_isSpawned == FALSE, "m_isSpawned in bcREGISTER_LOOPER"); + BND_ASSERT(that->m_isLooping == FALSE, "m_isLooping in bcREGISTER_LOOPER"); + that->m_isSpawned = TRUE; + that->m_isLooping = TRUE; + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_StartLooper(that->m_team, TRUE); + clear_bit(SPAWNING_BIT, &that->m_team->m_noop_spawner); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcENTER_LOOPER: { + DPRINTF(5, (KERN_WARNING "bcENTER_LOOPER for %p (%p:%d)\n", that, that->m_team, that->m_thid)); + /* This thread is going to loop, but it's not one of the + driver's own loopers. */ + // ASSERT(that->m_isLooping == FALSE); + that->m_isLooping = TRUE; + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_StartLooper(that->m_team, FALSE); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcEXIT_LOOPER: { + /* End of a looper that is not the driver's own. */ + DBSPAWN((KERN_WARNING "*** THREAD %p:%d RECEIVED bcEXIT_LOOPER\n", that->m_team, that->m_thid)); + if (binder_proc_IsAlive(that->m_team)) { + // ASSERT(that->m_isLooping == TRUE); + that->m_isLooping = FALSE; + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_FinishLooper(that->m_team, FALSE); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + } + iobuffer_mark_consumed(&io); + } break; +#if 0 + case bcCATCH_ROOT_OBJECTS: { + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + if (binder_proc_IsAlive(that->m_team)) { + binder_proc_StartCapturingRootObjects(that->m_team); + } + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; +#endif + case bcSTOP_PROCESS: { + int now; + if (iobuffer_read_u32(&io, &target)) goto finished; + if (iobuffer_read_u32(&io, &now)) goto finished; + DBREFS((KERN_WARNING "bcSTOP_PROCESS of %d\n", target)); + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_node_t *node = binder_proc_Descriptor2Node(that->m_team, target,that,WEAK); + if (node != NULL) { + binder_proc_t* proc = binder_node_AcquireHome(node, that); + if (proc != NULL) { + binder_proc_Stop(proc, now ? TRUE : FALSE); + BND_RELEASE(binder_proc, proc, STRONG, that); + } + BND_RELEASE(binder_node, node, WEAK,that); + } + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcSTOP_SELF: { + DPRINTF(5, (KERN_WARNING "bcSTOP_SELF\n")); + int now; + if (iobuffer_read_u32(&io, &now)) goto finished; + if (BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) { + binder_proc_Stop(that->m_team, now ? TRUE : FALSE); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcREQUEST_DEATH_NOTIFICATION: { + void *cookie; + binder_node_t *node; + if (iobuffer_read_u32(&io, &target)) goto finished; + if (iobuffer_read_void(&io, &cookie)) goto finished; + DPRINTF(5, (KERN_WARNING "bcREQUEST_DEATH_NOTIFICATION of %d w/cookie %p\n", target, cookie)); + node = binder_proc_Descriptor2Node(that->m_team, target, that, WEAK); + if(node != NULL) { + binder_proc_t* proc = binder_node_AcquireHome(node, node); + if (proc != NULL) { + binder_proc_RequestDeathNotification(proc, that->m_team, cookie); + BND_RELEASE(binder_proc, proc, STRONG, node); + } + BND_RELEASE(binder_node, node, WEAK, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcCLEAR_DEATH_NOTIFICATION: { + void *cookie; + binder_node_t *node; + if (iobuffer_read_u32(&io, &target)) goto finished; + if (iobuffer_read_void(&io, &cookie)) goto finished; + DPRINTF(5, (KERN_WARNING "bcCLEAR_DEATH_NOTIFICATION of %d w/cookie %p\n", target, cookie)); + node = binder_proc_Descriptor2Node(that->m_team, target, that, WEAK); + if(node != NULL) { + binder_proc_t* proc = binder_node_AcquireHome(node, node); + if (proc != NULL) { + binder_proc_ClearDeathNotification(proc, that->m_team, cookie); + BND_RELEASE(binder_proc, proc, STRONG, node); + } + BND_RELEASE(binder_node, node, WEAK, that); + } + iobuffer_mark_consumed(&io); + } break; + case bcDEAD_BINDER_DONE: { + void *cookie; + if (iobuffer_read_void(&io, &cookie)) goto finished; + DPRINTF(5, (KERN_WARNING "bcDEAD_BINDER_DONE of cookie %p\n", cookie)); + binder_proc_DeadBinderDone(that->m_team, cookie); + iobuffer_mark_consumed(&io); + } break; + default: { + DPRINTF(5, (KERN_WARNING "Bad command %d on binder write().\n", cmd)); + } break; + } + } + +finished: + DPRINTF(5, (KERN_WARNING "binder_thread_Write() finished\n")); + *consumed = iobuffer_consumed(&io); + return 0; +} + +status_t +binder_thread_ReturnTransaction(binder_thread_t *that, iobuffer_t *io, binder_transaction_t *t) +{ + bool acquired; + bool freeImmediately; + binder_transaction_data_t tr; + DPRINTF(0, (KERN_WARNING "%s(%p:%d, %p, %p)\n", __func__, that->m_team, that->m_thid, io, t)); + if (iobuffer_remaining(io) < 18) return -ENOBUFS; + + acquired = BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that); + if (acquired) binder_transaction_ConvertFromNodes(t, that->m_team); + + freeImmediately = FALSE; + + if (binder_transaction_RefFlags(t)) { + DPRINTF(5, (KERN_WARNING " -- binder_transaction_RefFlags()\n")); + switch (binder_transaction_RefFlags(t)) { + case tfAttemptAcquire: { + DPRINTF(5, (KERN_WARNING " --- tfAttemptAcquire\n")); + iobuffer_write_u32(io, brATTEMPT_ACQUIRE); + iobuffer_write_u32(io, binder_transaction_Priority(t)); + } break; + case tfRelease: + DPRINTF(5, (KERN_WARNING " --- tfRelease\n")); + iobuffer_write_u32(io, brRELEASE); + break; + case tfDecRefs: + DPRINTF(5, (KERN_WARNING " --- tfDecRefs\n")); + iobuffer_write_u32(io, brDECREFS); + break; + } + DPRINTF(5, (KERN_WARNING " --- writing data pointer %p\n", t->data_ptr)); + // iobuffer_write_void(io, *((void**)binder_transaction_Data(t))); + iobuffer_write_void(io, t->data_ptr); // binder object token + iobuffer_write_void(io, t->offsets_ptr); // binder object cookie + freeImmediately = binder_transaction_RefFlags(t) != tfAttemptAcquire; + // Take reference on team, so it won't go away until this transaction + // is processed. + if (binder_transaction_TakeTeam(t, that->m_team)) { + BND_LOCK(that->m_lock); + that->m_teamRefs++; + BND_UNLOCK(that->m_lock); + } + } else if (binder_transaction_IsAcquireReply(t)) { + DPRINTF(5, (KERN_WARNING " -- binder_transaction_IsAcquireReply()\n")); + iobuffer_write_u32(io, brACQUIRE_RESULT); + // iobuffer_write_u32(io, *((int*)binder_transaction_Data(t))); + iobuffer_write_u32(io, *(u32*)t->data); + freeImmediately = TRUE; + } else if (binder_transaction_IsDeadReply(t)) { + DPRINTF(5, (KERN_WARNING " -- binder_transaction_IsDeadReply()\n")); + if (that->pendingChild) binder_thread_ReleasePendingChild(that); + iobuffer_write_u32(io, brDEAD_REPLY); + freeImmediately = TRUE; + } else if (binder_transaction_IsFailedReply(t)) { + DPRINTF(5, (KERN_WARNING " -- binder_transaction_IsFailedReply()\n")); + if (that->pendingChild) binder_thread_ReleasePendingChild(that); + iobuffer_write_u32(io, brFAILED_REPLY); + freeImmediately = TRUE; + } else { + DPRINTF(5, (KERN_WARNING " -- else binder_transaction_IsReply(%p): %s\n", t, binder_transaction_IsReply(t) ? "true" : "false")); + if (that->pendingChild) binder_thread_ReleasePendingChild(that); + tr.flags = binder_transaction_UserFlags(t); + tr.priority = binder_transaction_Priority(t); + if (acquired) { + tr.data_size = binder_transaction_DataSize(t); + tr.offsets_size = binder_transaction_OffsetsSize(t); + tr.data.ptr.buffer = binder_transaction_UserData(t); + tr.data.ptr.offsets = binder_transaction_UserOffsets(t); + } else { + tr.data_size = 0; + tr.offsets_size = 0; + tr.data.ptr.buffer = NULL; + tr.data.ptr.offsets = NULL; + } + + DPRINTF(4, ("Response %s %p: size=%p, data=%p, first=%p\n", + !binder_transaction_IsReply(t) == bcTRANSACTION ? "transaction" : "reply", t, + tr.data_size, tr.data.ptr.buffer, + tr.data_size > 0 ? (*(u32*)binder_transaction_Data(t)) : 0)); + + DPRINTF(5, (KERN_WARNING "%s(%p:%d, %p, %p) tr-data %p %d tr-offsets %p %d\n", __func__, that->m_team, that->m_thid, io, t, tr.data.ptr.buffer, tr.data_size, tr.data.ptr.offsets, tr.offsets_size)); + + if (binder_transaction_IsReply(t)) { + tr.target.ptr = NULL; + tr.code = 0; + iobuffer_write_u32(io, brREPLY); + } else { + if (t->target) { + tr.target.ptr = binder_node_Ptr(t->target); + tr.cookie = binder_node_Cookie(t->target); + } else { + tr.target.ptr = NULL; + tr.cookie = NULL; + } + tr.code = binder_transaction_Code(t); + iobuffer_write_u32(io, brTRANSACTION); + } + iobuffer_write_raw(io, &tr, sizeof(tr)); + } + + if (freeImmediately) { + DPRINTF(0, (KERN_WARNING "binder_thread_ReturnTransaction() delete %p\n",t)); + binder_transaction_Destroy(t); + } else { + t->receiver = that; + BND_ACQUIRE(binder_thread, that, WEAK, t); + if (t->sender) { + /* A synchronous transaction blocks this thread until + the receiver completes. */ + DPRINTF(0, (KERN_WARNING "binder_thread %p:%d (%d): enqueueing transaction %p, pending reply %p\n", that->m_team, that->m_thid, that->virtualThid, t, that->m_pendingReply)); + BND_ASSERT(!binder_transaction_IsFreePending(t), "transaction with free pending!"); + if (that->virtualThid) { + if (t->sender->virtualThid) { + BND_ASSERT(t->sender->virtualThid == that->virtualThid, "Bad virtualThid from sender!"); + } else { + BND_ASSERT(t->sender->m_thid == that->virtualThid, "My virtualThid is different than sender thid!"); + } + } + DPRINTF(5, (KERN_WARNING "t->sender->virtualThid: %d, that->virtualThid: %d\n", t->sender->virtualThid, that->virtualThid)); + if (t->sender->virtualThid) { + BND_ASSERT(that->virtualThid == 0 || that->virtualThid == t->sender->virtualThid, "virtualThid not cleared!"); + that->virtualThid = t->sender->virtualThid; + DPRINTF(0, (KERN_WARNING "Continuing virtualThid: %d\n", that->virtualThid)); + } else { + BND_ASSERT(that->virtualThid == 0 || that->virtualThid == t->sender->m_thid, "virtualThid not cleared!"); + that->virtualThid = t->sender->m_thid; + DPRINTF(0, (KERN_WARNING "Starting new virtualThid: %d\n", that->virtualThid)); + } + BND_LOCK(that->m_lock); + DPRINTF(5, (KERN_WARNING "%p:%d(%d) new reply: %p, pending reply: %p\n", that->m_team, that->m_thid, that->virtualThid, t, that->m_pendingReply)); + t->next = that->m_pendingReply; + that->m_pendingReply = t; + BND_UNLOCK(that->m_lock); + } else { + /* A reply transaction just waits until the receiver is done with + its data. */ + DPRINTF(0, (KERN_WARNING "binder_thread: return reply transaction %p\n", t)); + binder_proc_AddToNeedFreeList(that->m_team, t); + } + } + + iobuffer_mark_consumed(io); + + if (acquired) BND_RELEASE(binder_proc, that->m_team, STRONG, that); + + return 0; +} + +status_t +binder_thread_WaitForReply(binder_thread_t *that, iobuffer_t *io) +{ + status_t err; + binder_transaction_t *t = NULL; + if (iobuffer_remaining(io) < 18) return -ENOBUFS; + + if (!BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)) return -ECONNREFUSED; + + if (that->m_isLooping) binder_proc_TakeMeOffYourList(that->m_team); + + // FIXME: implement reply timeouts? + err = wait_event_interruptible(that->m_wait, atomic_read(&that->m_wake_count) > 0); + if(err == 0) + atomic_dec(&that->m_wake_count); + DPRINTF(0, (KERN_WARNING "%p:%d down_interruptible() returned %08x\n", that->m_team, that->m_thid, err)); + + //DBTRANSACT((KERN_WARNING "*** Thread %d received direct %p! wait=%d, isAnyReply=%d\n", current->pid, that->m_reply, that->m_waitForReply, binder_transaction_IsAnyReply(that->m_reply))); + + /* FFB: why don't we check the err here, geh/hackbod? */ + if (that->m_isLooping) binder_proc_PutMeBackInTheGameCoach(that->m_team); + + BND_LOCK(that->m_lock); + if ((t = that->m_reply)) { + status_t result; + /* If this is a reply, handle it. When the binder_proc_t supplies + a reflection, it will take care of adjusting our thread + priority at that point. The user-space looper is responsible + for restoring its priority when done handling the reflect. */ + if (binder_transaction_IsAnyReply(t)) that->m_waitForReply--; + that->m_reply = t->next; + BND_UNLOCK(that->m_lock); + result = binder_thread_ReturnTransaction(that, io, t); + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + return result; + } + BND_UNLOCK(that->m_lock); + + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + // We can get here if we need to spawn a looper. + // BND_VALIDATE(err != 0, "Binder replySem released without reply available", return -EINVAL); + return err; +} + +status_t +binder_thread_WaitForRequest(binder_thread_t *that, iobuffer_t *io) +{ + binder_transaction_t *t = NULL; + status_t err; + if (iobuffer_remaining(io) < 18) return -ENOBUFS; + + err = binder_proc_WaitForRequest(that->m_team, that, &t); + if (err == 0 && t != NULL) { + // ASSERT(t); + err = binder_thread_ReturnTransaction(that, io, t); + } + + return err; +} + +static status_t +binder_thread_WaitForParent(binder_thread_t *that) +{ + binder_thread_t *targetThread; + struct task_struct *parentTask; + pid_t childPid; + bigtime_t wakeupTime; + status_t err; + + DPRINTF(5, (KERN_WARNING "%s: on thread %p\n", __func__, that)); + + // We want to support wrappers, where the real child process + // being run may have some additional processes (such as xterms, + // gdb sessions, etc) between it and the parent that started it. + // In that case, the parent won't be talking directly with our + // thread structure but instead with its immediate child, so we + // need to go up and find it. + + targetThread = that; + if (that->m_pendingReply == NULL) { + DPRINTF(5, (KERN_WARNING "%s: PID %d: finding parent who forked us.\n", __func__, that->m_thid)); + // Parent hasn't set this thread up for a reply... figure out + // what is going on. + targetThread = NULL; + parentTask = current; + do { + childPid = parentTask->pid; + parentTask = parentTask->parent; + if (!parentTask) break; + targetThread = check_for_thread(parentTask->pid, FALSE); + DPRINTF(5, (KERN_WARNING "%s: Up to parent PID %d: targetThread=%p\n", __func__, parentTask->pid, targetThread)); + } while (targetThread == NULL); + + // If we found a thread structure, and it is not set up to + // send a root reply, then we hit the parent and it has not + // yet stopped to wait for the reply. So we'll go ahead and + // and create the child thread structure so we can block on + // it until the parent gets it set up. + DPRINTF(5, (KERN_WARNING "%s: Finished search: targetThread=%p, childPid=%d\n", __func__, targetThread, childPid)); + if (targetThread && !targetThread->m_pendingReplyIsRoot) { + targetThread = check_for_thread(childPid, TRUE); + DPRINTF(5, (KERN_WARNING "%s: Created wrapper process thread structure: %p\n", __func__, targetThread)); + } + } + + if (targetThread == NULL) { + printk(KERN_WARNING "%s: Binder: PID %d attempting to send root reply without waiting parent\n", __func__, that->m_thid); + return -EINVAL; + } + + // Now wait for the parent to be blocked waiting for a reply. + // Hard-coded to give the parent 10 seconds to get around to us. + wakeupTime = 10*HZ; + do_div(wakeupTime, TICK_NSEC); + wakeupTime += get_jiffies_64(); + DPRINTF(0, (KERN_WARNING "%s: Process %d is about to snooze on thread %p (%d)\n", __func__, current->pid, targetThread, targetThread->m_thid)); + err = binder_thread_Snooze(targetThread, wakeupTime); + + // Just one more thing to deal with -- if there is a wrapper process, + // then it is the wrapper that has been set up to reply. We need to + // move that state to our own process because we are the one doing + // the reply. + if (targetThread != that) { + binder_transaction_t* reply; + BND_LOCK(targetThread->m_lock); + DPRINTF(1, (KERN_WARNING "%s: Wrapper has pendingReply=%p, isRoot=%d\n", __func__, targetThread->m_pendingReply, targetThread->m_pendingReplyIsRoot)); + reply = targetThread->m_pendingReply; + if (reply) { + targetThread->m_pendingReply = reply->next; + targetThread->m_pendingReplyIsRoot = FALSE; + } + BND_UNLOCK(targetThread->m_lock); + + if (reply) { + BND_LOCK(that->m_lock); + reply->next = that->m_pendingReply; + that->m_pendingReply = reply; + that->m_pendingReplyIsRoot = TRUE; + BND_UNLOCK(that->m_lock); + } + + // The retrieval of the wrapper thread structure caused us + // to take a reference on it. Now release the reference, + // removing the structure from our thread list if appropriate. + forget_thread(targetThread); + } + + if (err != 0 && that->m_pendingReply) { + /* If an error occurred but the pendingReply has + also been given, then our semaphore has also been + released. We don't want to get out of sync. */ + DPRINTF(5, (KERN_WARNING "Thread %d: Re-acquire IO sem!\n", binder_thread_Thid(that))); + // Note: targetThread -is- the correct one to use here, that + // is the one we blocked on. + binder_thread_AcquireIOSem(targetThread); + } + + DPRINTF(0, (KERN_WARNING "%s: Returning: pendingReply=%p, err=%d\n", __func__, that->m_pendingReply, err)); + return that->m_pendingReply ? 0 : err; +} + +int +binder_thread_Read(binder_thread_t *that, void *buffer, int size, signed long *consumed) +{ + int origRemain; + status_t err = 0; + bool isRoot; + bool isInline; + /* ditch these next two lines under linux, if we can */ + pid_t me = current->pid; + + binder_transaction_t *t,*replyTo; + iobuffer_t io; + bool acquired = FALSE; + + if (me != that->m_thid) return -EINVAL; + + DPRINTF(0, (KERN_WARNING "binder_thread_Read: %08lx (%p:%d)\n", (unsigned long)that, that->m_team, that->m_thid)); + iobuffer_init(&io, (unsigned long)buffer, size, *consumed); + + /* + * Write brNOOP, but don't mark it consumed. We'll replace the brNOOP with + * a brSPAWN_LOOPER if we need to spawn a thread. + * Only do this once, in case the system call gets restarted for some reason. + */ + if (*consumed == 0) iobuffer_write_u32(&io, brNOOP); + + /* Read as much data as possible, until we either have to block + or have filled the buffer. */ + + while (iobuffer_remaining(&io) > 8) { + if (!binder_proc_IsAlive(that->m_team)) { + /* If the team is dead, write a command to say so and exit + right now. Do not pass go, do not collect $200. */ + DPRINTF(0, (KERN_WARNING " binder_proc_IsAlive(%08x): false\n", (unsigned int)that->m_team)); + iobuffer_write_u32(&io, brFINISHED); + iobuffer_mark_consumed(&io); + err = -ECONNREFUSED; + goto finished; + } else if (that->m_shortAttemptAcquire) { + /* Return the result of a short-circuited attempt acquire. */ + DPRINTF(0, (KERN_WARNING "Thread %d already has reply!\n", that->m_thid)); + that->m_shortAttemptAcquire = FALSE; + iobuffer_write_u32(&io, brACQUIRE_RESULT); + iobuffer_write_u32(&io, that->m_resultAttemptAcquire); + iobuffer_mark_consumed(&io); + continue; + } else if (that->m_failedRootReceive) { + // XXX Would be nice to return a little more informative + // error message. + that->m_failedRootReceive = FALSE; + iobuffer_write_u32(&io, brDEAD_REPLY); + goto finished; + } + + /* Look for a queued transaction. */ + BND_LOCK(that->m_lock); + if ((t=that->m_pendingRefResolution) != NULL) { + if (iobuffer_consumed(&io) > 0 && (binder_transaction_MaxIOToNodes(t)+4) > iobuffer_remaining(&io)) { + /* If there is already data in the buffer, and may not be enough + room for what this transaction could generate, then stop now. */ + DPRINTF(0, (KERN_WARNING "Aborting ConvertToNodes: consumed=%d, max=%d, remain=%d\n", iobuffer_consumed(&io), binder_transaction_MaxIOToNodes(t)+4, iobuffer_remaining(&io))); + BND_UNLOCK(that->m_lock); + goto finished; + } + that->m_pendingRefResolution = t->next; + } + BND_UNLOCK(that->m_lock); + + /* If a transaction was found, twiddle it and send it off. */ + if (t != NULL && (acquired || (acquired=BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that)))) { + + DPRINTF(5, (KERN_WARNING "Thread %d has pending transaction %p\n", that->m_thid, t)); + + isRoot = (binder_transaction_IsRootObject(t)); + + /* Perform node conversion if not already done. */ + if (!binder_transaction_IsReferenced(t)) { + binder_proc_t *proc = NULL; + int acquiredProc = 0; + + DBREAD((KERN_WARNING "Thread %d performing ref resolution!\n", that->m_thid)); + origRemain = iobuffer_remaining(&io); + err = 0; + if (isRoot) { + // If we are replying with the root object, we first need to block + // until our parent has set us up to have somewhere to reply to. + err = binder_thread_WaitForParent(that); + + BND_LOCK(that->m_lock); + that->m_failedRootReply = that->m_pendingReply == NULL; + if (that->m_failedRootReply) err = -EINVAL; + BND_UNLOCK(that->m_lock); + } + /* + * The moment of truth. In order to convert nodes, we have to + * copy the data. In order to copy the data, we need to know + * the recipient of the transaction. If the transaction has a + * target, the target's team becomes the recipient. If the + * transaction carries a reply, use the pending reply's sending + * team. + */ + if (err == 0) { + if (t->target) { + proc = binder_node_AcquireHome(t->target, that); + acquiredProc = proc != NULL; + } else { + proc = that->m_pendingReply ? binder_thread_Team(that->m_pendingReply->sender) : NULL; + } + err = proc ? 0 : -EINVAL; + } + if (!proc) { + DPRINTF(0, (KERN_WARNING "*#*#*# NO TARGET PROCESS FOR binder_transaction_CopyTransactionData #*#*#*\n")); + DPRINTF(0, (KERN_WARNING "t->target: %p, that->m_pendingReply: %p, m_pendingReply->sender: %p\n", t->target, that->m_pendingReply, that->m_pendingReply ? that->m_pendingReply->sender : NULL)); + } + if (err == 0) + err = binder_transaction_CopyTransactionData(t, proc); + if (err == 0) + err = binder_transaction_ConvertToNodes(t, that->m_team, &io); + /* If we got some error, report error to the caller so they don't wait forever. */ + if (err < 0 && !binder_transaction_IsReply(t)) { + if(proc && binder_proc_IsAlive(proc)) + iobuffer_write_u32(&io, brFAILED_REPLY); + else + iobuffer_write_u32(&io, brDEAD_REPLY); + } + iobuffer_mark_consumed(&io); + + if (acquiredProc) { + BND_RELEASE(binder_proc, proc, STRONG, that); + } + + if (err < 0 || iobuffer_remaining(&io) < 4) { + /* XXX Fail if we run out of room. Do we need to deal with this + better. (It's only a problem if the caller is trying to read in + to a buffer that isn't big enough, in total, for a returned + transaction. */ + DPRINTF(0, (KERN_WARNING "Aborting transaction: err: %08x (or not enough room to return last command)\n", err)); + err = 0; + if(!binder_transaction_IsReply(t)) { + binder_transaction_Destroy(t); + goto finished; + } + binder_transaction_SetFailedReply(t, TRUE); + } + + /* If we aren't sending anything back to the caller, we can + deliver this transaction right away. Otherwise, we must + wait for the caller to process the returned data. This + is due to a race condition between the receiver releasing + its references and the caller acquiring any new references + returned by the driver. */ + if (origRemain != iobuffer_remaining(&io)) { + DBREAD((KERN_WARNING "Transaction acquired references! Keeping.\n")); + BND_LOCK(that->m_lock); + t->next = that->m_pendingRefResolution; + that->m_pendingRefResolution = t; + BND_UNLOCK(that->m_lock); + t = NULL; + } + } +#if 0 + // FFB's broken debug code + else { + DPRINTF(0, (KERN_WARNING "binder_transaction_IsReferenced(%p) true -- sender: %d (vthid: %d)\n", t, t->sender->m_thid, t->sender->virtualThid)); + } +#endif + + /* Send this transaction off to its target. */ + if (t != NULL) { + DBREAD((KERN_WARNING "Thread %d delivering transaction!\n", that->m_thid)); + isInline = binder_transaction_IsInline(t); + if (binder_transaction_IsAnyReply(t)) { + BND_LOCK(that->m_lock); + + replyTo = that->m_pendingReply; + if (replyTo) { + that->m_pendingReply = replyTo->next; + if (!that->m_pendingReply) { + that->virtualThid = 0; + DPRINTF(5, (KERN_WARNING "virtualThid reset to 0, m_waitForReply: %d\n", that->m_waitForReply)); + } else { + DPRINTF(5, (KERN_WARNING "virtualThid: %d, m_pendingReply: %p, m_waitForReply: %d\n", that->virtualThid, that->m_pendingReply, that->m_waitForReply)); + } + BND_UNLOCK(that->m_lock); + + /* If this is a successful bcATTEMPT_ACQUIRE, then take + care of reference counts now. + */ + if (binder_transaction_IsAcquireReply(t) && (*(int*)t->data != 0)) { + binder_proc_ForceRefNode(binder_thread_Team(replyTo->sender), replyTo->target, &io); + } + + if (binder_transaction_IsRootObject(replyTo)) { + BND_ASSERT(binder_transaction_IsRootObject(t), "EXPECTING ROOT REPLY!"); + } else if (binder_transaction_RefFlags(replyTo)&tfAttemptAcquire) { + BND_ASSERT(binder_transaction_IsAcquireReply(t), "EXPECTING ACQUIRE REPLY!"); + } else { + BND_ASSERT(!binder_transaction_IsRootObject(t) && !binder_transaction_IsAcquireReply(t), "EXPECTING REGULAR REPLY!"); + } + + DBTRANSACT((KERN_WARNING "*** Thread %d is replying to %p with %p! wait=%d\n", + that->m_thid, replyTo, t, that->m_waitForReply)); + binder_thread_Reply(replyTo->sender, t); + if (binder_transaction_IsInline(replyTo) || binder_transaction_IsRootObject(replyTo)) { + binder_transaction_Destroy(replyTo); + } else { + DPRINTF(0, (KERN_WARNING "binder_thread: finish reply request %p\n", replyTo)); + if (binder_transaction_IsFreePending(replyTo)) { + binder_transaction_Destroy(replyTo); + } else { + binder_proc_AddToNeedFreeList(that->m_team, replyTo); + } + } + } else { + BND_UNLOCK(that->m_lock); + DPRINTF(1, (KERN_WARNING "********** Nowhere for reply to go!!!!!!!!!!!\n")); +#if 0 + BND_ASSERT(binder_transaction_IsRootObject(t) || !binder_proc_IsAlive(that->m_team), "Unexpected reply!"); + if (binder_transaction_IsRootObject(t)) binder_proc_CaptureRootObject(t); + else { + binder_transaction_Destroy(t); + } +#endif + } + } else { + t->sender = that; + BND_ACQUIRE(binder_thread, that, WEAK, t); + that->m_waitForReply++; + DPRINTF(2, (KERN_WARNING "*** Thread %d going to wait for reply to %p! now wait=%d\n", that->m_thid, t, that->m_waitForReply)); + if (t->target) binder_node_Send(t->target, t); + else { + binder_thread_ReplyDead(that); + binder_transaction_Destroy(t); + } + } + if (!isInline) iobuffer_write_u32(&io, brTRANSACTION_COMPLETE); + iobuffer_mark_consumed(&io); + } + + /* Got a transaction but team is going away. Toss it. */ + } else if (t != NULL) { + DPRINTF(0, (KERN_WARNING "Transaction sent to dying team, thread %d.\n", that->m_thid)); + binder_transaction_DestroyNoRefs(t); + + /* If there is data available, return it now instead of + waiting for the next transaction. */ + } else if (iobuffer_consumed(&io) > 0) { + DPRINTF(2, (KERN_WARNING "Thread %d has %d bytes of data to return, won't wait for transaction.\n", that->m_thid, iobuffer_consumed(&io))); + goto finished; + + /* No transaction, but maybe we are waiting for a reply back? */ + } else if (that->m_waitForReply) { + DPRINTF(2, (KERN_WARNING "Thread %d waiting for reply!\n", that->m_thid)); + if ((sizeof(binder_transaction_data_t)+8) > iobuffer_remaining(&io)) { + /* If there isn't enough room in the buffer to return a transaction, + then stop now. */ + DPRINTF(0, (KERN_WARNING "Aborting read: Not enough room to return reply\n")); + goto finished; + } + err = binder_thread_WaitForReply(that, &io); + if (err == -ENOBUFS) err = 0; + goto finished; + + /* We're all out. Just wait for something else to do. */ + } else { + DPRINTF(2, (KERN_WARNING "Thread %d waiting for request, vthid: %d!\n", that->m_thid, that->virtualThid)); + BND_ASSERT(that->virtualThid == 0, "Waiting for transaction with vthid != 0"); + BND_ASSERT(that->m_pendingReply == NULL, "Waiting for transaction with pending reply"); + + if (that->m_teamRefs > 0) { + int relCount; + BND_LOCK(that->m_lock); + relCount = that->m_teamRefs; + that->m_teamRefs = 0; + BND_UNLOCK(that->m_lock); + DPRINTF(3, (KERN_WARNING "Unlocking proc %08x %d times\n", (unsigned int)that->m_team, relCount)); + + while (relCount) { + BND_RELEASE(binder_proc, that->m_team, STRONG, that); + relCount--; + } + } + + err = binder_thread_WaitForRequest(that, &io); + if (err == -ERESTARTSYS) { + goto finished; + } else if (err == -EINTR) { + goto finished; + } else if (err == -ECONNREFUSED) { + goto finished; + } else if (err == -ENOBUFS) { + err = 0; + goto finished; + } else if (err == REQUEST_EVENT_READY) { + iobuffer_write_u32(&io, brEVENT_OCCURRED); + iobuffer_write_u32(&io, that->returnedEventPriority); + iobuffer_mark_consumed(&io); + err = 0; + } else if (err == DEATH_NOTIFICATION_READY) { + binder_proc_GetPendingDeathNotifications(that->m_team, that, &io); + iobuffer_mark_consumed(&io); + err = 0; + } else if (err == -ETIMEDOUT) { + if (that->m_isLooping) { + if ((acquired=BND_ATTEMPT_ACQUIRE(binder_proc, that->m_team, STRONG, that))) + binder_proc_FinishLooper(that->m_team, that->m_isSpawned); + that->m_isLooping = FALSE; + } + if (that->m_isSpawned) iobuffer_write_u32(&io, brFINISHED); + else iobuffer_write_u32(&io, brOK); + iobuffer_mark_consumed(&io); + err = 0; + } + /* + else if (err == B_BAD_SEM_ID) { + iobuffer_write_u32(&io, brFINISHED); + iobuffer_mark_consumed(&io); + } + */ + else if (err < 0) { + iobuffer_write_u32(&io, brERROR); + iobuffer_write_u32(&io, err); + iobuffer_mark_consumed(&io); + err = 0; + goto finished; + } + } + } + +finished: + if (acquired) BND_RELEASE(binder_proc, that->m_team, STRONG, that); + + // Return number of bytes available, or the last error code + // if there are none. (This is so we can return -EINTR.) + *consumed = iobuffer_consumed(&io); + + if (err != -ERESTARTSYS) { + if (test_and_clear_bit(DO_SPAWN_BIT, &that->m_team->m_noop_spawner)) { + DBSPAWN((KERN_WARNING "Asking %p:%d to brSPAWN_LOOPER\n", that->m_team, that->m_thid)); + // make the brNOOP into a brSPAWN_LOOPER + // *(u32*)buffer = brSPAWN_LOOPER; + // We call the unchecked __put_user() here because the constructor + // for iobuffer already called access_ok(). + __put_user(brSPAWN_LOOPER, (u32*)buffer); + if (iobuffer_consumed(&io) < sizeof(u32)) { + iobuffer_mark_consumed(&io); + *consumed = iobuffer_consumed(&io); + } + } + } + return err; +} + +status_t +binder_thread_Snooze(binder_thread_t *that, bigtime_t timeout) +{ + status_t res = 0; + + DPRINTF(1, (KERN_WARNING "binder_thread_Snooze(%d, %lld)\n", that->m_thid, timeout)); + /* + * I don't know if I got the semantics correct for this. + status_t res = acquire_sem_etc(that->m_ioSem,1,B_CAN_INTERRUPT|B_ABSOLUTE_TIMEOUT,timeout); + */ + + if(signal_pending(current)) { + DPRINTF(1, (KERN_WARNING "binder_thread_Snooze(%d, %lld) signal pending -- ABORT\n", that->m_thid, timeout)); + return -ERESTARTSYS; + } + + timeout -= get_jiffies_64(); + DPRINTF(1, (KERN_WARNING "binder_thread_Snooze(%d, relative %lld)\n", that->m_thid, timeout)); + if (timeout > 0) { +#if 1 + bigtime_t check = timeout; + do_div(check, HZ); + if (check > 10) { + DPRINTF(0, (KERN_WARNING "%s: timeout exceeds 10 seconds at %Ld sec\n", __func__, check)); + return -ETIMEDOUT; + } +#endif + DPRINTF(5, (KERN_WARNING "%s: m_wake_count: %d\n", __func__, atomic_read(&that->m_wake_count))); + res = wait_event_interruptible_timeout(that->m_wait, atomic_read(&that->m_wake_count) > 0, timeout); + if(res > 0) + atomic_dec(&that->m_wake_count); + } + else { + /* Makes system lock up due to busy wait + * bug temporary + * when not using unlocked ioctl + */ + static unsigned int last_yield = 0; + unsigned int now = jiffies; + if ((now - last_yield) > 5*HZ) { + last_yield = now; + //printk(KERN_WARNING "binder_thread_Snooze(%d, %lld) yield wakeup_time thread %lld, team %lld, this %p, team->waitStack %p, team->state %x\n", + // that->m_thid, timeout, that->wakeupTime, that->m_team->m_wakeupTime, that, that->m_team->m_waitStack, that->m_team->m_state); + yield(); + } + } + + //ddprintf("Result of snooze in thread %ld: 0x%08lx\n", that->m_thid, res); + if (res == 0) // timed out + res = -ETIMEDOUT; + else if (res > 0) // acquired, reports time remaining + res = 0; + return res; +} + +status_t +binder_thread_AcquireIOSem(binder_thread_t *that) +{ + int err; + DPRINTF(0, (KERN_WARNING "binder_thread_AcquireIOSem(%d)\n", that->m_thid)); + // while (acquire_sem_etc(that->m_ioSem,1,B_TIMEOUT,0) == -EINTR) ; + //wait_event(that->m_wait, that->m_wake_count > 0); + err = wait_event_interruptible(that->m_wait, atomic_read(&that->m_wake_count) > 0); // this should probably not be interruptible, but it allows us to kill the thread + if(err == 0) + atomic_dec(&that->m_wake_count); + return err; +} + +void +binder_thread_Wakeup(binder_thread_t *that) +{ + DIPRINTF(0, (KERN_WARNING "binder_thread_Wakeup(%d)\n", that->m_thid)); + // We use B_DO_NOT_RESCHEDULE here because Wakeup() is usually called + // while the binder_proc_t is locked. If the thread is a real-time + // priority, waking up here will cause pinging between this thread + // and its caller. (We wake up, block on the binder_proc_t, the caller + // continues and unlocks, then we continue.) + // release_sem_etc(that->m_ioSem, 1, B_DO_NOT_RESCHEDULE); + // FIXME: this may not have the do-not-reschedule semantics we want (wake_up_interruptible_sync may work for this) + atomic_add(1, &that->m_wake_count); + wake_up(&that->m_wait); + //wake_up_interruptible_sync(&that->m_wait); +} + +void +binder_thread_Reply(binder_thread_t *that, binder_transaction_t *t) +{ + DBTRANSACT((KERN_WARNING "*** Thread %d (vthid %d) sending to %d (vthid %d)! wait=%d, isReply=%d, isAcquireReply=%d\n", + current->pid, t->sender ? t->sender->virtualThid : -1, + that->m_thid, that->virtualThid, that->m_waitForReply, binder_transaction_IsReply(t), binder_transaction_IsAcquireReply(t))); + BND_LOCK(that->m_lock); + if (that->m_team && binder_proc_IsAlive(that->m_team)) { + // BND_VALIDATE(that->m_reply == NULL, "Already have reply!", ddprintf("Current reply: %p, new reply: %p\n", that->m_reply, t)); + BND_ASSERT(that->m_waitForReply > 0, "Not waiting for a reply!"); + t->next = that->m_reply; + that->m_reply = t; + } else { + BND_ASSERT(t != NULL, "binder_thread_Reply() called with NULL transaction!"); + if (t) binder_transaction_Destroy(t); + } + BND_UNLOCK(that->m_lock); + atomic_add(1, &that->m_wake_count); + wake_up(&that->m_wait); +} + +void +binder_thread_ReplyDead(binder_thread_t *that) +{ + binder_transaction_t* t = binder_transaction_CreateEmpty(); + binder_transaction_SetDeadReply(t, TRUE); + binder_thread_Reply(that, t); +} + +BND_IMPLEMENT_ACQUIRE_RELEASE(binder_thread); +BND_IMPLEMENT_ATTEMPT_ACQUIRE(binder_thread); + + diff -Nru linux-2.6.23/drivers/binder/binder_thread.h kernel.android/drivers/binder/binder_thread.h --- linux-2.6.23/drivers/binder/binder_thread.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_thread.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,153 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef BINDER_THREAD_H +#define BINDER_THREAD_H + +#include "binder_defs.h" +#include + +typedef struct binder_thread { + /* These are protected by binder.c's global lock. */ + struct hlist_node node; + bool attachedToThread; /* Expecting a BINDER_THREAD_EXIT. */ + + /* These are managed by binder_proc_t. Nothing else should + touch them. */ + struct binder_thread * next; /* List of all threads */ + struct list_head waitStackEntry; + struct binder_thread * pendingChild; /* Child for bcREQUEST_ROOT_OBJECT */ + struct binder_transaction * nextRequest; /* Return request to waiting thread */ + enum { + WAKE_REASON_NONE = 0, + WAKE_REASON_IDLE, + WAKE_REASON_PROCESS_DEATH + } wakeReason; + + /* Stupid hack. */ + int returnedEventPriority; + + pid_t virtualThid; /* The thid for the transaction thread group */ + atomic_t m_primaryRefs; + atomic_t m_secondaryRefs; + status_t m_err; + pid_t m_thid; + wait_queue_head_t m_wait; + atomic_t m_wake_count; + int m_waitForReply; + int m_consume; + + struct semaphore m_lock; + struct binder_proc * m_team; // the team we belong to + struct binder_transaction * m_reply; + struct binder_transaction * m_pendingReply; + struct binder_transaction * m_pendingRefResolution; + + /* This is the number of primary references on our team + that must be removed when we continue looping. It is + used to keep the team around while processing final + brRELEASE and brDECREFS commands on objects inside it. */ + int m_teamRefs; + + /* Did the driver spawn this thread? */ + bool m_isSpawned : 1; + + /* Is this thread running as a looper? */ + bool m_isLooping : 1; + + /* For driver spawned threads: first time looping? */ + bool m_firstLoop : 1; + + /* Set if thread has determined an immediate reply for a + bcATTEMPT_ACQUIRE. In this case, 'short' is true and + 'result' is whether it succeeded. */ + bool m_shortAttemptAcquire : 1; + bool m_resultAttemptAcquire : 1; + + /* Set if this thread structure has been initialized to + reply with a root object to its parent thread. */ + bool m_pendingReplyIsRoot : 1; + + /*! Set if this thread had an error when trying to + receive a child's root reply, to return the result + at the next Read(). */ + bool m_failedRootReceive : 1; + + /* Set if this thread tried to send a root object, but + timed out. */ + bool m_failedRootReply : 1; +} binder_thread_t; + +int binder_thread_GlobalCount(void); + +binder_thread_t * binder_thread_init(pid_t thid, struct binder_proc *team); +void binder_thread_destroy(binder_thread_t *that); + +void binder_thread_Released(binder_thread_t *that); + +void binder_thread_Die(binder_thread_t *that); + +BND_DECLARE_ACQUIRE_RELEASE(binder_thread); +BND_DECLARE_ATTEMPT_ACQUIRE(binder_thread); + +/* Attach parent thread to this thread. The child is set up as if it had + received a transaction, and the first thing it should do is send a reply + that will go back to the parent. This is for bcRETRIEVE_ROOT_OBJECT. */ +bool binder_thread_SetParentThread(binder_thread_t *that, binder_thread_t *replyTo); + +/* Clear the pendingChild field when we have received the reply. */ +void binder_thread_ReleasePendingChild(binder_thread_t *that); + +/* When binder_thread_SetParentThread() is used to wait for the child thread + to send its root object, we can create a binder_thread structure that is + not attached to a binder_proc. This function is called when the child + thread finally gets into the driver, to get its pre-created thread + structure attached to its new process structure. */ +void binder_thread_AttachProcess(binder_thread_t *that, struct binder_proc *team); + +/* Calls from binder_proc_t to block until new requests arrive */ +status_t binder_thread_Snooze(binder_thread_t *that, bigtime_t wakeupTime); +status_t binder_thread_AcquireIOSem(binder_thread_t *that); +void binder_thread_Wakeup(binder_thread_t *that); + +/* Returning transactions -- reflections and the final reply */ +void binder_thread_Reply(binder_thread_t *that, struct binder_transaction *t); +void binder_thread_Reflect(binder_thread_t *that, struct binder_transaction *t); + +/* Reply that the target is no longer with us. */ +void binder_thread_ReplyDead(binder_thread_t *that); + +bool binder_thread_AttemptExecution(binder_thread_t *that, struct binder_transaction *t); +void binder_thread_FinishAsync(binder_thread_t *that, struct binder_transaction *t); +void binder_thread_Sync(binder_thread_t *that); + +#define binder_thread_Thid(that) ((that)->m_thid) +#define binder_thread_Team(that) ((that)->m_team) + +#define binder_thread_VirtualThid(that) ((that)->virtualThid) + +#define binder_thread_PrimaryRefCount(that) atomic_read(&(that)->m_primaryRefs) +#define binder_thread_SecondaryRefCount(that) atomic_read(&(that)->m_secondaryRefs) + +int binder_thread_Control(binder_thread_t *that, unsigned int cmd, void *buffer); +int binder_thread_Write(binder_thread_t *that, void *buffer, int size, signed long *consumed); +int binder_thread_Read(binder_thread_t *that, void *buffer, int size, signed long *consumed); + +#define binder_thread_Reflect(that, t) binder_thread_Reply(that, t) + +#endif // BINDER_THREAD_H diff -Nru linux-2.6.23/drivers/binder/binder_transaction.c kernel.android/drivers/binder/binder_transaction.c --- linux-2.6.23/drivers/binder/binder_transaction.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_transaction.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,541 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "binder_defs.h" +#include "binder_transaction.h" +#include "binder_proc.h" +#include "binder_thread.h" +#include "binder_node.h" +#include +#include +#include + +static int binder_transaction_print_size = 32 * 1024; +static int binder_transaction_fail_size = 16 * 1024 * 1024; +module_param_named(warn_transaction_size, binder_transaction_print_size, int, 0644); +module_param_named(max_transaction_size, binder_transaction_fail_size, int, 0644); + +#define PURGATORY 0 +#if PURGATORY +static DECLARE_MUTEX(sem); +static binder_transaction_t* head = NULL; +static binder_transaction_t** tail = &head; +static int count = 0; + +static void my_free_trans(binder_transaction_t *t) +{ + down(&sem); + *tail = t; + tail = &t->next; + count++; + if (count > 20) { + t = head; + head = head->next; + kmem_cache_free(transaction_cache, t); + count--; + } + up(&sem); +} +#define ALLOC_TRANS kmem_cache_alloc(transaction_cache, GFP_KERNEL) +#define FREE_TRANS(x) my_free_trans(x) +#else +#define ALLOC_TRANS kmem_cache_alloc(transaction_cache, GFP_KERNEL) +#define FREE_TRANS(x) kmem_cache_free(transaction_cache, x) +#endif + +void binder_transaction_dtor(binder_transaction_t *that); + +void binder_transaction_Init(binder_transaction_t *that); +void binder_transaction_debug_dump(binder_transaction_t *that); + +status_t +binder_transaction_ConvertToNodes(binder_transaction_t *that, binder_proc_t *from, iobuffer_t *io) +{ + DPRINTF(4, (KERN_WARNING "%s(%p, %p, %p)\n", __func__, that, from, io)); + if (binder_transaction_RefFlags(that)) return 0; + + if (that->team != from) { + BND_ACQUIRE(binder_proc, from, WEAK, that); + if (that->team) BND_RELEASE(binder_proc, that->team, WEAK, that); + that->team = from; + } + + if (that->offsets_size > 0) { + u8 *ptr = binder_transaction_Data(that); + const size_t *off = binder_transaction_Offsets(that); //(const size_t*)(ptr + INT_ALIGN(that->data_size)); + const size_t *offEnd = off + (that->offsets_size/sizeof(size_t)); + struct flat_binder_object* flat; + + // This function is called before any references have been acquired. + BND_ASSERT((that->flags&tfReferenced) == 0, "ConvertToNodes() already called!"); + that->flags |= tfReferenced; + + BND_FLUSH_CACHE( binder_transaction_UserData(that), + binder_transaction_UserOffsets(that) + + binder_transaction_OffsetsSize(that) ); + + while (off < offEnd) { + bool strong = TRUE; + BND_ASSERT(*off <= (that->data_size-sizeof(struct flat_binder_object)), "!!! ConvertToNodes: type code pointer out of range."); + flat = (struct flat_binder_object*)(ptr + *off++); + switch (flat->type) { + case kPackedLargeBinderHandleType: + DPRINTF(5,(KERN_WARNING "ConvertToNodes B_BINDER_HANDLE_TYPE %ld\n",flat->handle)); + // Retrieve node and acquire reference. + flat->node = binder_proc_Descriptor2Node(from, flat->handle,that, STRONG); + break; + case kPackedLargeBinderType: + DPRINTF(5,(KERN_WARNING "ConvertToNodes B_BINDER_TYPE %p\n",flat->binder)); + // Lookup node and acquire reference. + if (binder_proc_Ptr2Node(from, flat->binder,flat->cookie,&flat->node,io,that, STRONG) != 0) return -EINVAL; + if (binder_transaction_IsRootObject(that)) { + DPRINTF(5,(KERN_WARNING "Making node %p a root node\n", flat->node)); + binder_proc_SetRootObject(from, flat->node); + } + break; + case kPackedLargeBinderWeakHandleType: + DPRINTF(5,(KERN_WARNING "ConvertToNodes B_BINDER_WEAK_HANDLE_TYPE %ld\n",flat->handle)); + // Retrieve node and acquire reference. + flat->node = binder_proc_Descriptor2Node(from, flat->handle,that,WEAK); + strong = FALSE; + break; + case kPackedLargeBinderWeakType: + DPRINTF(5,(KERN_WARNING "ConvertToNodes B_BINDER_WEAK_TYPE %p\n",flat->binder)); + // Lookup node and acquire reference. + if (binder_proc_Ptr2Node(from, flat->binder,flat->cookie,&flat->node,io,that,WEAK) != 0) return -EINVAL; + strong = FALSE; + break; + default: + BND_ASSERT(FALSE, "Bad binder offset given to transaction!"); + DPRINTF(0, (KERN_WARNING "ConvertToNodes: unknown typecode %08lx, off: %p, offEnd: %p\n", flat->type, off, offEnd)); + BND_FLUSH_CACHE(ptr, offEnd); + return -EINVAL; + } + flat->type = strong ? kPackedLargeBinderNodeType : kPackedLargeBinderWeakNodeType; + } + BND_FLUSH_CACHE(ptr, offEnd); + } + + return 0; +} + +status_t +binder_transaction_ConvertFromNodes(binder_transaction_t *that, binder_proc_t *to) +{ + u8 *ptr; + size_t *off; + size_t *offEnd; + DPRINTF(4, (KERN_WARNING "%s(%p, %p)\n", __func__, that, to)); + if (binder_transaction_RefFlags(that)) return 0; + + if (that->team != to) { + BND_ACQUIRE(binder_proc, to, WEAK, that); + if (that->team) BND_RELEASE(binder_proc, that->team, WEAK, that); + that->team = to; + } + + if (that->offsets_size > 0) { + // This function is called after references have been acquired. + BND_ASSERT((that->flags&tfReferenced) != 0, "ConvertToNodes() not called!"); + + ptr = binder_transaction_Data(that); + off = binder_transaction_Offsets(that); //(const size_t*)(ptr + INT_ALIGN(that->data_size)); + offEnd = off + (that->offsets_size/sizeof(size_t)); + struct flat_binder_object* flat; + + BND_FLUSH_CACHE( binder_transaction_UserData(that), + binder_transaction_UserOffsets(that) + + binder_transaction_OffsetsSize(that) ); + while (off < offEnd) { + flat = (struct flat_binder_object*)(ptr + *off++); + binder_node_t *n = flat->node; + if (flat->type == kPackedLargeBinderNodeType) { + if (!n) { + flat->type = kPackedLargeBinderType; + flat->binder = NULL; + flat->cookie = NULL; + } else if (n->m_home == to) { + flat->type = kPackedLargeBinderType; + flat->binder = binder_node_Ptr(n); + flat->cookie = binder_node_Cookie(n); + // Keep a reference on the node so that it doesn't + // go away until this transaction completes. + } else { + flat->type = kPackedLargeBinderHandleType; + flat->handle = binder_proc_Node2Descriptor(to, n, TRUE, STRONG); + flat->cookie = NULL; + // We now have a reference on the node through the + // target team's descriptor, so remove our own ref. + BND_RELEASE(binder_node, n, STRONG, that); + } + } else if (flat->type == kPackedLargeBinderWeakNodeType) { + if (!n) { + flat->type = kPackedLargeBinderWeakType; + flat->binder = NULL; + flat->cookie = NULL; + } else if (n->m_home == to) { + flat->type = kPackedLargeBinderWeakType; + flat->binder = binder_node_Ptr(n); + flat->cookie = binder_node_Cookie(n); + // Keep a reference on the node so that it doesn't + // go away until this transaction completes. + } else { + flat->type = kPackedLargeBinderWeakHandleType; + flat->handle = binder_proc_Node2Descriptor(to, n, TRUE, WEAK); + flat->cookie = NULL; + // We now have a reference on the node through the + // target team's descriptor, so remove our own ref. + BND_RELEASE(binder_node, n, WEAK, that); + } + } else { + BND_ASSERT(FALSE, "Bad binder offset given to transaction!"); + DPRINTF(0, (KERN_WARNING "ConvertToNodes: unknown typecode %08lx, off: %p, offEnd: %p\n", flat->type, off, offEnd)); + BND_FLUSH_CACHE(ptr, offEnd); + return -EINVAL; + } + } + BND_FLUSH_CACHE(ptr, offEnd); + } + + return 0; +} + +void +binder_transaction_ReleaseTarget(binder_transaction_t *that) +{ + DPRINTF(4, (KERN_WARNING "%s(%p)\n", __func__, that)); + if (that->sender) { + DPRINTF(5, (KERN_WARNING "%s(%p) release sender %p\n", __func__, that, that->sender)); + BND_RELEASE(binder_thread, that->sender, WEAK, that); + that->sender = NULL; + } + if (that->receiver) { + DPRINTF(5, (KERN_WARNING "%s(%p) release receiver %p\n", __func__, that, that->receiver)); + BND_RELEASE(binder_thread, that->receiver, WEAK, that); + that->receiver = NULL; + } + + if (that->target) { + DPRINTF(5, (KERN_WARNING "%s(%p) release target %p\n", __func__, that, that->target)); + BND_RELEASE(binder_node, that->target, binder_transaction_RefFlags(that) == tfAttemptAcquire ? WEAK : STRONG,that); + that->target = NULL; + } + DPRINTF(4, (KERN_WARNING "%s(%p) fini\n", __func__, that)); +} + +void +binder_transaction_ReleaseTeam(binder_transaction_t *that) +{ + DPRINTF(4, (KERN_WARNING "%s(%p), team: %p\n", __func__, that, that->team)); + + if (that->team) { + BND_RELEASE(binder_proc, that->team, binder_transaction_RefFlags(that) ? STRONG : WEAK, that); + that->team = NULL; + } +} + +size_t +binder_transaction_MaxIOToNodes(binder_transaction_t *that) +{ + DPRINTF(4, (KERN_WARNING "%s(%p): %d\n", __func__, that, (that->offsets_size/8)*16)); + // Each offsets entry is 4 bytes, and could result in 24 bytes + // being written. (To be more accurate, we could actually look + // at the offsets and only include the ones that are a + // B_BINDER_TYPE or B_BINDER_WEAK_TYPE.) + return (that->offsets_size/4)*24; +} + +binder_proc_t * +binder_transaction_TakeTeam(binder_transaction_t *that, binder_proc_t * me) +{ + binder_proc_t *ret; + DPRINTF(4, (KERN_WARNING "%s(%p, %p)\n", __func__, that, me)); + if (that->team != me || binder_transaction_RefFlags(that)) return NULL; + + ret = that->team; + that->team = NULL; + return ret; +} + +binder_transaction_t* +binder_transaction_CreateRef(u16 refFlags, void *ptr, void *cookie, binder_proc_t *team) +{ + binder_transaction_t* that = ALLOC_TRANS; + DPRINTF(4, (KERN_WARNING "%s(%04x, %p, %p): %p\n", __func__, refFlags, ptr, team, that)); + if (that) { + binder_transaction_Init(that); + BND_ASSERT((refFlags&(~tfRefTransaction)) == 0 && (refFlags&tfRefTransaction) != 0, + "Bad flags to binder_transaction::create_ref()"); + that->flags |= refFlags; + that->data_ptr = ptr; + that->offsets_ptr = cookie; + if (team) { + that->team = team; + BND_ACQUIRE(binder_proc, that->team, STRONG, that); + } + } + return that; +} + +binder_transaction_t* +binder_transaction_Create(u32 _code, size_t _dataSize, const void *_data, size_t _offsetsSize, const void *_offsetsData) +{ + binder_transaction_t* that = ALLOC_TRANS; + DPRINTF(4, (KERN_WARNING "%s(%08x, %u:%p, %u:%p): %p\n", __func__, _code, _dataSize, _data, _offsetsSize, _offsetsData, that)); + if (that) { + binder_transaction_Init(that); + that->code = _code; + BND_ASSERT(_dataSize == 0 || _data != NULL, "Transaction with dataSize > 0, but NULL data!"); + if (_dataSize && _data) { + that->data_size = _dataSize; + that->data_ptr = _data; + BND_ASSERT(_offsetsSize == 0 || _offsetsData != NULL, "Transaction with offsetsSize > 0, but NULL offsets!"); + if (_offsetsSize && _offsetsData) { + that->offsets_size = _offsetsSize; + that->offsets_ptr = _offsetsData; + } + } + } + return that; +} + +binder_transaction_t* binder_transaction_CreateEmpty(void) +{ + binder_transaction_t* that = ALLOC_TRANS; + DPRINTF(4, (KERN_WARNING "%s(void): %p\n", __func__, that)); + if (that) binder_transaction_Init(that); + return that; +} + +void binder_transaction_Destroy(binder_transaction_t *that) +{ + DPRINTF(4, (KERN_WARNING "%s(%p)\n", __func__, that)); + if (that) { + binder_transaction_dtor(that); + } +} + +void binder_transaction_DestroyNoRefs(binder_transaction_t *that) +{ + DPRINTF(4, (KERN_WARNING "%s(%p)\n", __func__, that)); + if (that) { + that->offsets_size = 0; + binder_transaction_dtor(that); + } +} + +void binder_transaction_Init(binder_transaction_t *that) +{ + that->next = NULL; + that->target = NULL; + that->sender = NULL; + that->receiver = NULL; + + that->code = 0; + that->team = NULL; + that->flags = 0; + that->priority = B_NORMAL_PRIORITY; // FIXME? + that->data_size = 0; + that->offsets_size = 0; + that->data_ptr = NULL; + that->offsets_ptr = NULL; + + that->map = NULL; +} + +void +binder_transaction_dtor(binder_transaction_t *that) +{ + binder_proc_t *owner = NULL; + DPRINTF(4, (KERN_WARNING "%s(%p)\n", __func__, that)); + if (that->offsets_size > 0) { + DPRINTF(5, (KERN_WARNING " -- have binders to clean up\n")); + if(that->flags & tfReferenced) { + BND_ASSERT((that->map) != NULL, "binder_transaction_dtor that->map == NULL"); + } + else { + DPRINTF(0, (KERN_WARNING "ConvertToNodes() not called on %p! that->map == %p\n", that, that->map)); + BND_ASSERT((that->map) == NULL, "binder_transaction_dtor ConvertToNodes() not called and that->map != NULL"); + } + if (that->team && BND_ATTEMPT_ACQUIRE(binder_proc, that->team, STRONG, that)) owner = that->team; + + DPRINTF(5, (KERN_WARNING " -- that->map == %p\n", that->map)); + if(that->map != NULL) { // avoid crash due to corrupt transaction + u8 *ptr = 0; + const size_t *off; + const size_t *offEnd; + struct flat_binder_object* flat; + + ptr = binder_transaction_Data(that); + off = (const size_t*)binder_transaction_Offsets(that); + offEnd = off + (that->offsets_size/sizeof(size_t)); + + BND_FLUSH_CACHE( binder_transaction_UserData(that), + binder_transaction_UserOffsets(that) + + binder_transaction_OffsetsSize(that) ); + while (off < offEnd) { + DPRINTF(9, (KERN_WARNING "type ptr: %p\n", ptr+*off)); + flat = (struct flat_binder_object*)(ptr + *off++); + DPRINTF(9, (KERN_WARNING " type: %08lx\n", flat->type)); + switch (flat->type) { + case kPackedLargeBinderHandleType: + DPRINTF(9, (KERN_WARNING "Delete binder_transaction B_BINDER_HANDLE_TYPE %ld\n",flat->handle)); + // Only call if there are primary references on the team. + // Otherwise, it has already removed all of its handles. + if (owner) binder_proc_UnrefDescriptor(owner, flat->handle, STRONG); + break; + case kPackedLargeBinderType: + // Only do this if there are primary references on the team. + // The team doesn't go away until all published binders are + // removed; after that, there are no references to remove. + if (owner) { + binder_node_t *n; + if (binder_proc_Ptr2Node(owner, flat->binder,flat->cookie,&n,NULL,that, STRONG) == 0) { + if (n) { + BND_RELEASE(binder_node, n, STRONG,that); // once for the grab we just did + BND_RELEASE(binder_node, n, STRONG,that); // and once for the reference this transaction holds + } + } else { + BND_ASSERT(FALSE, "Can't find node!"); + } + } + break; + case kPackedLargeBinderNodeType: + if (flat->node) BND_RELEASE(binder_node, flat->node, STRONG,that); + break; + case kPackedLargeBinderWeakHandleType: + DPRINTF(9, (KERN_WARNING "Delete binder_transaction B_BINDER_HANDLE_TYPE %ld\n",flat->handle)); + // Only call if there are primary references on the team. + // Otherwise, it has already removed all of its handles. + if (owner) binder_proc_UnrefDescriptor(owner, flat->handle, WEAK); + break; + case kPackedLargeBinderWeakType: + // Only do this if there are primary references on the team. + // The team doesn't go away until all published binders are + // removed; after that, there are no references to remove. + if (owner) { + binder_node_t *n; + if (binder_proc_Ptr2Node(owner, flat->binder,flat->cookie,&n,NULL,that,WEAK) == 0) { + if (n) { + BND_RELEASE(binder_node, n, WEAK,that); // once for the grab we just did + BND_RELEASE(binder_node, n, WEAK,that); // and once for the reference this transaction holds + } + } else { + BND_ASSERT(FALSE, "Can't find node!"); + } + } + break; + case kPackedLargeBinderWeakNodeType: + if (flat->node) BND_RELEASE(binder_node, flat->node, WEAK,that); + break; + } + } + BND_FLUSH_CACHE(ptr, offEnd); + } + } + + // release the RAM and address space in the receiver. + if (that->map) { + binder_proc_t* mapProc = that->map->team; + if (mapProc) { + binder_proc_FreeTransactionBuffer(mapProc, that->map); + BND_RELEASE(binder_proc, mapProc, WEAK, that); + } + else printk(KERN_WARNING "%s(%p) -- no team trying to release map %p\n", __func__, that, that->map); + } + + if (owner) BND_RELEASE(binder_proc, owner, STRONG,that); + + binder_transaction_ReleaseTeam(that); + binder_transaction_ReleaseTarget(that); + + // release the RAM + FREE_TRANS(that); +} + +/* We need the recipient team passed in because we can't always know the + * receiver at this point. */ +status_t +binder_transaction_CopyTransactionData(binder_transaction_t *that, binder_proc_t *recipient) +{ + status_t result = -EINVAL; + size_t tSize = INT_ALIGN(that->data_size) + INT_ALIGN(that->offsets_size); + DPRINTF(0, (KERN_WARNING "%s(%p, %p)\n", __func__, that, recipient)); + // Do we need to ensure that->map contains NULL? What do we do if it doesn't? + if(tSize >= binder_transaction_print_size) { + printk(KERN_WARNING "%s-%d: binder_transaction_CopyTransactionData size %d (%d,%d) to %p, reply=%d\n", + current->comm, current->pid, tSize, that->data_size, that->offsets_size, recipient, binder_transaction_IsReply(that)); + } + if (binder_transaction_IsAcquireReply(that)) { + // No data to copy + result = 0; + } else { + // if (tSize >= sizeof(that->data)) { + if(tSize >= binder_transaction_fail_size) { + printk(KERN_ERR "%s-%d: binder_transaction_CopyTransactionData transaction size too big, size %d (%d,%d) to %p\n", + current->comm, current->pid, tSize, that->data_size, that->offsets_size, recipient); + return result; + } + that->map = binder_proc_AllocateTransactionBuffer(recipient, tSize); + if (that->map) { + BND_ACQUIRE(binder_proc, that->map->team, WEAK, that); + + // locate our kernel-space address + u8 *to = page_address(that->map->page); + size_t not_copied; + // copy the data from user-land + BND_FLUSH_CACHE( binder_transaction_UserData(that), + binder_transaction_UserData(that) + tSize ); + not_copied = copy_from_user(to, that->data_ptr, that->data_size); + // and the offsets, too + if ((not_copied == 0) && (that->offsets_size != 0)) { + to += INT_ALIGN(that->data_size); + not_copied = copy_from_user(to, that->offsets_ptr, that->offsets_size); + if (not_copied) { + DPRINTF(0, (KERN_WARNING " -- failed to copy %u of %u bytes of offsets from %p to %p\n", not_copied, that->offsets_size, that->offsets_ptr, to)); + } + } else if (not_copied) { + // BUSTED! + DPRINTF(0, (KERN_WARNING " -- Couldn't copy %u of %u bytes from user-land %p to %p\n", not_copied, that->data_size, that->data_ptr, to)); + } + DPRINTF(4, ("Copied transaction %p: data=%p, size=%p, not_copied=%p\n", + that, binder_transaction_Data(that), + binder_transaction_DataSize(that), + not_copied)); + if (binder_transaction_DataSize(that) > 0) { + DPRINTF(4, ("Copied transaction %p: my_first=%p, user_first=%p\n", + that, + (*(u32*)binder_transaction_Data(that)), + (*(u32*)that->data_ptr))); + } + BND_FLUSH_CACHE( binder_transaction_Data(that), + binder_transaction_Data(that) + tSize ); + result = not_copied ? -EFAULT : 0; + } + else { + DPRINTF(0, (KERN_WARNING "binder_transaction_CopyTransactionData() failed to allocate transaction buffer in %p\n", recipient)); + } + // } else { + // // ignore inlined data for now + // printk(KERN_WARNING "Small transaction in binder_transaction_CopyTransactionData\n"); + // binder_transaction_SetInline(that, TRUE); + // } + } + return result; +} + diff -Nru linux-2.6.23/drivers/binder/binder_transaction.h kernel.android/drivers/binder/binder_transaction.h --- linux-2.6.23/drivers/binder/binder_transaction.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/binder_transaction.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,127 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef BINDER_TRANSACTION_H +#define BINDER_TRANSACTION_H + +#include "binder_defs.h" +#include "iobuffer.h" +#include // for page_address() + +enum { + tfUserFlags = 0x000F, + + tfIsReply = 0x0100, + tfIsEvent = 0x0200, + tfIsAcquireReply = 0x0400, + tfIsDeadReply = 0x0800, + tfIsFailedReply = 0x0020, + tfIsFreePending = 0x0040, + + tfAttemptAcquire = 0x1000, + tfRelease = 0x2000, + tfDecRefs = 0x3000, + tfRefTransaction = 0xF000, + + tfReferenced = 0x0080 +}; + +typedef struct binder_transaction { + struct binder_transaction * next; // next in the transaction queue + struct binder_node * target; // the receiving binder + struct binder_thread * sender; // the sending thread + struct binder_thread * receiver; // the receiving thread + + u32 code; + struct binder_proc * team; // do we need this? Won't sender or receiver's m_team do? + u16 flags; + s16 priority; + size_t data_size; + size_t offsets_size; + const void * data_ptr; + const void * offsets_ptr; + + // The pointer to the actual transaction data. The binder offsets appear + // at (mapped address + data_size). + struct range_map * map; + // 12 bytes of inlined transaction data: just enough for one binder (type, ptr/descriptor, offset) + u8 data[12]; +} binder_transaction_t; + +binder_transaction_t* binder_transaction_CreateRef(u16 refFlags, void *ptr, void *cookie, struct binder_proc* team /* = NULL */); +binder_transaction_t* binder_transaction_Create(u32 code, size_t dataSize, const void *data, size_t offsetsSize /* = 0 */, const void *offsetsData /* = NULL */); +binder_transaction_t* binder_transaction_CreateEmpty(void); +void binder_transaction_Destroy(binder_transaction_t *that); +/* Call this to destroy a transaction before you have called + ConvertToNodes() on it. This will avoid releasing references + on any nodes in the transaction, which you haven't yet acquired. */ +void binder_transaction_DestroyNoRefs(binder_transaction_t *that); +/* Converts from user-types to kernel-nodes */ +status_t binder_transaction_ConvertToNodes(binder_transaction_t *that, struct binder_proc *from, iobuffer_t *io); +/* Converts from kernel-nodes to user-types */ +status_t binder_transaction_ConvertFromNodes(binder_transaction_t *that, struct binder_proc *to); +void binder_transaction_ReleaseTarget(binder_transaction_t *that); +void binder_transaction_ReleaseTeam(binder_transaction_t *that); + +/* Return the maximum IO bytes that will be written by + ConvertToNodes(). */ +size_t binder_transaction_MaxIOToNodes(binder_transaction_t *that); + +/* If this transaction has a primary reference on its team, + return it and clear the pointer. You now own the reference. */ +struct binder_proc * binder_transaction_TakeTeam(binder_transaction_t *that, struct binder_proc *me); +status_t binder_transaction_CopyTransactionData(binder_transaction_t *that, struct binder_proc *recipient); + +#define INT_ALIGN(x) (((x)+sizeof(int)-1)&~(sizeof(int)-1)) +#define binder_transaction_Data(that) ((u8*)page_address((that)->map->page)) +#define binder_transaction_UserData(that) ((void*)((that)->map->start)) +#define binder_transaction_DataSize(that) ((that)->data_size) +#define binder_transaction_Offsets(that) ((size_t*)(binder_transaction_Data(that)+INT_ALIGN((that)->data_size))) +#define binder_transaction_UserOffsets(that) ((void*)((that)->map->start + INT_ALIGN((that)->data_size))) +#define binder_transaction_OffsetsSize(that) ((that)->offsets_size) + +#define binder_transaction_UserFlags(that) ((that)->flags & tfUserFlags) +#define binder_transaction_RefFlags(that) ((that)->flags & tfRefTransaction) +#define binder_transaction_IsInline(that) ((that)->flags & tfInline) +#define binder_transaction_IsRootObject(that) ((that)->flags & tfRootObject) +#define binder_transaction_IsReply(that) ((that)->flags & tfIsReply) +#define binder_transaction_IsEvent(that) ((that)->flags & tfIsEvent) +#define binder_transaction_IsAcquireReply(that) ((that)->flags & tfIsAcquireReply) +#define binder_transaction_IsDeadReply(that) ((that)->flags & tfIsDeadReply) +#define binder_transaction_IsFailedReply(that) ((that)->flags & tfIsFailedReply) +#define binder_transaction_IsAnyReply(that) ((that)->flags & (tfIsReply|tfIsAcquireReply|tfIsDeadReply)) +#define binder_transaction_IsFreePending(that) ((that)->flags & tfIsFreePending) +#define binder_transaction_IsReferenced(that) ((that)->flags & tfReferenced) + +#define binder_transaction_SetUserFlags(that, f) { (that)->flags = ((that)->flags&(~tfUserFlags)) | (f&tfUserFlags); } +#define binder_transaction_SetInline(that, f) { if (f) (that)->flags |= tfInline; else (that)->flags &= ~tfInline; } +#define binder_transaction_SetRootObject(that, f) { if (f) (that)->flags |= tfRootObject; else (that)->flags &= ~tfRootObject; } +#define binder_transaction_SetReply(that, f) { if (f) (that)->flags |= tfIsReply; else (that)->flags &= ~tfIsReply; } +#define binder_transaction_SetDeadReply(that, f) { if (f) (that)->flags |= tfIsDeadReply; else (that)->flags &= ~tfIsDeadReply; } +#define binder_transaction_SetFailedReply(that, f) { if (f) (that)->flags |= tfIsFailedReply; else (that)->flags &= ~tfIsFailedReply; } +#define binder_transaction_SetEvent(that, f) { if (f) (that)->flags |= tfIsEvent; else (that)->flags &= ~tfIsEvent; } +#define binder_transaction_SetAcquireReply(that, f) { if (f) (that)->flags |= tfIsAcquireReply; else (that)->flags &= ~tfIsAcquireReply; } +#define binder_transaction_SetFreePending(that, f) { if (f) (that)->flags |= tfIsFreePending; else (that)->flags &= ~tfIsFreePending; } + +#define binder_transaction_Code(that) ((that)->code) + +#define binder_transaction_Priority(that) ((that)->priority) +#define binder_transaction_SetPriority(that, pri) { (that)->priority = pri; } + + +#endif diff -Nru linux-2.6.23/drivers/binder/iobuffer.c kernel.android/drivers/binder/iobuffer.c --- linux-2.6.23/drivers/binder/iobuffer.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/iobuffer.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,112 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "iobuffer.h" +#include "binder_defs.h" +#include + +int iobuffer_init(iobuffer_t *that, unsigned long base, int size, int consumed) { + // require 4 byte alignment for base + if ((base & 0x3) != 0) printk(KERN_WARNING "iobuffer_init() bad buffer alignment\n"); + if ((base & 0x3) != 0) return -EFAULT; + if (!access_ok(VERIFY_WRITE, base, size)) printk(KERN_WARNING "access_ok(): FALSE\n"); + if (!access_ok(VERIFY_WRITE, base, size)) return -EFAULT; + DPRINTF(9, (KERN_WARNING "iobuffer_init(%p, %08lx, %d)\n", that, base, size)); + that->m_base = base; + that->m_size = size; + that->m_offs = that->m_consumed = consumed; + return 0; +} + +int iobuffer_read_raw(iobuffer_t *that, void *data, int size) +{ + if ((that->m_size-that->m_offs) < size) return -EFAULT; + copy_from_user(data, (void*)(that->m_base+that->m_offs), size); + that->m_offs += size; + return 0; +} + +int iobuffer_read_u32(iobuffer_t *that, u32 *data) +{ + if ((that->m_size-that->m_offs) < sizeof(u32)) return -EFAULT; + copy_from_user(data, (void*)(that->m_base+that->m_offs), sizeof(u32)); + that->m_offs += sizeof(u32); + return 0; +} + +int iobuffer_read_void(iobuffer_t *that, void **data) +{ + if ((that->m_size-that->m_offs) < sizeof(void*)) return -EFAULT; + copy_from_user(data, (void*)(that->m_base+that->m_offs), sizeof(void*)); + that->m_offs += sizeof(void*); + return 0; +} + +int iobuffer_write_raw(iobuffer_t *that, const void *data, int size) +{ + if ((that->m_size-that->m_offs) < size) return -EFAULT; + copy_to_user((void*)(that->m_base+that->m_offs), data, size); + that->m_offs += size; + return 0; +} + +int iobuffer_write_u32(iobuffer_t *that, u32 data) +{ + if ((that->m_size-that->m_offs) < sizeof(u32)) return -EFAULT; + // *((u32*)(that->m_base+that->m_offs)) = data; + __put_user(data, ((u32*)(that->m_base+that->m_offs))); + that->m_offs += sizeof(u32); + return 0; +} + +int iobuffer_write_void(iobuffer_t *that, const void *data) +{ + if ((that->m_size-that->m_offs) < sizeof(void *)) return -EFAULT; + // *((void **)(that->m_base+that->m_offs)) = data; + __put_user(data, ((void**)(that->m_base+that->m_offs))); + that->m_offs += sizeof(void*); + return 0; +} + +int iobuffer_drain(iobuffer_t *that, int size) { + if (size > (that->m_size-that->m_offs)) size = that->m_size-that->m_offs; + that->m_offs += size; + return size; +} + +int iobuffer_remaining(iobuffer_t *that) +{ + return that->m_size-that->m_offs; +} + +int iobuffer_consumed(iobuffer_t *that) +{ + return that->m_consumed; +} + +void iobuffer_mark_consumed(iobuffer_t *that) +{ + that->m_consumed = that->m_offs; +} + +void iobuffer_remainder(iobuffer_t *that, void **ptr, int *size) +{ + *ptr = ((uint8_t*)that->m_base)+that->m_offs; + *size = that->m_size - that->m_offs; +} + diff -Nru linux-2.6.23/drivers/binder/iobuffer.h kernel.android/drivers/binder/iobuffer.h --- linux-2.6.23/drivers/binder/iobuffer.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/iobuffer.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,44 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _IOBUFFER_H_ +#define _IOBUFFER_H_ + +#include + +typedef struct iobuffer { + unsigned long m_base; + int m_offs; + int m_size; + int m_consumed; +} iobuffer_t; + +extern int iobuffer_init(iobuffer_t *that, unsigned long base, int size, int consumed); +extern int iobuffer_read_raw(iobuffer_t *that, void *data, int size); +extern int iobuffer_read_u32(iobuffer_t *that, u32 *data); +extern int iobuffer_read_void(iobuffer_t *that, void **data); +extern int iobuffer_write_raw(iobuffer_t *that, const void *data, int size); +extern int iobuffer_write_u32(iobuffer_t *that, u32 data); +extern int iobuffer_write_void(iobuffer_t *that, const void *data); +extern int iobuffer_drain(iobuffer_t *that, int size); +extern int iobuffer_remaining(iobuffer_t *that); +extern int iobuffer_consumed(iobuffer_t *that); +extern void iobuffer_mark_consumed(iobuffer_t *that); +extern void iobuffer_remainder(iobuffer_t *that, void **ptr, int *size); + +#endif diff -Nru linux-2.6.23/drivers/binder/tester.c kernel.android/drivers/binder/tester.c --- linux-2.6.23/drivers/binder/tester.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/binder/tester.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,63 @@ +/* binder driver + * Copyright (C) 2005 Palmsource, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef signed long sl_t; +typedef unsigned long ul_t; + +const sl_t cmd_write_limit = 1024; +const sl_t cmd_read_limit = 1024; + +int main(int argc, char **argv) { + int result; + binder_write_read_t bwr; + sl_t write_count = 0; + uint8_t *write_buf = malloc(cmd_write_limit); + uint8_t *read_buf = malloc(cmd_read_limit); + bwr.write_buffer = (ul_t)write_buf; + bwr.write_size = 0; + bwr.read_size = cmd_read_limit; + bwr.read_buffer = (ul_t)read_buf; + uint8_t *wb = write_buf; + + + int fd = open("/dev/binder", O_RDWR); + if (fd < 0) { + printf("Open failed: %s\n", strerror(errno)); + return -1; + } + *(ul_t*)wb = bcSET_CONTEXT_MANAGER; + bwr.write_size += sizeof(ul_t); + wb += sizeof(ul_t); + *(ul_t*)wb = bcENTER_LOOPER; + bwr.write_size += sizeof(ul_t); + result = ioctl(fd, BINDER_WRITE_READ, &bwr); + printf("ioctl(fd, BINDER_WRITE_READ, &bwr): %08x", result); + if (result < 0) printf(" %08x : %s", errno, strerror(errno)); + printf("\n"); + return 0; +} diff -Nru linux-2.6.23/drivers/char/Kconfig kernel.android/drivers/char/Kconfig --- linux-2.6.23/drivers/char/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/char/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -1062,6 +1062,18 @@ depends on ISA || PCI default y +config GOLDFISH_TTY + tristate "Goldfish TTY Driver" + default n + help + TTY driver for Goldfish Virtual Platform. + +config BINDER + tristate "OpenBinder IPC Driver" + default n + help + from openbinder.org + source "drivers/s390/char/Kconfig" endmenu diff -Nru linux-2.6.23/drivers/char/Makefile kernel.android/drivers/char/Makefile --- linux-2.6.23/drivers/char/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/char/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -96,6 +96,7 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o obj-$(CONFIG_GPIO_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o +obj-$(CONFIG_GOLDFISH_TTY) += goldfish_tty.o obj-$(CONFIG_MWAVE) += mwave/ obj-$(CONFIG_AGP) += agp/ diff -Nru linux-2.6.23/drivers/char/goldfish_tty.c kernel.android/drivers/char/goldfish_tty.c --- linux-2.6.23/drivers/char/goldfish_tty.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/char/goldfish_tty.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,315 @@ +/* drivers/char/goldfish_tty.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +enum { + GOLDFISH_TTY_PUT_CHAR = 0x00, + GOLDFISH_TTY_BYTES_READY = 0x04, + GOLDFISH_TTY_CMD = 0x08, + + GOLDFISH_TTY_DATA_PTR = 0x10, + GOLDFISH_TTY_DATA_LEN = 0x14, + + GOLDFISH_TTY_CMD_INT_DISABLE = 0, + GOLDFISH_TTY_CMD_INT_ENABLE = 1, + GOLDFISH_TTY_CMD_WRITE_BUFFER = 2, + GOLDFISH_TTY_CMD_READ_BUFFER = 3, +}; + +struct goldfish_tty { + spinlock_t lock; + uint32_t base; + uint32_t irq; + int opencount; + struct tty_struct *tty; + struct console console; +}; + +static DEFINE_MUTEX(goldfish_tty_lock); +static struct tty_driver *goldfish_tty_driver; +static uint32_t goldfish_tty_line_count = 8; +static uint32_t goldfish_tty_current_line_count; +static struct goldfish_tty *goldfish_ttys; + +static void goldfish_tty_do_write(int line, const char *buf, unsigned count) +{ + unsigned long irq_flags; + struct goldfish_tty *qtty = &goldfish_ttys[line]; + uint32_t base = qtty->base; + spin_lock_irqsave(&qtty->lock, irq_flags); + writel(buf, base + GOLDFISH_TTY_DATA_PTR); + writel(count, base + GOLDFISH_TTY_DATA_LEN); + writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD); + spin_unlock_irqrestore(&qtty->lock, irq_flags); +} + +static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct goldfish_tty *qtty = &goldfish_ttys[pdev->id]; + uint32_t base = qtty->base; + unsigned long irq_flags; + unsigned char *buf; + uint32_t count; + + count = readl(base + GOLDFISH_TTY_BYTES_READY); + if(count == 0) { + return IRQ_NONE; + } + count = tty_prepare_flip_string(qtty->tty, &buf, count); + spin_lock_irqsave(&qtty->lock, irq_flags); + writel(buf, base + GOLDFISH_TTY_DATA_PTR); + writel(count, base + GOLDFISH_TTY_DATA_LEN); + writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD); + spin_unlock_irqrestore(&qtty->lock, irq_flags); + tty_schedule_flip(qtty->tty); + return IRQ_HANDLED; +} + +static int goldfish_tty_open(struct tty_struct * tty, struct file * filp) +{ + int ret; + struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; + + mutex_lock(&goldfish_tty_lock); + if(qtty->tty == NULL || qtty->tty == tty) { + if(qtty->opencount++ == 0) { + qtty->tty = tty; + writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD); + } + ret = 0; + } + else + ret = -EBUSY; + mutex_unlock(&goldfish_tty_lock); + return ret; +} + +static void goldfish_tty_close(struct tty_struct * tty, struct file * filp) +{ + struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; + + mutex_lock(&goldfish_tty_lock); + if(qtty->tty == tty) { + if(--qtty->opencount == 0) { + writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD); + qtty->tty = NULL; + } + } + mutex_unlock(&goldfish_tty_lock); +} + +static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) +{ + goldfish_tty_do_write(tty->index, buf, count); + return count; +} + +static int goldfish_tty_write_room(struct tty_struct *tty) +{ + return 0x10000; +} + +static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count) +{ + goldfish_tty_do_write(co->index, b, count); +} + +static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index) +{ + *index = c->index; + return goldfish_tty_driver; +} + +static int __init goldfish_tty_console_setup(struct console *co, char *options) +{ + if((unsigned)co->index > goldfish_tty_line_count) + return -ENODEV; + if(goldfish_ttys[co->index].base == 0) + return -ENODEV; + return 0; +} + +static struct tty_operations goldfish_tty_ops = { + .open = goldfish_tty_open, + .close = goldfish_tty_close, + .write = goldfish_tty_write, + .write_room = goldfish_tty_write_room, +}; + +static int goldfish_tty_create_driver(void) +{ + int ret; + struct tty_driver *tty; + + goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL); + if(goldfish_ttys == NULL) { + ret = -ENOMEM; + goto err_alloc_goldfish_ttys_failed; + } + + tty = alloc_tty_driver(goldfish_tty_line_count); + if(tty == NULL) { + ret = -ENOMEM; + goto err_alloc_tty_driver_failed; + } + tty->driver_name = "goldfish"; + tty->name = "ttyS"; + tty->type = TTY_DRIVER_TYPE_SERIAL; + tty->subtype = SERIAL_TYPE_NORMAL; + tty->init_termios = tty_std_termios; + tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(tty, &goldfish_tty_ops); + ret = tty_register_driver(tty); + if(ret) + goto err_tty_register_driver_failed; + + goldfish_tty_driver = tty; + return 0; + +err_tty_register_driver_failed: + put_tty_driver(tty); +err_alloc_tty_driver_failed: + kfree(goldfish_ttys); + goldfish_ttys = NULL; +err_alloc_goldfish_ttys_failed: + return ret; +} + +static void goldfish_tty_delete_driver(void) +{ + tty_unregister_driver(goldfish_tty_driver); + put_tty_driver(goldfish_tty_driver); + goldfish_tty_driver = NULL; + kfree(goldfish_ttys); + goldfish_ttys = NULL; +} + +static int goldfish_tty_probe(struct platform_device *pdev) +{ + int ret; + int i; + struct resource *r; + struct device *ttydev; + uint32_t base; + uint32_t irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) + return -EINVAL; + base = IO_ADDRESS(r->start - IO_START); + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if(r == NULL) + return -EINVAL; + irq = r->start; + + if(pdev->id >= goldfish_tty_line_count) + return -EINVAL; + + mutex_lock(&goldfish_tty_lock); + if(goldfish_tty_current_line_count == 0) { + ret = goldfish_tty_create_driver(); + if(ret) + goto err_create_driver_failed; + } + goldfish_tty_current_line_count++; + + spin_lock_init(&goldfish_ttys[pdev->id].lock); + goldfish_ttys[pdev->id].base = base; + goldfish_ttys[pdev->id].irq = irq; + + writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD); + + ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, "goldfish_tty", pdev); + if(ret) + goto err_request_irq_failed; + + + ttydev = tty_register_device(goldfish_tty_driver, pdev->id, NULL); + if(IS_ERR(ttydev)) { + ret = PTR_ERR(ttydev); + goto err_tty_register_device_failed; + } + + strcpy(goldfish_ttys[pdev->id].console.name, "ttyS"); + goldfish_ttys[pdev->id].console.write = goldfish_tty_console_write; + goldfish_ttys[pdev->id].console.device = goldfish_tty_console_device; + goldfish_ttys[pdev->id].console.setup = goldfish_tty_console_setup; + goldfish_ttys[pdev->id].console.flags = CON_PRINTBUFFER; + goldfish_ttys[pdev->id].console.index = pdev->id; + register_console(&goldfish_ttys[pdev->id].console); + + + mutex_unlock(&goldfish_tty_lock); + + return 0; + + tty_unregister_device(goldfish_tty_driver, i); +err_tty_register_device_failed: + free_irq(irq, pdev); +err_request_irq_failed: + goldfish_tty_current_line_count--; + if(goldfish_tty_current_line_count == 0) { + goldfish_tty_delete_driver(); + } +err_create_driver_failed: + mutex_unlock(&goldfish_tty_lock); + return ret; +} + +static int goldfish_tty_remove(struct platform_device *pdev) +{ + mutex_lock(&goldfish_tty_lock); + unregister_console(&goldfish_ttys[pdev->id].console); + tty_unregister_device(goldfish_tty_driver, pdev->id); + goldfish_ttys[pdev->id].base = 0; + free_irq(goldfish_ttys[pdev->id].irq, pdev); + goldfish_tty_current_line_count--; + if(goldfish_tty_current_line_count == 0) { + goldfish_tty_delete_driver(); + } + mutex_unlock(&goldfish_tty_lock); + return 0; +} + +static struct platform_driver goldfish_tty = { + .probe = goldfish_tty_probe, + .remove = goldfish_tty_remove, + .driver = { + .name = "goldfish_tty" + } +}; + +static int __init goldfish_tty_init(void) +{ + return platform_driver_register(&goldfish_tty); +} + +static void goldfish_tty_exit(void) +{ + platform_driver_unregister(&goldfish_tty); +} + +module_init(goldfish_tty_init); +module_exit(goldfish_tty_exit); diff -Nru linux-2.6.23/drivers/input/evdev.c kernel.android/drivers/input/evdev.c --- linux-2.6.23/drivers/input/evdev.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/input/evdev.c 2007-11-12 07:49:02.000000000 +1100 @@ -20,6 +20,9 @@ #include #include #include +#ifdef CONFIG_ANDROID_POWER +#include +#endif struct evdev { int exist; @@ -40,10 +43,23 @@ struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; +#ifdef CONFIG_ANDROID_POWER + android_suspend_lock_t suspend_lock; +#endif }; static struct evdev *evdev_table[EVDEV_MINORS]; +#ifdef CONFIG_ANDROID_POWER +static void do_gettimeofday_monotonic(struct timeval *tv) +{ + struct timespec ts; + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} +#endif + static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct evdev *evdev = handle->private; @@ -52,7 +68,12 @@ if (evdev->grab) { client = evdev->grab; +#ifdef CONFIG_ANDROID_POWER + android_lock_suspend_auto_expire(&client->suspend_lock, 5 * HZ); + do_gettimeofday_monotonic(&client->buffer[client->head].time); +#else do_gettimeofday(&client->buffer[client->head].time); +#endif client->buffer[client->head].type = type; client->buffer[client->head].code = code; client->buffer[client->head].value = value; @@ -62,7 +83,12 @@ } else list_for_each_entry(client, &evdev->client_list, node) { +#ifdef CONFIG_ANDROID_POWER + android_lock_suspend_auto_expire(&client->suspend_lock, 5 * HZ); + do_gettimeofday_monotonic(&client->buffer[client->head].time); +#else do_gettimeofday(&client->buffer[client->head].time); +#endif client->buffer[client->head].type = type; client->buffer[client->head].code = code; client->buffer[client->head].value = value; @@ -121,6 +147,9 @@ input_close_device(&evdev->handle); put_device(&evdev->dev); +#ifdef CONFIG_ANDROID_POWER + android_uninit_suspend_lock(&client->suspend_lock); +#endif return 0; } @@ -148,6 +177,10 @@ goto err_put_evdev; } +#ifdef CONFIG_ANDROID_POWER + client->suspend_lock.name = "evdev"; + android_init_suspend_lock(&client->suspend_lock); +#endif client->evdev = evdev; list_add_tail(&client->node, &evdev->client_list); @@ -315,6 +348,10 @@ return -EFAULT; client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1); +#ifdef CONFIG_ANDROID_POWER + if(client->head == client->tail) + android_unlock_suspend(&client->suspend_lock); +#endif retval += evdev_event_size(); } diff -Nru linux-2.6.23/drivers/input/keyboard/Kconfig kernel.android/drivers/input/keyboard/Kconfig --- linux-2.6.23/drivers/input/keyboard/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/input/keyboard/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -253,4 +253,9 @@ To compile this driver as a module, choose M here: the module will be called gpio-keys. +config KEYBOARD_GOLDFISH_EVENTS + tristate "Generic Input Event device for Goldfish" + help + no help + endif diff -Nru linux-2.6.23/drivers/input/keyboard/Makefile kernel.android/drivers/input/keyboard/Makefile --- linux-2.6.23/drivers/input/keyboard/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/input/keyboard/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -21,4 +21,4 @@ obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o - +obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o diff -Nru linux-2.6.23/drivers/input/keyboard/goldfish_events.c kernel.android/drivers/input/keyboard/goldfish_events.c --- linux-2.6.23/drivers/input/keyboard/goldfish_events.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/input/keyboard/goldfish_events.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,126 @@ +/* drivers/input/keyboard/goldfish-events.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 +#include + +struct event_dev { + struct input_dev *input; + int irq; + unsigned addr; +}; + +static irqreturn_t events_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct event_dev *edev = dev_id; + unsigned type, code, value; + + type = __raw_readl(edev->addr); + code = __raw_readl(edev->addr); + value = __raw_readl(edev->addr); + + input_event(edev->input, type, code, value); + return IRQ_HANDLED; +} + +static int events_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct event_dev *edev; + struct resource *res; + + printk("*** events probe ***\n"); + + edev = kzalloc(sizeof(struct event_dev), GFP_KERNEL); + input_dev = input_allocate_device(); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(!input_dev || !edev || !res) goto fail; + + edev->input = input_dev; + edev->addr = (unsigned) ioremap(res->start, 4096); + edev->irq = platform_get_irq(pdev, 0); + + printk("events_probe() addr=0x%08x irq=%d\n", edev->addr, edev->irq); + + if(!edev->addr) goto fail; + if(edev->irq < 0) goto fail; + + if(request_irq(edev->irq, events_interrupt, 0, + "goldfish-events-keypad", edev) < 0) { + goto fail; + } + + /* indicate that we generate key events */ + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_REL, input_dev->evbit); + set_bit(EV_ABS, input_dev->evbit); + + /* indicate that we generate *any* key event -- we + * do not know what keycodes the emulator will send + * us, so we advertise *all* of them + */ + bitmap_fill(input_dev->keybit, KEY_MAX); + bitmap_fill(input_dev->relbit, REL_MAX); + bitmap_fill(input_dev->absbit, ABS_MAX); + + platform_set_drvdata(pdev, edev); + + input_dev->name = "goldfish-events-keyboard"; + input_dev->private = edev; + input_dev->cdev.dev = &pdev->dev; + + input_register_device(input_dev); + return 0; + +fail: + kfree(edev); + input_free_device(input_dev); + + return -EINVAL; +} + +static struct platform_driver events_driver = { + .probe = events_probe, + .driver = { + .name = "goldfish_events", + }, +}; + +static int __devinit events_init(void) +{ + return platform_driver_register(&events_driver); +} + + +static void __exit events_exit(void) +{ +} + +module_init(events_init); +module_exit(events_exit); + +MODULE_AUTHOR("Brian Swetland"); +MODULE_DESCRIPTION("Goldfish Event Device"); +MODULE_LICENSE("GPL"); diff -Nru linux-2.6.23/drivers/misc/Kconfig kernel.android/drivers/misc/Kconfig --- linux-2.6.23/drivers/misc/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/misc/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -202,5 +202,14 @@ If you are not sure, say Y here. +config LOW_MEMORY_KILLER + tristate "Low Memory Killer" + ---help--- + Register processes to be killed when memory is low. + +config QEMU_TRACE + tristate "Virtual Device for QEMU tracing" + ---help--- + This is a virtual device for QEMU tracing. endif # MISC_DEVICES diff -Nru linux-2.6.23/drivers/misc/Makefile kernel.android/drivers/misc/Makefile --- linux-2.6.23/drivers/misc/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/misc/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -15,3 +15,5 @@ obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o +obj-$(CONFIG_LOW_MEMORY_KILLER) += lowmemorykiller/ +obj-$(CONFIG_QEMU_TRACE) += qemutrace/ diff -Nru linux-2.6.23/drivers/misc/lowmemorykiller/Makefile kernel.android/drivers/misc/lowmemorykiller/Makefile --- linux-2.6.23/drivers/misc/lowmemorykiller/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/misc/lowmemorykiller/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1 @@ +obj-$(CONFIG_LOW_MEMORY_KILLER) := lowmemorykiller.o diff -Nru linux-2.6.23/drivers/misc/lowmemorykiller/lowmemorykiller.c kernel.android/drivers/misc/lowmemorykiller/lowmemorykiller.c --- linux-2.6.23/drivers/misc/lowmemorykiller/lowmemorykiller.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/misc/lowmemorykiller/lowmemorykiller.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,119 @@ +/* drivers/misc/lowmemorykiller/lowmemorykiller.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask); + +static struct shrinker lowmem_shrinker = { + .shrink = lowmem_shrink, + .seeks = DEFAULT_SEEKS * 16 +}; +static uint32_t lowmem_debug_level = 2; +static int lowmem_adj[6] = { + 0, + 1, + 6, + 12, +}; +static int lowmem_adj_size = 4; +static size_t lowmem_minfree[6] = { + 3*512, // 6MB + 2*1024, // 8MB + 4*1024, // 16MB + 16*1024, // 64MB +}; +static int lowmem_minfree_size = 4; + +#define lowmem_print(level, x...) do { if(lowmem_debug_level >= (level)) printk(x); } while(0) + +module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); +module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, S_IRUGO | S_IWUSR); +module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, S_IRUGO | S_IWUSR); +module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); + +static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) +{ + struct task_struct *p; + struct task_struct *selected = NULL; + int rem = 0; + int tasksize; + int i; + int min_adj = OOM_ADJUST_MAX + 1; + int selected_tasksize = 0; + int array_size = ARRAY_SIZE(lowmem_adj); + int other_free = global_page_state(NR_FREE_PAGES) + global_page_state(NR_FILE_PAGES); + if(lowmem_adj_size < array_size) + array_size = lowmem_adj_size; + if(lowmem_minfree_size < array_size) + array_size = lowmem_minfree_size; + for(i = 0; i < array_size; i++) { + if(other_free < lowmem_minfree[i]) { + min_adj = lowmem_adj[i]; + break; + } + } + if(nr_to_scan > 0) + lowmem_print(3, "lowmem_shrink %d, %x, ofree %d, ma %d\n", nr_to_scan, gfp_mask, other_free, min_adj); + read_lock(&tasklist_lock); + for_each_process(p) { + if(p->oomkilladj >= 0 && p->mm) { + tasksize = get_mm_rss(p->mm); + if(nr_to_scan > 0 && tasksize > 0 && p->oomkilladj >= min_adj) { + if(selected == NULL || + p->oomkilladj > selected->oomkilladj || + (p->oomkilladj == selected->oomkilladj && + tasksize > selected_tasksize)) { + selected = p; + selected_tasksize = tasksize; + lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", + p->pid, p->comm, p->oomkilladj, tasksize); + } + } + rem += tasksize; + } + } + if(selected != NULL) { + lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", + selected->pid, selected->comm, + selected->oomkilladj, selected_tasksize); + force_sig(SIGKILL, selected); + rem -= selected_tasksize; + } + lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem); + read_unlock(&tasklist_lock); + return rem; +} + +static int __init lowmem_init(void) +{ + register_shrinker(&lowmem_shrinker); + return 0; +} + +static void __exit lowmem_exit(void) +{ + unregister_shrinker(&lowmem_shrinker); +} + +module_init(lowmem_init); +module_exit(lowmem_exit); + +MODULE_LICENSE("GPL"); + diff -Nru linux-2.6.23/drivers/misc/qemutrace/Makefile kernel.android/drivers/misc/qemutrace/Makefile --- linux-2.6.23/drivers/misc/qemutrace/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/misc/qemutrace/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,2 @@ +obj-$(CONFIG_QEMU_TRACE) := qemu_trace.o +obj-$(CONFIG_QEMU_TRACE) += qemu_trace_sysfs.o diff -Nru linux-2.6.23/drivers/misc/qemutrace/qemu_trace.c kernel.android/drivers/misc/qemutrace/qemu_trace.c --- linux-2.6.23/drivers/misc/qemutrace/qemu_trace.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/misc/qemutrace/qemu_trace.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,259 @@ +/* + * QEMU trace virtual device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qemu_trace.h" + +/* trace device registers */ +#define TRACE_DEV_REG_SWITCH 0 +#define TRACE_DEV_REG_FORK 1 +#define TRACE_DEV_REG_EXECVE_PID 2 +#define TRACE_DEV_REG_EXECVE_VMSTART 3 +#define TRACE_DEV_REG_EXECVE_VMEND 4 +#define TRACE_DEV_REG_EXECVE_OFFSET 5 +#define TRACE_DEV_REG_EXECVE_EXEPATH 6 +#define TRACE_DEV_REG_EXIT 7 +#define TRACE_DEV_REG_CMDLINE 8 +#define TRACE_DEV_REG_CMDLINE_LEN 9 +#define TRACE_DEV_REG_MMAP_EXEPATH 10 +#define TRACE_DEV_REG_INIT_PID 11 +#define TRACE_DEV_REG_INIT_NAME 12 +#define TRACE_DEV_REG_CLONE 13 +#define TRACE_DEV_REG_DYN_SYM 50 +#define TRACE_DEV_REG_DYN_SYM_ADDR 51 +#define TRACE_DEV_REG_ENABLE 100 + +static unsigned char __iomem *qt_base = NULL; +static int init_called = 0; + +/* PIDs that start before our device registered */ +#define MAX_INIT_PIDS 2048 +static int tb_next = 1; +static int init_pids[MAX_INIT_PIDS]; + +void qemu_trace_start(void) +{ + if(qt_base == NULL) + return; + writel(1, qt_base + (TRACE_DEV_REG_ENABLE << 2)); +} + +void qemu_trace_stop(void) +{ + if(qt_base == NULL) + return; + writel(0, qt_base + (TRACE_DEV_REG_ENABLE << 2)); +} + +int qemu_trace_get_tracing(void) +{ + int val = 0; + if(qt_base != NULL) + val = readl(qt_base + (TRACE_DEV_REG_ENABLE << 2)); + return val; +} + +void qemu_trace_dynamic_symbol(unsigned int addr, const char *symbol) +{ + if(qt_base == NULL) + return; + + // Write the address first, then the symbol name. + writel(addr, qt_base + (TRACE_DEV_REG_DYN_SYM_ADDR << 2)); + writel(symbol, qt_base + (TRACE_DEV_REG_DYN_SYM << 2)); +} + +/* trace the context switch */ +void qemu_trace_cs(struct task_struct *prev, struct task_struct *next) +{ + if (qt_base != NULL) { + writel(next->pid, qt_base); + } +} +EXPORT_SYMBOL(qemu_trace_cs); + +/* trace the execve */ +void qemu_trace_execve(int argc, char __user * __user * argv) +{ + char page[PAGE_SIZE]; + char *ptr = page; + + if(qt_base == NULL) + return; + + while (argc-- > 0) { + char __user *str; + int len; + if (get_user(str, argv ++) || !(len = strnlen_user(str, PAGE_SIZE))) + return; + if (copy_from_user(ptr, str, len)) + return; + ptr += len; + } + + if (ptr > page) { + writel((ptr - page), qt_base + (TRACE_DEV_REG_CMDLINE_LEN << 2)); + writel(page, qt_base + (TRACE_DEV_REG_CMDLINE << 2)); + } +} +EXPORT_SYMBOL(qemu_trace_execve); + +/* trace the mmap */ +void qemu_trace_mmap(struct vm_area_struct *vma) +{ + char page[PAGE_SIZE]; + + if(qt_base == NULL) + return; + + if ((vma->vm_flags & VM_EXEC) && vma->vm_file) { + char *p; + p = d_path(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt, page, PAGE_SIZE); + if (!IS_ERR(p)) { + writel(vma->vm_start, qt_base + (TRACE_DEV_REG_EXECVE_VMSTART << 2)); + writel(vma->vm_end, qt_base + (TRACE_DEV_REG_EXECVE_VMEND << 2)); + writel(vma->vm_pgoff * PAGE_SIZE, qt_base + (TRACE_DEV_REG_EXECVE_OFFSET << 2)); + writel(p, qt_base + (TRACE_DEV_REG_MMAP_EXEPATH << 2)); + } + } +} +EXPORT_SYMBOL(qemu_trace_mmap); + +/* trace the fork */ +void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags) +{ + if (qt_base == NULL) { + if (tb_next >= MAX_INIT_PIDS) { + if(!init_called) + printk("QEMU Trace: too many PIDs before device registered ignoring %d\n", forked->pid); + } + else { + init_pids[tb_next] = forked->pid; + tb_next ++; + } + } + else { + if (clone_flags & CLONE_VM) + writel(forked->pid, qt_base + (TRACE_DEV_REG_CLONE << 2)); + else + writel(forked->pid, qt_base + (TRACE_DEV_REG_FORK << 2)); + } +} +EXPORT_SYMBOL(qemu_trace_fork); + +/* trace the exit */ +void qemu_trace_exit(int code) +{ + if (qt_base != NULL) + writel(code, qt_base + (TRACE_DEV_REG_EXIT << 2)); +} +EXPORT_SYMBOL(qemu_trace_exit); + +static void qemu_trace_pid_exec(struct task_struct *tsk) +{ + char page[PAGE_SIZE]; + struct mm_struct *mm = get_task_mm(tsk); + if (mm == NULL) + return; + down_read(&mm->mmap_sem); + { + struct vm_area_struct *vma = mm->mmap; + while (vma) { + if ((vma->vm_flags & VM_EXEC) && vma->vm_file) { + char *p; + p = d_path(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt, page, PAGE_SIZE); + if (!IS_ERR(p)) { + writel(vma->vm_start, qt_base + (TRACE_DEV_REG_EXECVE_VMSTART << 2)); + writel(vma->vm_end, qt_base + (TRACE_DEV_REG_EXECVE_VMEND << 2)); + writel(vma->vm_pgoff * PAGE_SIZE, qt_base + (TRACE_DEV_REG_EXECVE_OFFSET << 2)); + writel(p, qt_base + (TRACE_DEV_REG_EXECVE_EXEPATH << 2)); + } + } + vma = vma->vm_next; + } + } + up_read(&mm->mmap_sem); + mmput(mm); +} + +static void qemu_trace_dump_init_threads(void) +{ + int i; + for (i = 0; i < tb_next; i ++) { + struct task_struct *tsk; + if ((tsk = find_task_by_pid(init_pids[i])) != NULL) { + /* first give the pid and name */ + task_lock(tsk); + writel(tsk->pid, qt_base + (TRACE_DEV_REG_INIT_PID << 2)); + writel(tsk->comm, qt_base + (TRACE_DEV_REG_INIT_NAME << 2)); + task_unlock(tsk); + /* check if the task has execs */ + qemu_trace_pid_exec(tsk); + } + } +} + +static int qemu_trace_probe(struct platform_device *pdev) +{ + struct resource *r; + if(qt_base != NULL) { // not thread safe, but this should not happen + printk("QEMU TRACE Device: already mapped at %p\n", qt_base); + return -ENODEV; + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) + return -EINVAL; + qt_base = ioremap(r->start, PAGE_SIZE); + printk("QEMU TRACE Device: The mapped IO base is %p\n", qt_base); + + qemu_trace_dump_init_threads(); + + return 0; +} + +static int qemu_trace_remove(struct platform_device *pdev) +{ + iounmap(qt_base); + qt_base = NULL; + return 0; +} + +static struct platform_driver qemu_trace = { + .probe = qemu_trace_probe, + .remove = qemu_trace_remove, + .driver = { + .name = "qemu_trace" + } +}; + +static int __init qemu_trace_dev_init(void) +{ + int ret; + ret = platform_driver_register(&qemu_trace); + init_called = 1; + return ret; +} + +static void qemu_trace_dev_exit(void) +{ + platform_driver_unregister(&qemu_trace); +} + + +module_init(qemu_trace_dev_init); +module_exit(qemu_trace_dev_exit); + +MODULE_AUTHOR("Ye Wen (ywen@google.com)"); +MODULE_LICENSE("GPL"); diff -Nru linux-2.6.23/drivers/misc/qemutrace/qemu_trace.h kernel.android/drivers/misc/qemutrace/qemu_trace.h --- linux-2.6.23/drivers/misc/qemutrace/qemu_trace.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/misc/qemutrace/qemu_trace.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,5 @@ +// Shared functions between qemu_trace.c and qemu_trace_sysfs.c +void qemu_trace_start(void); +void qemu_trace_stop(void); +int qemu_trace_get_tracing(void); +void qemu_trace_dynamic_symbol(unsigned int addr, const char *symbol); diff -Nru linux-2.6.23/drivers/misc/qemutrace/qemu_trace_sysfs.c kernel.android/drivers/misc/qemutrace/qemu_trace_sysfs.c --- linux-2.6.23/drivers/misc/qemutrace/qemu_trace_sysfs.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/misc/qemutrace/qemu_trace_sysfs.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,149 @@ +/* drivers/misc/qemu_sysfs.c +** +** Copyright 2005-2006, Google Inc. +** Author: Jack Veenstra +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qemu_trace.h" + +MODULE_DESCRIPTION("Qemu Trace Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +decl_subsys(qemu_trace, NULL, NULL); + + +static ssize_t state_show(struct kset * subsys, char * buf) +{ + int val = qemu_trace_get_tracing(); + buf[0] = '0' + val; + buf[1] = '\n'; + return 2; +} + +static ssize_t state_store(struct kset * subsys, const char * buf, size_t n) +{ + if (n <= 0) + return -EINVAL; + if (buf[0] == '0') + qemu_trace_stop(); + else if (buf[0] == '1') + qemu_trace_start(); + else + return -EINVAL; + return n; +} + +static ssize_t symbol_show(struct kset * subsys, char * buf) +{ + return 0; +} + +// We are expecting a string of the form "addr symbol" where 'addr' is a hex address +// (without the leading '0x') and symbol is a newline-terminated string. This symbol +// with its corresponding address will be added to the trace file. +static ssize_t symbol_store(struct kset * subsys, const char * buf, size_t n) +{ + const char *cp; + unsigned int addr = 0; + int len; + char *sym; + + if (n <= 0 || buf == NULL) + return -EINVAL; + for (cp = buf; *cp != ' '; ++cp) { + unsigned int digit; + + if (*cp >= '0' && *cp <= '9') + digit = *cp - '0'; + else if (*cp >= 'a' && *cp <= 'f') + digit = *cp - 'a' + 10; + else + return -EINVAL; + addr = (addr << 4) + digit; + } + // Move past the space + cp += 1; + + // Copy the string to a new buffer so that we can replace the newline + // with '\0'. + len = strlen(cp); + sym = kzalloc(len + 1, GFP_KERNEL); + strcpy(sym, cp); + if (sym[len - 1] == '\n') + sym[len - 1] = 0; + + qemu_trace_dynamic_symbol(addr, sym); + kfree(sym); + return n; +} + + +#define qemu_trace_attr(_name) \ +static struct subsys_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ + .store = _name##_store, \ +} + +qemu_trace_attr(state); +qemu_trace_attr(symbol); + +static struct attribute * qemu_trace_attrs[] = { + &state_attr.attr, + &symbol_attr.attr, + NULL, +}; + +static struct attribute_group qemu_trace_attr_group = { + .attrs = qemu_trace_attrs, +}; + +static int __init qemu_trace_init(void) +{ + int ret; + + ret = subsystem_register(&qemu_trace_subsys); + if (ret) { + printk("qemu_trace_init: subsystem_register failed\n"); + return ret; + } + ret = sysfs_create_group(&qemu_trace_subsys.kobj, &qemu_trace_attr_group); + if (ret) { + printk("qemu_trace_init: sysfs_create_group failed\n"); + goto err; + } + + return 0; + +err: + subsystem_unregister(&qemu_trace_subsys); + return ret; +} + +static void __exit qemu_trace_exit(void) +{ + sysfs_remove_group(&qemu_trace_subsys.kobj,&qemu_trace_attr_group); + subsystem_unregister(&qemu_trace_subsys); +} + +core_initcall(qemu_trace_init); +module_exit(qemu_trace_exit); diff -Nru linux-2.6.23/drivers/mmc/host/Kconfig kernel.android/drivers/mmc/host/Kconfig --- linux-2.6.23/drivers/mmc/host/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/mmc/host/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -100,3 +100,10 @@ To compile this driver as a module, choose M here: the module will be called tifm_sd. +config MMC_GOLDFISH + tristate "goldfish qemu Multimedia Card Interface support" + depends on ARCH_GOLDFISH + help + This selects the Goldfish Multimedia card Interface emulation. + + If unsure, say N. diff -Nru linux-2.6.23/drivers/mmc/host/Makefile kernel.android/drivers/mmc/host/Makefile --- linux-2.6.23/drivers/mmc/host/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/mmc/host/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -15,4 +15,5 @@ obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o +obj-$(CONFIG_MMC_GOLDFISH) += goldfish.o diff -Nru linux-2.6.23/drivers/mmc/host/goldfish.c kernel.android/drivers/mmc/host/goldfish.c --- linux-2.6.23/drivers/mmc/host/goldfish.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/mmc/host/goldfish.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,577 @@ +/* + * linux/drivers/media/mmc/goldfish.c + * + * Copyright 2007, Google Inc. + * + * based on omap.c driver, which was + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen and Juha Yrjölä + * Misc hacks here and there by Tony Lindgren + * Other hacks (DMA, SD, etc) by David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * 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 +#include +#include +#include + +#include +#include +#include +#include + + +#include +#include +#include + +#define DRIVER_NAME "goldfish_mmc" + +#define BUFFER_SIZE 16384 + +#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr)) +#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr)) + + +enum { + /* status register */ + MMC_INT_STATUS = 0x00, + /* set this to enable IRQ */ + MMC_INT_ENABLE = 0x04, + /* set this to specify buffer address */ + MMC_SET_BUFFER = 0x08, + + /* MMC command number */ + MMC_CMD = 0x0C, + + /* MMC argument */ + MMC_ARG = 0x10, + + /* MMC response (or R2 bits 0 - 31) */ + MMC_RESP_0 = 0x14, + + /* MMC R2 response bits 32 - 63 */ + MMC_RESP_1 = 0x18, + + /* MMC R2 response bits 64 - 95 */ + MMC_RESP_2 = 0x1C, + + /* MMC R2 response bits 96 - 127 */ + MMC_RESP_3 = 0x20, + + MMC_BLOCK_LENGTH = 0x24, + MMC_BLOCK_COUNT = 0x28, + + /* MMC state flags */ + MMC_STATE = 0x2C, + + /* MMC_INT_STATUS bits */ + + MMC_STAT_END_OF_CMD = 1U << 0, + MMC_STAT_END_OF_DATA = 1U << 1, + MMC_STAT_STATE_CHANGE = 1U << 2, + + /* MMC_STATE bits */ + MMC_STATE_INSERTED = 1U << 0, + MMC_STATE_READ_ONLY = 1U << 1, +}; + +/* + * Command types + */ +#define OMAP_MMC_CMDTYPE_BC 0 +#define OMAP_MMC_CMDTYPE_BCR 1 +#define OMAP_MMC_CMDTYPE_AC 2 +#define OMAP_MMC_CMDTYPE_ADTC 3 + + +struct goldfish_mmc_host { + struct mmc_request * mrq; + struct mmc_command * cmd; + struct mmc_data * data; + struct mmc_host * mmc; + struct device * dev; + unsigned char id; /* 16xx chips have 2 MMC blocks */ + void __iomem *virt_base; + unsigned int phys_base; + int irq; + unsigned char bus_mode; + unsigned char hw_bus_mode; + + unsigned int sg_len; + unsigned dma_done:1; + unsigned dma_in_use:1; + + struct work_struct switch_work; + int switch_last_state; + + uint32_t reg_base; +}; + +static inline int +goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host) +{ + return 0; +} + +static ssize_t +goldfish_mmc_show_cover_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goldfish_mmc_host *host = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" : + "closed"); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL); + +static void +goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd) +{ + u32 cmdreg; + u32 resptype; + u32 cmdtype; + + host->cmd = cmd; + + resptype = 0; + cmdtype = 0; + + /* Our hardware needs to know exact type */ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + break; + case MMC_RSP_R1: + case MMC_RSP_R1B: + /* resp 1, 1b, 6, 7 */ + resptype = 1; + break; + case MMC_RSP_R2: + resptype = 2; + break; + case MMC_RSP_R3: + resptype = 3; + break; + default: + dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd)); + break; + } + + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { + cmdtype = OMAP_MMC_CMDTYPE_ADTC; + } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) { + cmdtype = OMAP_MMC_CMDTYPE_BC; + } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) { + cmdtype = OMAP_MMC_CMDTYPE_BCR; + } else { + cmdtype = OMAP_MMC_CMDTYPE_AC; + } + + cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdreg |= 1 << 6; + + if (cmd->flags & MMC_RSP_BUSY) + cmdreg |= 1 << 11; + + if (host->data && !(host->data->flags & MMC_DATA_WRITE)) + cmdreg |= 1 << 15; + + GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg); + GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg); +} + +static void +goldfish_mmc_xfer_done(struct goldfish_mmc_host *host, struct mmc_data *data) +{ + if (host->dma_in_use) { + enum dma_data_direction dma_data_dir; + + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + + if (dma_data_dir == DMA_FROM_DEVICE) { + // we don't really have DMA, so we need to copy from our platform driver buffer + uint8_t* dest = (uint8_t *)__phys_to_virt(page_to_phys(data->sg->page)) + data->sg->offset; + memcpy(dest, host->virt_base, data->sg->length); + } + + host->data->bytes_xfered += data->sg->length; + + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, dma_data_dir); + } + + host->data = NULL; + host->sg_len = 0; + + /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing + * dozens of requests until the card finishes writing data. + * It'd be cheaper to just wait till an EOFB interrupt arrives... + */ + + if (!data->stop) { + host->mrq = NULL; + mmc_request_done(host->mmc, data->mrq); + return; + } + + goldfish_mmc_start_command(host, data->stop); +} + +static void +goldfish_mmc_end_of_data(struct goldfish_mmc_host *host, struct mmc_data *data) +{ + if (!host->dma_in_use) { + goldfish_mmc_xfer_done(host, data); + return; + } + if (host->dma_done) + goldfish_mmc_xfer_done(host, data); +} + +static void +goldfish_mmc_cmd_done(struct goldfish_mmc_host *host, struct mmc_command *cmd) +{ + host->cmd = NULL; + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + /* response type 2 */ + cmd->resp[3] = + GOLDFISH_MMC_READ(host, MMC_RESP_0); + cmd->resp[2] = + GOLDFISH_MMC_READ(host, MMC_RESP_1); + cmd->resp[1] = + GOLDFISH_MMC_READ(host, MMC_RESP_2); + cmd->resp[0] = + GOLDFISH_MMC_READ(host, MMC_RESP_3); + } else { + /* response types 1, 1b, 3, 4, 5, 6 */ + cmd->resp[0] = + GOLDFISH_MMC_READ(host, MMC_RESP_0); + } + } + + if (host->data == NULL || cmd->error != MMC_ERR_NONE) { + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); + } +} + +static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id) +{ + struct goldfish_mmc_host * host = (struct goldfish_mmc_host *)dev_id; + u16 status; + int end_command; + int end_transfer; + int transfer_error; + int state_changed; + + if (host->cmd == NULL && host->data == NULL) { + status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS); + dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); + if (status != 0) { + GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status); + GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0); + } + return IRQ_HANDLED; + } + + end_command = 0; + end_transfer = 0; + transfer_error = 0; + state_changed = 0; + + while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) { + GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status); + + if (status & MMC_STAT_END_OF_CMD) { + end_command = 1; + } + + if (status & MMC_STAT_END_OF_DATA) { + end_transfer = 1; + } + if (status & MMC_STAT_STATE_CHANGE) { + state_changed = 1; + } + } + + if (end_command) { + goldfish_mmc_cmd_done(host, host->cmd); + } + if (transfer_error) + goldfish_mmc_xfer_done(host, host->data); + else if (end_transfer) { + host->dma_done = 1; + goldfish_mmc_end_of_data(host, host->data); + } + if (state_changed) { + schedule_work(&host->switch_work); + } + + return IRQ_HANDLED; +} + + +static void goldfish_mmc_switch_handler(struct work_struct *work) +{ +/* + struct goldfish_mmc_host *host = container_of(work, struct goldfish_mmc_host, switch_work); + struct mmc_card *card; + static int complained = 0; + int cards = 0, cover_open; + + cover_open = goldfish_mmc_cover_is_open(host); + if (cover_open != host->switch_last_state) { + kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); + host->switch_last_state = cover_open; + } + mmc_detect_change(host->mmc, 0); + list_for_each_entry(card, &host->mmc->cards, node) { + if (mmc_card_present(card)) + cards++; + } + if (goldfish_mmc_cover_is_open(host)) { + if (!complained) { + dev_info(mmc_dev(host->mmc), "cover is open\n"); + complained = 1; + } + } else { + complained = 0; + } +*/ +} + + +static void +goldfish_mmc_prepare_data(struct goldfish_mmc_host *host, struct mmc_request *req) +{ + struct mmc_data *data = req->data; + int block_size; + unsigned sg_len; + enum dma_data_direction dma_data_dir; + + host->data = data; + if (data == NULL) { + GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0); + GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0); + host->dma_in_use = 0; + return; + } + + block_size = data->blksz; + + GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1); + GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1); + + /* cope with calling layer confusion; it issues "single + * block" writes using multi-block scatterlists. + */ + sg_len = (data->blocks == 1) ? 1 : data->sg_len; + + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + + host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + sg_len, dma_data_dir); + host->dma_done = 0; + host->dma_in_use = 1; + + if (dma_data_dir == DMA_TO_DEVICE) { + // we don't really have DMA, so we need to copy to our platform driver buffer + const uint8_t* src = (uint8_t *)__phys_to_virt(page_to_phys(data->sg->page)) + data->sg->offset; + memcpy(host->virt_base, src, data->sg->length); + } +} + +static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct goldfish_mmc_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + + host->mrq = req; + goldfish_mmc_prepare_data(host, req); + goldfish_mmc_start_command(host, req->cmd); +} + +static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct goldfish_mmc_host *host = mmc_priv(mmc); + + host->bus_mode = ios->bus_mode; + host->hw_bus_mode = host->bus_mode; +} + +static int goldfish_mmc_get_ro(struct mmc_host *mmc) +{ + uint32_t state; + struct goldfish_mmc_host *host = mmc_priv(mmc); + + state = GOLDFISH_MMC_READ(host, MMC_STATE); + return ((state & MMC_STATE_READ_ONLY) != 0); +} + +static const struct mmc_host_ops goldfish_mmc_ops = { + .request = goldfish_mmc_request, + .set_ios = goldfish_mmc_set_ios, + .get_ro = goldfish_mmc_get_ro, +}; + +static int __init goldfish_mmc_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct goldfish_mmc_host *host = NULL; + struct resource *res; + int ret = 0; + int irq; + dma_addr_t buf_addr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (res == NULL || irq < 0) + return -ENXIO; + + mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev); + if (mmc == NULL) { + ret = -ENOMEM; + goto err_alloc_host_failed; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + host->reg_base = IO_ADDRESS(res->start - IO_START); + host->virt_base = dma_alloc_writecombine(&pdev->dev, BUFFER_SIZE, + &buf_addr, GFP_KERNEL); + if(host->virt_base == 0) { + ret = -EBUSY; + goto dma_alloc_failed; + } + host->phys_base = buf_addr; + + host->id = pdev->id; + host->irq = irq; + + mmc->ops = &goldfish_mmc_ops; + mmc->f_min = 400000; + mmc->f_max = 24000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + mmc->caps |= MMC_CAP_4_BIT_DATA; + + /* Use scatterlist DMA to reduce per-transfer costs. + * NOTE max_seg_size assumption that small blocks aren't + * normally used (except e.g. for reading SD registers). + */ + mmc->max_phys_segs = 32; + mmc->max_hw_segs = 32; + mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */ + mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */ + mmc->max_req_size = BUFFER_SIZE; + mmc->max_seg_size = mmc->max_req_size; + + ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host); + if (ret) + goto err_request_irq_failed; + + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + + ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); + if (ret) + dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); + + mmc_add_host(mmc); + + GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base); + GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, + MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA | MMC_STAT_STATE_CHANGE + ); + + // we start with the card present + kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); + mmc_detect_change(host->mmc, 0); + + INIT_WORK(&host->switch_work, goldfish_mmc_switch_handler); + + return 0; + +err_request_irq_failed: + dma_free_writecombine(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base); +dma_alloc_failed: + mmc_free_host(host->mmc); +err_alloc_host_failed: + return ret; +} + +static int goldfish_mmc_remove(struct platform_device *pdev) +{ + struct goldfish_mmc_host *host = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + BUG_ON(host == NULL); + + mmc_remove_host(host->mmc); + free_irq(host->irq, host); + dma_free_writecombine(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base); + mmc_free_host(host->mmc); + + return 0; +} + +static struct platform_driver goldfish_mmc_driver = { + .probe = goldfish_mmc_probe, + .remove = goldfish_mmc_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init goldfish_mmc_init(void) +{ + return platform_driver_register(&goldfish_mmc_driver); +} + +static void __exit goldfish_mmc_exit(void) +{ + platform_driver_unregister(&goldfish_mmc_driver); +} + +module_init(goldfish_mmc_init); +module_exit(goldfish_mmc_exit); + diff -Nru linux-2.6.23/drivers/mtd/devices/Kconfig kernel.android/drivers/mtd/devices/Kconfig --- linux-2.6.23/drivers/mtd/devices/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/mtd/devices/Kconfig 2007-11-12 07:49:02.000000000 +1100 @@ -269,5 +269,10 @@ LinuxBIOS or if you need to recover a DiskOnChip Millennium on which you have managed to wipe the first block. +config MTD_GOLDFISH_NAND + tristate "Goldfish NAND device" + help + none + endmenu diff -Nru linux-2.6.23/drivers/mtd/devices/Makefile kernel.android/drivers/mtd/devices/Makefile --- linux-2.6.23/drivers/mtd/devices/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/mtd/devices/Makefile 2007-11-12 07:49:02.000000000 +1100 @@ -18,3 +18,4 @@ obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o obj-$(CONFIG_MTD_M25P80) += m25p80.o +obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o diff -Nru linux-2.6.23/drivers/mtd/devices/goldfish_nand.c kernel.android/drivers/mtd/devices/goldfish_nand.c --- linux-2.6.23/drivers/mtd/devices/goldfish_nand.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/mtd/devices/goldfish_nand.c 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,422 @@ +/* drivers/mtd/devices/goldfish_nand.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 +#include + +#include "goldfish_nand_reg.h" + +struct goldfish_nand { + spinlock_t lock; + unsigned char __iomem *base; + size_t mtd_count; + struct mtd_info mtd[0]; +}; + +static uint32_t goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd, + uint64_t addr, uint32_t len, void *ptr) +{ + struct goldfish_nand *nand = mtd->priv; + uint32_t rv; + unsigned long irq_flags; + unsigned char __iomem *base = nand->base; + + spin_lock_irqsave(&nand->lock, irq_flags); + writel(mtd - nand->mtd, base + NAND_DEV); + writel((uint32_t)(addr >> 32), base + NAND_ADDR_HIGH); + writel((uint32_t)addr, base + NAND_ADDR_LOW); + writel(len, base + NAND_TRANSFER_SIZE); + writel(ptr, base + NAND_DATA); + writel(cmd, base + NAND_COMMAND); + rv = readl(base + NAND_RESULT); + spin_unlock_irqrestore(&nand->lock, irq_flags); + return rv; +} + +static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + loff_t ofs = instr->addr; + uint32_t len = instr->len; + uint32_t rem; + + if (ofs + len > mtd->size) + goto invalid_arg; + rem = do_div(ofs, mtd->writesize); + if(rem) + goto invalid_arg; + ofs *= (mtd->writesize + mtd->oobsize); + + if(len % mtd->writesize) + goto invalid_arg; + len = len / mtd->writesize * (mtd->writesize + mtd->oobsize); + + if(goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) { + printk("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size " + "%x, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize); + return -EIO; + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; + +invalid_arg: + printk("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size " + "%x, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize); + return -EINVAL; +} + +static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) +{ + uint32_t rem; + + if(ofs + ops->len > mtd->size) + goto invalid_arg; + if(ops->datbuf && ops->len && ops->len != mtd->writesize) + goto invalid_arg; + if(ops->ooblen + ops->ooboffs > mtd->oobsize) + goto invalid_arg; + + rem = do_div(ofs, mtd->writesize); + if(rem) + goto invalid_arg; + ofs *= (mtd->writesize + mtd->oobsize); + + if(ops->datbuf) + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs, + ops->len, ops->datbuf); + ofs += mtd->writesize + ops->ooboffs; + if(ops->oobbuf) + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs, + ops->ooblen, ops->oobbuf); + return 0; + +invalid_arg: + printk("goldfish_nand_read_oob: invalid read, start %llx, len %x, " + "ooblen %x, dev_size %x, write_size %x\n", + ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize); + return -EINVAL; +} + +static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) +{ + uint32_t rem; + + if(ofs + ops->len > mtd->size) + goto invalid_arg; + if(ops->len && ops->len != mtd->writesize) + goto invalid_arg; + if(ops->ooblen + ops->ooboffs > mtd->oobsize) + goto invalid_arg; + + rem = do_div(ofs, mtd->writesize); + if(rem) + goto invalid_arg; + ofs *= (mtd->writesize + mtd->oobsize); + + if(ops->datbuf) + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs, + ops->len, ops->datbuf); + ofs += mtd->writesize + ops->ooboffs; + if(ops->oobbuf) + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs, + ops->ooblen, ops->oobbuf); + return 0; + +invalid_arg: + printk("goldfish_nand_write_oob: invalid write, start %llx, len %x, " + "ooblen %x, dev_size %x, write_size %x\n", + ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize); + return -EINVAL; +} + +static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + uint32_t rem; + + if(from + len > mtd->size) + goto invalid_arg; + if(len != mtd->writesize) + goto invalid_arg; + + rem = do_div(from, mtd->writesize); + if(rem) + goto invalid_arg; + from *= (mtd->writesize + mtd->oobsize); + + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf); + return 0; + +invalid_arg: + printk("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %x" + ", write_size %x\n", from, len, mtd->size, mtd->writesize); + return -EINVAL; +} + +static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + uint32_t rem; + + if(to + len > mtd->size) + goto invalid_arg; + if(len != mtd->writesize) + goto invalid_arg; + + rem = do_div(to, mtd->writesize); + if(rem) + goto invalid_arg; + to *= (mtd->writesize + mtd->oobsize); + + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf); + return 0; + +invalid_arg: + printk("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %x" + ", write_size %x\n", to, len, mtd->size, mtd->writesize); + return -EINVAL; +} + +static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + uint32_t rem; + + if(ofs >= mtd->size) + goto invalid_arg; + + rem = do_div(ofs, mtd->erasesize); + if(rem) + goto invalid_arg; + ofs *= mtd->erasesize / mtd->writesize; + ofs *= (mtd->writesize + mtd->oobsize); + + return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL); + +invalid_arg: + printk("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %x, " + "write_size %x\n", ofs, mtd->size, mtd->writesize); + return -EINVAL; +} + +static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + uint32_t rem; + + if(ofs >= mtd->size) + goto invalid_arg; + + rem = do_div(ofs, mtd->erasesize); + if(rem) + goto invalid_arg; + ofs *= mtd->erasesize / mtd->writesize; + ofs *= (mtd->writesize + mtd->oobsize); + + if(goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1) + return -EIO; + return 0; + +invalid_arg: + printk("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %x, " + "write_size %x\n", ofs, mtd->size, mtd->writesize); + return -EINVAL; +} + +static int goldfish_nand_init_device(struct goldfish_nand *nand, int id) +{ + uint32_t dev_size_high; + uint32_t name_len; + uint32_t result; + uint32_t flags; + unsigned long irq_flags; + unsigned char __iomem *base = nand->base; + struct mtd_info *mtd = &nand->mtd[id]; + + spin_lock_irqsave(&nand->lock, irq_flags); + writel(id, base + NAND_DEV); + flags = readl(base + NAND_DEV_FLAGS); + name_len = readl(base + NAND_DEV_NAME_LEN); + mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE); + mtd->size = readl(base + NAND_DEV_SIZE_LOW); + mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE); + mtd->oobavail = mtd->oobsize; + mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) / + (mtd->writesize + mtd->oobsize) * mtd->writesize; + mtd->size = mtd->size / (mtd->writesize + mtd->oobsize) * mtd->writesize; + dev_size_high = readl(base + NAND_DEV_SIZE_HIGH); + printk("goldfish nand dev%d: size %x, page %d, extra %d, erase %d\n", + id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize); + spin_unlock_irqrestore(&nand->lock, irq_flags); + + if(dev_size_high) { + printk("goldfish_nand_init_device device to big 0x%08x%08x\n", + dev_size_high, mtd->size); + return -ENODEV; + } + mtd->priv = nand; + + mtd->name = kmalloc(name_len + 1, GFP_KERNEL); + if(mtd->name == NULL) + return -ENOMEM; + + result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, mtd->name); + if(result != name_len) { + kfree(mtd->name); + mtd->name = NULL; + printk("goldfish_nand_init_device failed to get dev name %d != %d\n", + result, name_len); + return -ENODEV; + } + mtd->name[name_len] = '\0'; + + /* Setup the MTD structure */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + if(flags & NAND_DEV_FLAG_READ_ONLY) + mtd->flags &= ~MTD_WRITEABLE; + + mtd->owner = THIS_MODULE; + mtd->erase = goldfish_nand_erase; + mtd->read = goldfish_nand_read; + mtd->write = goldfish_nand_write; + mtd->read_oob = goldfish_nand_read_oob; + mtd->write_oob = goldfish_nand_write_oob; + mtd->block_isbad = goldfish_nand_block_isbad; + mtd->block_markbad = goldfish_nand_block_markbad; + + if (add_mtd_device(mtd)) { + kfree(mtd->name); + mtd->name = NULL; + return -EIO; + } + + return 0; +} + +static int goldfish_nand_probe(struct platform_device *pdev) +{ + uint32_t num_dev; + int i; + int err; + uint32_t num_dev_working; + uint32_t version; + struct resource *r; + struct goldfish_nand *nand; + unsigned char __iomem *base; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) { + err = -ENODEV; + goto err_no_io_base; + } + + base = ioremap(r->start, PAGE_SIZE); + if(base == NULL) { + err = -ENOMEM; + goto err_ioremap; + } + version = readl(base + NAND_VERSION); + if(version != NAND_VERSION_CURRENT) { + printk("goldfish_nand_init: version mismatch, got %d, expected %d\n", + version, NAND_VERSION_CURRENT); + err = -ENODEV; + goto err_no_dev; + } + num_dev = readl(base + NAND_NUM_DEV); + if(num_dev == 0) { + err = -ENODEV; + goto err_no_dev; + } + + nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL); + if(nand == NULL) { + err = -ENOMEM; + goto err_nand_alloc_failed; + } + spin_lock_init(&nand->lock); + nand->base = base; + nand->mtd_count = num_dev; + platform_set_drvdata(pdev, nand); + + num_dev_working = 0; + for(i = 0; i < num_dev; i++) { + err = goldfish_nand_init_device(nand, i); + if(err == 0) + num_dev_working++; + } + if(num_dev_working == 0) { + err = -ENODEV; + goto err_no_working_dev; + } + return 0; + +err_no_working_dev: + kfree(nand); +err_nand_alloc_failed: +err_no_dev: + iounmap(base); +err_ioremap: +err_no_io_base: + return err; +} + +static int goldfish_nand_remove(struct platform_device *pdev) +{ + struct goldfish_nand *nand = platform_get_drvdata(pdev); + int i; + for(i = 0; i < nand->mtd_count; i++) { + if(nand->mtd[i].name) { + del_mtd_device(&nand->mtd[i]); + kfree(nand->mtd[i].name); + } + } + iounmap(nand->base); + kfree(nand); + return 0; +} + +static struct platform_driver goldfish_nand_driver = { + .probe = goldfish_nand_probe, + .remove = goldfish_nand_remove, + .driver = { + .name = "goldfish_nand" + } +}; + +static int __init goldfish_nand_init(void) +{ + return platform_driver_register(&goldfish_nand_driver); +} + +static void __exit goldfish_nand_exit(void) +{ + platform_driver_unregister(&goldfish_nand_driver); +} + + +module_init(goldfish_nand_init); +module_exit(goldfish_nand_exit); + diff -Nru linux-2.6.23/drivers/mtd/devices/goldfish_nand_reg.h kernel.android/drivers/mtd/devices/goldfish_nand_reg.h --- linux-2.6.23/drivers/mtd/devices/goldfish_nand_reg.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/mtd/devices/goldfish_nand_reg.h 2007-11-12 07:49:02.000000000 +1100 @@ -0,0 +1,58 @@ +/* drivers/mtd/devices/goldfish_nand_reg.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef GOLDFISH_NAND_REG_H +#define GOLDFISH_NAND_REG_H + +enum nand_cmd { + NAND_CMD_GET_DEV_NAME, // Write device name for NAND_DEV to NAND_DATA (vaddr) + NAND_CMD_READ, + NAND_CMD_WRITE, + NAND_CMD_ERASE, + NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not + NAND_CMD_BLOCK_BAD_SET +}; + +enum nand_dev_flags { + NAND_DEV_FLAG_READ_ONLY = 0x00000001 +}; + +#define NAND_VERSION_CURRENT (1) + +enum nand_reg { + // Global + NAND_VERSION = 0x000, + NAND_NUM_DEV = 0x004, + NAND_DEV = 0x008, + + // Dev info + NAND_DEV_FLAGS = 0x010, + NAND_DEV_NAME_LEN = 0x014, + NAND_DEV_PAGE_SIZE = 0x018, + NAND_DEV_EXTRA_SIZE = 0x01c, + NAND_DEV_ERASE_SIZE = 0x020, + NAND_DEV_SIZE_LOW = 0x028, + NAND_DEV_SIZE_HIGH = 0x02c, + + // Command + NAND_RESULT = 0x040, + NAND_COMMAND = 0x044, + NAND_DATA = 0x048, + NAND_TRANSFER_SIZE = 0x04c, + NAND_ADDR_LOW = 0x050, + NAND_ADDR_HIGH = 0x054, +}; + +#endif diff -Nru linux-2.6.23/drivers/rtc/Kconfig kernel.android/drivers/rtc/Kconfig --- linux-2.6.23/drivers/rtc/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/rtc/Kconfig 2007-11-12 07:49:03.000000000 +1100 @@ -448,4 +448,10 @@ help If you say yes here you get support for the Ricoh RS5C313 RTC chips. +config RTC_DRV_GOLDFISH + tristate "GOLDFISH" + depends on RTC_CLASS + help + RTC driver for Goldfish Virtual Platform + endif # RTC_CLASS diff -Nru linux-2.6.23/drivers/rtc/Makefile kernel.android/drivers/rtc/Makefile --- linux-2.6.23/drivers/rtc/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/rtc/Makefile 2007-11-12 07:49:03.000000000 +1100 @@ -27,6 +27,7 @@ obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o +obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o diff -Nru linux-2.6.23/drivers/rtc/rtc-goldfish.c kernel.android/drivers/rtc/rtc-goldfish.c --- linux-2.6.23/drivers/rtc/rtc-goldfish.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/rtc/rtc-goldfish.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,138 @@ +/* drivers/rtc/rtc-goldfish.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + +struct goldfish_rtc { + uint32_t base; + uint32_t irq; + struct rtc_device *rtc; +}; + +static irqreturn_t +goldfish_rtc_interrupt(int irq, void *dev_id) +{ + struct goldfish_rtc *qrtc = dev_id; + unsigned long events = 0; + + writel(1, qrtc->base + TIMER_CLEAR_INTERRUPT); + events = RTC_IRQF | RTC_AF; + + rtc_update_irq(qrtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + int64_t time; + struct goldfish_rtc *qrtc = platform_get_drvdata(to_platform_device(dev)); + + time = readl(qrtc->base + TIMER_TIME_LOW); + time |= (int64_t)readl(qrtc->base + TIMER_TIME_HIGH) << 32; + do_div(time, NSEC_PER_SEC); + + rtc_time_to_tm(time, tm); + return 0; +} + +static struct rtc_class_ops goldfish_rtc_ops = { +// .ioctl = goldfish_rtc_ioctl, + .read_time = goldfish_rtc_read_time, +// .set_time = goldfish_rtc_set_time, +// .read_alarm = goldfish_rtc_read_alarm, +// .set_alarm = goldfish_rtc_set_alarm, +}; + + +static int goldfish_rtc_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct goldfish_rtc *qrtc; + + qrtc = kzalloc(sizeof(*qrtc), GFP_KERNEL); + if(qrtc == NULL) { + ret = -ENOMEM; + goto err_qrtc_alloc_failed; + } + platform_set_drvdata(pdev, qrtc); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) { + ret = -ENODEV; + goto err_no_io_base; + } + qrtc->base = IO_ADDRESS(r->start - IO_START); + qrtc->irq = platform_get_irq(pdev, 0); + if(qrtc->irq < 0) { + ret = -ENODEV; + goto err_no_irq; + } + qrtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &goldfish_rtc_ops, THIS_MODULE); + if (IS_ERR(qrtc->rtc)) { + ret = PTR_ERR(qrtc->rtc); + goto err_rtc_device_register_failed; + } + + ret = request_irq(qrtc->irq, goldfish_rtc_interrupt, 0, pdev->name, qrtc); + if(ret) + goto request_irq; + + return 0; + + free_irq(qrtc->irq, qrtc); +request_irq: + rtc_device_unregister(qrtc->rtc); +err_rtc_device_register_failed: +err_no_irq: +err_no_io_base: + kfree(qrtc); +err_qrtc_alloc_failed: + return ret; +} + +static int goldfish_rtc_remove(struct platform_device *pdev) +{ + struct goldfish_rtc *qrtc = platform_get_drvdata(pdev); + free_irq(qrtc->irq, qrtc); + rtc_device_unregister(qrtc->rtc); + kfree(qrtc); + return 0; +} + +static struct platform_driver goldfish_timer = { + .probe = goldfish_rtc_probe, + .remove = goldfish_rtc_remove, + .driver = { + .name = "goldfish_rtc" + } +}; + +static int __init goldfish_rtc_init(void) +{ + return platform_driver_register(&goldfish_timer); +} + +module_init(goldfish_rtc_init); + diff -Nru linux-2.6.23/drivers/usb/gadget/Makefile kernel.android/drivers/usb/gadget/Makefile --- linux-2.6.23/drivers/usb/gadget/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/usb/gadget/Makefile 2007-11-12 07:49:03.000000000 +1100 @@ -28,6 +28,9 @@ g_file_storage-objs := file_storage.o usbstring.o config.o \ epautoconf.o +# needed for drivers/android/android_gadget.c +obj-$(CONFIG_ANDROID_GADGET) += config.o epautoconf.o usbstring.o + ifeq ($(CONFIG_USB_ETH_RNDIS),y) g_ether-objs += rndis.o endif diff -Nru linux-2.6.23/drivers/video/Kconfig kernel.android/drivers/video/Kconfig --- linux-2.6.23/drivers/video/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/video/Kconfig 2007-11-12 07:49:03.000000000 +1100 @@ -1837,6 +1837,15 @@ source "drivers/video/omap/Kconfig" endif +config FB_GOLDFISH + tristate "Goldfish Framebuffer + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Framebuffer driver for Goldfish Virtual Platform + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff -Nru linux-2.6.23/drivers/video/Makefile kernel.android/drivers/video/Makefile --- linux-2.6.23/drivers/video/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/drivers/video/Makefile 2007-11-12 07:49:03.000000000 +1100 @@ -91,6 +91,7 @@ obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o +obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o diff -Nru linux-2.6.23/drivers/video/goldfishfb.c kernel.android/drivers/video/goldfishfb.c --- linux-2.6.23/drivers/video/goldfishfb.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/drivers/video/goldfishfb.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,303 @@ +/* drivers/video/goldfishfb.c +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 +#include +#include +#include +#include + +#include + +enum { + FB_GET_WIDTH = 0x00, + FB_GET_HEIGHT = 0x04, + FB_INT_STATUS = 0x08, + FB_INT_ENABLE = 0x0c, + FB_SET_BASE = 0x10, + FB_SET_ROTATION = 0x14, + + FB_INT_VSYNC = 1U << 0, + FB_INT_BASE_UPDATE_DONE = 1U << 1 +}; + +struct goldfish_fb { + uint32_t reg_base; + int irq; + spinlock_t lock; + wait_queue_head_t wait; + int base_update_count; + int rotation; + struct fb_info fb; + u32 cmap[16]; +}; + +static irqreturn_t +goldfish_fb_interrupt(int irq, void *dev_id) +{ + unsigned long irq_flags; + struct goldfish_fb *fb = dev_id; + uint32_t status; + + spin_lock_irqsave(&fb->lock, irq_flags); + status = readl(fb->reg_base + FB_INT_STATUS); + if(status & FB_INT_BASE_UPDATE_DONE) { + fb->base_update_count++; + wake_up(&fb->wait); + } + spin_unlock_irqrestore(&fb->lock, irq_flags); + return status ? IRQ_HANDLED : IRQ_NONE; +} + +static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) +{ + unsigned int mask = (1 << bf->length) - 1; + + return (val >> (16 - bf->length) & mask) << bf->offset; +} + +static int +goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + + if (regno < 16) { + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | + convert_bitfield(blue, &fb->fb.var.blue) | + convert_bitfield(green, &fb->fb.var.green) | + convert_bitfield(red, &fb->fb.var.red); + return 0; + } + else { + return 1; + } +} + +static int goldfish_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + if((var->rotate & 1) != (info->var.rotate & 1)) { + if((var->xres != info->var.yres) || + (var->yres != info->var.xres) || + (var->xres_virtual != info->var.yres) || + (var->yres_virtual > info->var.xres * 2) || + (var->yres_virtual < info->var.xres )) { + return -EINVAL; + } + } + else { + if((var->xres != info->var.xres) || + (var->yres != info->var.yres) || + (var->xres_virtual != info->var.xres) || + (var->yres_virtual > info->var.yres * 2) || + (var->yres_virtual < info->var.yres )) { + return -EINVAL; + } + } + if((var->xoffset != info->var.xoffset) || + (var->bits_per_pixel != info->var.bits_per_pixel) || + (var->grayscale != info->var.grayscale)) { + return -EINVAL; + } + return 0; +} + +static int goldfish_fb_set_par(struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + if(fb->rotation != fb->fb.var.rotate) { + info->fix.line_length = info->var.xres * 2; + fb->rotation = fb->fb.var.rotate; + writel(fb->rotation, fb->reg_base + FB_SET_ROTATION); + } + return 0; +} + + +static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + unsigned long irq_flags; + int base_update_count; + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + + spin_lock_irqsave(&fb->lock, irq_flags); + base_update_count = fb->base_update_count; + writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, fb->reg_base + FB_SET_BASE); + spin_unlock_irqrestore(&fb->lock, irq_flags); + wait_event_timeout(fb->wait, fb->base_update_count != base_update_count, HZ / 15); + if(fb->base_update_count == base_update_count) + printk("goldfish_fb_pan_display: timeout wating for base update\n"); + return 0; +} + + +static struct fb_ops goldfish_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = goldfish_fb_check_var, + .fb_set_par = goldfish_fb_set_par, + .fb_setcolreg = goldfish_fb_setcolreg, + .fb_pan_display = goldfish_fb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + + +static int goldfish_fb_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct goldfish_fb *fb; + size_t framesize; + uint32_t width, height; + dma_addr_t fbpaddr; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if(fb == NULL) { + ret = -ENOMEM; + goto err_fb_alloc_failed; + } + spin_lock_init(&fb->lock); + init_waitqueue_head(&fb->wait); + platform_set_drvdata(pdev, fb); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(r == NULL) { + ret = -ENODEV; + goto err_no_io_base; + } + fb->reg_base = IO_ADDRESS(r->start - IO_START); + + fb->irq = platform_get_irq(pdev, 0); + if(fb->irq < 0) { + ret = -ENODEV; + goto err_no_irq; + } + + width = readl(fb->reg_base + FB_GET_WIDTH); + height = readl(fb->reg_base + FB_GET_HEIGHT); + + fb->fb.fbops = &goldfish_fb_ops; + fb->fb.flags = FBINFO_FLAG_DEFAULT; + fb->fb.pseudo_palette = fb->cmap; + //strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); + fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + fb->fb.fix.line_length = width * 2; + fb->fb.fix.accel = FB_ACCEL_NONE; + fb->fb.fix.ypanstep = 1; + + fb->fb.var.xres = width; + fb->fb.var.yres = height; + fb->fb.var.xres_virtual = width; + fb->fb.var.yres_virtual = height * 2; + fb->fb.var.bits_per_pixel = 16; + fb->fb.var.activate = FB_ACTIVATE_NOW; + fb->fb.var.height = height; + fb->fb.var.width = width; + + fb->fb.var.red.offset = 11; + fb->fb.var.red.length = 5; + fb->fb.var.green.offset = 5; + fb->fb.var.green.length = 6; + fb->fb.var.blue.offset = 0; + fb->fb.var.blue.length = 5; + + framesize = width * height * 2 * 2; + fb->fb.screen_base = dma_alloc_writecombine(&pdev->dev, framesize, + &fbpaddr, GFP_KERNEL); + printk("allocating frame buffer %d * %d, got %p\n", width, height, fb->fb.screen_base); + if(fb->fb.screen_base == 0) { + ret = -ENOMEM; + goto err_alloc_screen_base_failed; + } + fb->fb.fix.smem_start = fbpaddr; + fb->fb.fix.smem_len = framesize; + + ret = fb_set_var(&fb->fb, &fb->fb.var); + if(ret) + goto err_fb_set_var_failed; + + ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb); + if(ret) + goto err_request_irq_failed; + + writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE); + goldfish_fb_pan_display(&fb->fb.var, &fb->fb); // updates base + + ret = register_framebuffer(&fb->fb); + if(ret) + goto err_register_framebuffer_failed; + + return 0; + + +err_register_framebuffer_failed: + free_irq(fb->irq, fb); +err_request_irq_failed: +err_fb_set_var_failed: + dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start); +err_alloc_screen_base_failed: +err_no_irq: +err_no_io_base: + kfree(fb); +err_fb_alloc_failed: + return ret; +} + +static int goldfish_fb_remove(struct platform_device *pdev) +{ + size_t framesize; + struct goldfish_fb *fb = platform_get_drvdata(pdev); + + framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2; + + unregister_framebuffer(&fb->fb); + free_irq(fb->irq, fb); + dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start); + kfree(fb); + return 0; +} + + +static struct platform_driver goldfish_fb_driver = { + .probe = goldfish_fb_probe, + .remove = goldfish_fb_remove, + .driver = { + .name = "goldfish_fb" + } +}; + +static int __init goldfish_fb_init(void) +{ + return platform_driver_register(&goldfish_fb_driver); +} + +static void __exit goldfish_fb_exit(void) +{ + platform_driver_unregister(&goldfish_fb_driver); +} + +module_init(goldfish_fb_init); +module_exit(goldfish_fb_exit); + diff -Nru linux-2.6.23/fs/Kconfig kernel.android/fs/Kconfig --- linux-2.6.23/fs/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/fs/Kconfig 2007-11-12 07:49:03.000000000 +1100 @@ -1182,6 +1182,10 @@ To compile the EFS file system support as a module, choose M here: the module will be called efs. + +# Patched by YAFFS +source "fs/yaffs2/Kconfig" + config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" select CRC32 diff -Nru linux-2.6.23/fs/Makefile kernel.android/fs/Makefile --- linux-2.6.23/fs/Makefile 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/fs/Makefile 2007-11-12 07:49:03.000000000 +1100 @@ -118,3 +118,6 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_GFS2_FS) += gfs2/ + +# Patched by YAFFS +obj-$(CONFIG_YAFFS_FS) += yaffs2/ diff -Nru linux-2.6.23/fs/exec.c kernel.android/fs/exec.c --- linux-2.6.23/fs/exec.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/fs/exec.c 2007-11-12 07:49:03.000000000 +1100 @@ -1331,6 +1331,11 @@ EXPORT_SYMBOL(search_binary_handler); +#ifdef CONFIG_QEMU_TRACE +/* -- wen */ +extern void qemu_trace_execve(int argc, char __user * __user * argv); +#endif + /* * sys_execve() executes a new program. */ @@ -1395,6 +1400,14 @@ goto out; bprm->argv_len = env_p - bprm->p; +#ifdef CONFIG_QEMU_TRACE + /* + * signal QEMU of execve arg + * -- wen + */ + qemu_trace_execve(bprm->argc, argv); +#endif + retval = search_binary_handler(bprm,regs); if (retval >= 0) { /* execve success */ @@ -1402,6 +1415,7 @@ security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); + return retval; } diff -Nru linux-2.6.23/fs/inotify_user.c kernel.android/fs/inotify_user.c --- linux-2.6.23/fs/inotify_user.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/fs/inotify_user.c 2007-11-12 07:49:03.000000000 +1100 @@ -31,6 +31,9 @@ #include #include #include +#ifdef CONFIG_ANDROID_POWER +#include +#endif #include @@ -81,6 +84,9 @@ unsigned int queue_size; /* size of the queue (bytes) */ unsigned int event_count; /* number of pending events */ unsigned int max_events; /* maximum number of events */ +#ifdef CONFIG_ANDROID_POWER + android_suspend_lock_t suspend_lock; +#endif }; /* @@ -157,6 +163,9 @@ if (atomic_dec_and_test(&dev->count)) { atomic_dec(&dev->user->inotify_devs); free_uid(dev->user); +#ifdef CONFIG_ANDROID_POWER + android_uninit_suspend_lock(&dev->suspend_lock); +#endif kfree(dev); } } @@ -301,6 +310,9 @@ dev->queue_size += sizeof(struct inotify_event) + kevent->event.len; list_add_tail(&kevent->list, &dev->events); wake_up_interruptible(&dev->wq); +#ifdef CONFIG_ANDROID_POWER + android_lock_suspend_auto_expire(&dev->suspend_lock, 5 * HZ); +#endif out: mutex_unlock(&dev->ev_mutex); @@ -318,6 +330,10 @@ dev->event_count--; dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len; +#ifdef CONFIG_ANDROID_POWER + if(dev->event_count == 0) + android_unlock_suspend(&dev->suspend_lock); +#endif kfree(kevent->name); kmem_cache_free(event_cachep, kevent); @@ -594,6 +610,10 @@ dev->max_events = inotify_max_queued_events; dev->user = user; atomic_set(&dev->count, 0); +#ifdef CONFIG_ANDROID_POWER + dev->suspend_lock.name = "inotify"; + android_init_suspend_lock(&dev->suspend_lock); +#endif get_inotify_dev(dev); atomic_inc(&user->inotify_devs); diff -Nru linux-2.6.23/fs/yaffs2/Kconfig kernel.android/fs/yaffs2/Kconfig --- linux-2.6.23/fs/yaffs2/Kconfig 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/Kconfig 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,176 @@ +# +# YAFFS file system configurations +# + +config YAFFS_FS + tristate "YAFFS2 file system support" + default n + depends on MTD + select YAFFS_YAFFS1 + select YAFFS_YAFFS2 + help + YAFFS2, or Yet Another Flash Filing System, is a filing system + optimised for NAND Flash chips. + + To compile the YAFFS2 file system support as a module, choose M + here: the module will be called yaffs2. + + If unsure, say N. + + Further information on YAFFS2 is available at + . + +config YAFFS_YAFFS1 + bool "512 byte / page devices" + depends on YAFFS_FS + default y + help + Enable YAFFS1 support -- yaffs for 512 byte / page devices + + Not needed for 2K-page devices. + + If unsure, say Y. + +config YAFFS_9BYTE_TAGS + bool "Use older-style on-NAND data format with pageStatus byte" + depends on YAFFS_YAFFS1 + default n + help + + Older-style on-NAND data format has a "pageStatus" byte to record + chunk/page state. This byte is zero when the page is discarded. + Choose this option if you have existing on-NAND data using this + format that you need to continue to support. New data written + also uses the older-style format. Note: Use of this option + generally requires that MTD's oob layout be adjusted to use the + older-style format. See notes on tags formats and MTD versions + in yaffs_mtdif1.c. + + If unsure, say N. + +config YAFFS_DOES_ECC + bool "Lets Yaffs do its own ECC" + depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS + default n + help + This enables Yaffs to use its own ECC functions instead of using + the ones from the generic MTD-NAND driver. + + If unsure, say N. + +config YAFFS_ECC_WRONG_ORDER + bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" + depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS + default n + help + This makes yaffs_ecc.c use the same ecc byte order as Steven + Hill's nand_ecc.c. If not set, then you get the same ecc byte + order as SmartMedia. + + If unsure, say N. + +config YAFFS_YAFFS2 + bool "2048 byte (or larger) / page devices" + depends on YAFFS_FS + default y + help + Enable YAFFS2 support -- yaffs for >= 2K bytes per page devices + + If unsure, say Y. + +config YAFFS_AUTO_YAFFS2 + bool "Autoselect yaffs2 format" + depends on YAFFS_YAFFS2 + default y + help + Without this, you need to explicitely use yaffs2 as the file + system type. With this, you can say "yaffs" and yaffs or yaffs2 + will be used depending on the device page size (yaffs on + 512-byte page devices, yaffs2 on 2K page devices). + + If unsure, say Y. + +config YAFFS_DISABLE_LAZY_LOAD + bool "Disable lazy loading" + depends on YAFFS_YAFFS2 + default n + help + "Lazy loading" defers loading file details until they are + required. This saves mount time, but makes the first look-up + a bit longer. + + Lazy loading will only happen if enabled by this option being 'n' + and if the appropriate tags are available, else yaffs2 will + automatically fall back to immediate loading and do the right + thing. + + Lazy laoding will be required by checkpointing. + + Setting this to 'y' will disable lazy loading. + + If unsure, say N. + +config YAFFS_CHECKPOINT_RESERVED_BLOCKS + int "Reserved blocks for checkpointing" + depends on YAFFS_YAFFS2 + default 10 + help + Give the number of Blocks to reserve for checkpointing. + Checkpointing saves the state at unmount so that mounting is + much faster as a scan of all the flash to regenerate this state + is not needed. These Blocks are reserved per partition, so if + you have very small partitions the default (10) may be a mess + for you. You can set this value to 0, but that does not mean + checkpointing is disabled at all. There only won't be any + specially reserved blocks for checkpointing, so if there is + enough free space on the filesystem, it will be used for + checkpointing. + + If unsure, leave at default (10), but don't wonder if there are + always 2MB used on your large page device partition (10 x 2k + pagesize). When using small partitions or when being very small + on space, you probably want to set this to zero. + +config YAFFS_DISABLE_WIDE_TNODES + bool "Turn off wide tnodes" + depends on YAFFS_FS + default n + help + Wide tnodes are only used for NAND arrays >=32MB for 512-byte + page devices and >=128MB for 2k page devices. They use slightly + more RAM but are faster since they eliminate chunk group + searching. + + Setting this to 'y' will force tnode width to 16 bits and save + memory but make large arrays slower. + + If unsure, say N. + +config YAFFS_ALWAYS_CHECK_CHUNK_ERASED + bool "Force chunk erase check" + depends on YAFFS_FS + default n + help + Normally YAFFS only checks chunks before writing until an erased + chunk is found. This helps to detect any partially written + chunks that might have happened due to power loss. + + Enabling this forces on the test that chunks are erased in flash + before writing to them. This takes more time but is potentially + a bit more secure. + + Suggest setting Y during development and ironing out driver + issues etc. Suggest setting to N if you want faster writing. + + If unsure, say Y. + +config YAFFS_SHORT_NAMES_IN_RAM + bool "Cache short names in RAM" + depends on YAFFS_FS + default y + help + If this config is set, then short names are stored with the + yaffs_Object. This costs an extra 16 bytes of RAM per object, + but makes look-ups faster. + + If unsure, say Y. diff -Nru linux-2.6.23/fs/yaffs2/Makefile kernel.android/fs/yaffs2/Makefile --- linux-2.6.23/fs/yaffs2/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/Makefile 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,10 @@ +# +# Makefile for the linux YAFFS filesystem routines. +# + +obj-$(CONFIG_YAFFS_FS) += yaffs.o + +yaffs-y := yaffs_ecc.o yaffs_fs.o yaffs_guts.o yaffs_checkptrw.o +yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o yaffs_qsort.o +yaffs-y += yaffs_tagscompat.o yaffs_tagsvalidity.o +yaffs-y += yaffs_mtdif.o yaffs_mtdif1.o yaffs_mtdif2.o diff -Nru linux-2.6.23/fs/yaffs2/devextras.h kernel.android/fs/yaffs2/devextras.h --- linux-2.6.23/fs/yaffs2/devextras.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/devextras.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,264 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * This file is just holds extra declarations used during development. + * Most of these are from kernel includes placed here so we can use them in + * applications. + * + */ + +#ifndef __EXTRAS_H__ +#define __EXTRAS_H__ + +#if defined WIN32 +#define __inline__ __inline +#define new newHack +#endif + +#if !(defined __KERNEL__) || (defined WIN32) + +/* User space defines */ + +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned __u32; + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define prefetch(x) 1 + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *new, + struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head *prev, + struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * list_for_each_safe - iterate over a list safe against removal + * of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/* + * File types + */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +#ifndef WIN32 +#include +#endif + +/* + * Attribute flags. These should be or-ed together to figure out what + * has been changed! + */ +#define ATTR_MODE 1 +#define ATTR_UID 2 +#define ATTR_GID 4 +#define ATTR_SIZE 8 +#define ATTR_ATIME 16 +#define ATTR_MTIME 32 +#define ATTR_CTIME 64 +#define ATTR_ATIME_SET 128 +#define ATTR_MTIME_SET 256 +#define ATTR_FORCE 512 /* Not a change, but a change it */ +#define ATTR_ATTR_FLAG 1024 + +struct iattr { + unsigned int ia_valid; + unsigned ia_mode; + unsigned ia_uid; + unsigned ia_gid; + unsigned ia_size; + unsigned ia_atime; + unsigned ia_mtime; + unsigned ia_ctime; + unsigned int ia_attr_flags; +}; + +#define KERN_DEBUG + +#else + +#ifndef WIN32 +#include +#include +#include +#include +#endif + +#endif + +#if defined WIN32 +#undef new +#endif + +#endif diff -Nru linux-2.6.23/fs/yaffs2/moduleconfig.h kernel.android/fs/yaffs2/moduleconfig.h --- linux-2.6.23/fs/yaffs2/moduleconfig.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/moduleconfig.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,65 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Martin Fouts + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_CONFIG_H__ +#define __YAFFS_CONFIG_H__ + +#ifdef YAFFS_OUT_OF_TREE + +/* DO NOT UNSET THESE THREE. YAFFS2 will not compile if you do. */ +#define CONFIG_YAFFS_FS +#define CONFIG_YAFFS_YAFFS1 +#define CONFIG_YAFFS_YAFFS2 + +/* These options are independent of each other. Select those that matter. */ + +/* Default: Not selected */ +/* Meaning: Yaffs does its own ECC, rather than using MTD ECC */ +//#define CONFIG_YAFFS_DOES_ECC + +/* Default: Not selected */ +/* Meaning: ECC byte order is 'wrong'. Only meaningful if */ +/* CONFIG_YAFFS_DOES_ECC is set */ +//#define CONFIG_YAFFS_ECC_WRONG_ORDER + +/* Default: Selected */ +/* Meaning: Disables testing whether chunks are erased before writing to them*/ +#define CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK + +/* Default: Selected */ +/* Meaning: Cache short names, taking more RAM, but faster look-ups */ +#define CONFIG_YAFFS_SHORT_NAMES_IN_RAM + +/* Default: 10 */ +/* Meaning: set the count of blocks to reserve for checkpointing */ +#define CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 + +/* +Older-style on-NAND data format has a "pageStatus" byte to record +chunk/page state. This byte is zeroed when the page is discarded. +Choose this option if you have existing on-NAND data in this format +that you need to continue to support. New data written also uses the +older-style format. +Note: Use of this option generally requires that MTD's oob layout be +adjusted to use the older-style format. See notes on tags formats and +MTD versions in yaffs_mtdif1.c. +*/ +/* Default: Not selected */ +/* Meaning: Use older-style on-NAND data format with pageStatus byte */ +//#define CONFIG_YAFFS_9BYTE_TAGS + +#endif /* YAFFS_OUT_OF_TREE */ + +#endif /* __YAFFS_CONFIG_H__ */ diff -Nru linux-2.6.23/fs/yaffs2/yaffs_checkptrw.c kernel.android/fs/yaffs2/yaffs_checkptrw.c --- linux-2.6.23/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_checkptrw.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,404 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + */ + +const char *yaffs_checkptrw_c_version = + "$Id$"; + + +#include "yaffs_checkptrw.h" + + +static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) +{ + + int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; + + T(YAFFS_TRACE_CHECKPOINT, + (TSTR("checkpt blocks available = %d" TENDSTR), + blocksAvailable)); + + + return (blocksAvailable <= 0) ? 0 : 1; +} + + +static int yaffs_CheckpointErase(yaffs_Device *dev) +{ + + int i; + + + if(!dev->eraseBlockInNAND) + return 0; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR), + dev->internalStartBlock,dev->internalEndBlock)); + + for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i)); + if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){ + bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + dev->nFreeChunks += dev->nChunksPerBlock; + } + else { + dev->markNANDBlockBad(dev,i); + bi->blockState = YAFFS_BLOCK_STATE_DEAD; + } + } + } + + dev->blocksInCheckpoint = 0; + + return 1; +} + + +static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) +{ + int i; + int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; + T(YAFFS_TRACE_CHECKPOINT, + (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR), + dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock)); + + if(dev->checkpointNextBlock >= 0 && + dev->checkpointNextBlock <= dev->internalEndBlock && + blocksAvailable > 0){ + + for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){ + dev->checkpointNextBlock = i + 1; + dev->checkpointCurrentBlock = i; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i)); + return; + } + } + } + T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR))); + + dev->checkpointNextBlock = -1; + dev->checkpointCurrentBlock = -1; +} + +static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) +{ + int i; + yaffs_ExtendedTags tags; + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR), + dev->blocksInCheckpoint, dev->checkpointNextBlock)); + + if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) + for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ + int chunk = i * dev->nChunksPerBlock; + int realignedChunk = chunk - dev->chunkOffset; + + dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags); + T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), + i, tags.objectId,tags.sequenceNumber,tags.eccResult)); + + if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ + /* Right kind of block */ + dev->checkpointNextBlock = tags.objectId; + dev->checkpointCurrentBlock = i; + dev->checkpointBlockList[dev->blocksInCheckpoint] = i; + dev->blocksInCheckpoint++; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i)); + return; + } + } + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR))); + + dev->checkpointNextBlock = -1; + dev->checkpointCurrentBlock = -1; +} + + +int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) +{ + + /* Got the functions we need? */ + if (!dev->writeChunkWithTagsToNAND || + !dev->readChunkWithTagsFromNAND || + !dev->eraseBlockInNAND || + !dev->markNANDBlockBad) + return 0; + + if(forWriting && !yaffs_CheckpointSpaceOk(dev)) + return 0; + + if(!dev->checkpointBuffer) + dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk); + if(!dev->checkpointBuffer) + return 0; + + + dev->checkpointPageSequence = 0; + + dev->checkpointOpenForWrite = forWriting; + + dev->checkpointByteCount = 0; + dev->checkpointSum = 0; + dev->checkpointXor = 0; + dev->checkpointCurrentBlock = -1; + dev->checkpointCurrentChunk = -1; + dev->checkpointNextBlock = dev->internalStartBlock; + + /* Erase all the blocks in the checkpoint area */ + if(forWriting){ + memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); + dev->checkpointByteOffset = 0; + return yaffs_CheckpointErase(dev); + + + } else { + int i; + /* Set to a value that will kick off a read */ + dev->checkpointByteOffset = dev->nDataBytesPerChunk; + /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) + * going to be way more than we need */ + dev->blocksInCheckpoint = 0; + dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2; + dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks); + for(i = 0; i < dev->checkpointMaxBlocks; i++) + dev->checkpointBlockList[i] = -1; + } + + return 1; +} + +int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) +{ + __u32 compositeSum; + compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); + *sum = compositeSum; + return 1; +} + +static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) +{ + + int chunk; + int realignedChunk; + + yaffs_ExtendedTags tags; + + if(dev->checkpointCurrentBlock < 0){ + yaffs_CheckpointFindNextErasedBlock(dev); + dev->checkpointCurrentChunk = 0; + } + + if(dev->checkpointCurrentBlock < 0) + return 0; + + tags.chunkDeleted = 0; + tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */ + tags.chunkId = dev->checkpointPageSequence + 1; + tags.sequenceNumber = YAFFS_SEQUENCE_CHECKPOINT_DATA; + tags.byteCount = dev->nDataBytesPerChunk; + if(dev->checkpointCurrentChunk == 0){ + /* First chunk we write for the block? Set block state to + checkpoint */ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock); + bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; + dev->blocksInCheckpoint++; + } + + chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk; + + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR), + chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); + + realignedChunk = chunk - dev->chunkOffset; + + dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); + dev->checkpointByteOffset = 0; + dev->checkpointPageSequence++; + dev->checkpointCurrentChunk++; + if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ + dev->checkpointCurrentChunk = 0; + dev->checkpointCurrentBlock = -1; + } + memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); + + return 1; +} + + +int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes) +{ + int i=0; + int ok = 1; + + + __u8 * dataBytes = (__u8 *)data; + + + + if(!dev->checkpointBuffer) + return 0; + + if(!dev->checkpointOpenForWrite) + return -1; + + while(i < nBytes && ok) { + + + + dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; + dev->checkpointSum += *dataBytes; + dev->checkpointXor ^= *dataBytes; + + dev->checkpointByteOffset++; + i++; + dataBytes++; + dev->checkpointByteCount++; + + + if(dev->checkpointByteOffset < 0 || + dev->checkpointByteOffset >= dev->nDataBytesPerChunk) + ok = yaffs_CheckpointFlushBuffer(dev); + + } + + return i; +} + +int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) +{ + int i=0; + int ok = 1; + yaffs_ExtendedTags tags; + + + int chunk; + int realignedChunk; + + __u8 *dataBytes = (__u8 *)data; + + if(!dev->checkpointBuffer) + return 0; + + if(dev->checkpointOpenForWrite) + return -1; + + while(i < nBytes && ok) { + + + if(dev->checkpointByteOffset < 0 || + dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { + + if(dev->checkpointCurrentBlock < 0){ + yaffs_CheckpointFindNextCheckpointBlock(dev); + dev->checkpointCurrentChunk = 0; + } + + if(dev->checkpointCurrentBlock < 0) + ok = 0; + else { + + chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + + dev->checkpointCurrentChunk; + + realignedChunk = chunk - dev->chunkOffset; + + /* read in the next chunk */ + /* printf("read checkpoint page %d\n",dev->checkpointPage); */ + dev->readChunkWithTagsFromNAND(dev, realignedChunk, + dev->checkpointBuffer, + &tags); + + if(tags.chunkId != (dev->checkpointPageSequence + 1) || + tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) + ok = 0; + + dev->checkpointByteOffset = 0; + dev->checkpointPageSequence++; + dev->checkpointCurrentChunk++; + + if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) + dev->checkpointCurrentBlock = -1; + } + } + + if(ok){ + *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; + dev->checkpointSum += *dataBytes; + dev->checkpointXor ^= *dataBytes; + dev->checkpointByteOffset++; + i++; + dataBytes++; + dev->checkpointByteCount++; + } + } + + return i; +} + +int yaffs_CheckpointClose(yaffs_Device *dev) +{ + + if(dev->checkpointOpenForWrite){ + if(dev->checkpointByteOffset != 0) + yaffs_CheckpointFlushBuffer(dev); + } else { + int i; + for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) + bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; + else { + // Todo this looks odd... + } + } + YFREE(dev->checkpointBlockList); + dev->checkpointBlockList = NULL; + } + + dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock; + dev->nErasedBlocks -= dev->blocksInCheckpoint; + + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR), + dev->checkpointByteCount)); + + if(dev->checkpointBuffer){ + /* free the buffer */ + YFREE(dev->checkpointBuffer); + dev->checkpointBuffer = NULL; + return 1; + } + else + return 0; + +} + +int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) +{ + /* Erase the first checksum block */ + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR))); + + if(!yaffs_CheckpointSpaceOk(dev)) + return 0; + + return yaffs_CheckpointErase(dev); +} + + + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_checkptrw.h kernel.android/fs/yaffs2/yaffs_checkptrw.h --- linux-2.6.23/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_checkptrw.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,35 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_CHECKPTRW_H__ +#define __YAFFS_CHECKPTRW_H__ + +#include "yaffs_guts.h" + +int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting); + +int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes); + +int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes); + +int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum); + +int yaffs_CheckpointClose(yaffs_Device *dev); + +int yaffs_CheckpointInvalidateStream(yaffs_Device *dev); + + +#endif + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_ecc.c kernel.android/fs/yaffs2/yaffs_ecc.c --- linux-2.6.23/fs/yaffs2/yaffs_ecc.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_ecc.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,331 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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 code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC + * blocks are used on a 512-byte NAND page. + * + */ + +/* Table generated by gen-ecc.c + * Using a table means we do not have to calculate p1..p4 and p1'..p4' + * for each byte of data. These are instead provided in a table in bits7..2. + * Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore + * this bytes influence on the line parity. + */ + +const char *yaffs_ecc_c_version = + "$Id$"; + +#include "yportenv.h" + +#include "yaffs_ecc.h" + +static const unsigned char column_parity_table[] = { + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, +}; + +/* Count the bits in an unsigned char or a U32 */ + +static int yaffs_CountBits(unsigned char x) +{ + int r = 0; + while (x) { + if (x & 1) + r++; + x >>= 1; + } + return r; +} + +static int yaffs_CountBits32(unsigned x) +{ + int r = 0; + while (x) { + if (x & 1) + r++; + x >>= 1; + } + return r; +} + +/* Calculate the ECC for a 256-byte block of data */ +void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc) +{ + unsigned int i; + + unsigned char col_parity = 0; + unsigned char line_parity = 0; + unsigned char line_parity_prime = 0; + unsigned char t; + unsigned char b; + + for (i = 0; i < 256; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) // odd number of bits in the byte + { + line_parity ^= i; + line_parity_prime ^= ~i; + } + + } + + ecc[2] = (~col_parity) | 0x03; + + t = 0; + if (line_parity & 0x80) + t |= 0x80; + if (line_parity_prime & 0x80) + t |= 0x40; + if (line_parity & 0x40) + t |= 0x20; + if (line_parity_prime & 0x40) + t |= 0x10; + if (line_parity & 0x20) + t |= 0x08; + if (line_parity_prime & 0x20) + t |= 0x04; + if (line_parity & 0x10) + t |= 0x02; + if (line_parity_prime & 0x10) + t |= 0x01; + ecc[1] = ~t; + + t = 0; + if (line_parity & 0x08) + t |= 0x80; + if (line_parity_prime & 0x08) + t |= 0x40; + if (line_parity & 0x04) + t |= 0x20; + if (line_parity_prime & 0x04) + t |= 0x10; + if (line_parity & 0x02) + t |= 0x08; + if (line_parity_prime & 0x02) + t |= 0x04; + if (line_parity & 0x01) + t |= 0x02; + if (line_parity_prime & 0x01) + t |= 0x01; + ecc[0] = ~t; + +#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER + // Swap the bytes into the wrong order + t = ecc[0]; + ecc[0] = ecc[1]; + ecc[1] = t; +#endif +} + + +/* Correct the ECC on a 256 byte block of data */ + +int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc) +{ + unsigned char d0, d1, d2; /* deltas */ + + d0 = read_ecc[0] ^ test_ecc[0]; + d1 = read_ecc[1] ^ test_ecc[1]; + d2 = read_ecc[2] ^ test_ecc[2]; + + if ((d0 | d1 | d2) == 0) + return 0; /* no error */ + + if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && + ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && + ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { + /* Single bit (recoverable) error in data */ + + unsigned byte; + unsigned bit; + +#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER + // swap the bytes to correct for the wrong order + unsigned char t; + + t = d0; + d0 = d1; + d1 = t; +#endif + + bit = byte = 0; + + if (d1 & 0x80) + byte |= 0x80; + if (d1 & 0x20) + byte |= 0x40; + if (d1 & 0x08) + byte |= 0x20; + if (d1 & 0x02) + byte |= 0x10; + if (d0 & 0x80) + byte |= 0x08; + if (d0 & 0x20) + byte |= 0x04; + if (d0 & 0x08) + byte |= 0x02; + if (d0 & 0x02) + byte |= 0x01; + + if (d2 & 0x80) + bit |= 0x04; + if (d2 & 0x20) + bit |= 0x02; + if (d2 & 0x08) + bit |= 0x01; + + data[byte] ^= (1 << bit); + + return 1; /* Corrected the error */ + } + + if ((yaffs_CountBits(d0) + + yaffs_CountBits(d1) + + yaffs_CountBits(d2)) == 1) { + /* Reccoverable error in ecc */ + + read_ecc[0] = test_ecc[0]; + read_ecc[1] = test_ecc[1]; + read_ecc[2] = test_ecc[2]; + + return 1; /* Corrected the error */ + } + + /* Unrecoverable error */ + + return -1; + +} + + +/* + * ECCxxxOther does ECC calcs on arbitrary n bytes of data + */ +void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, + yaffs_ECCOther * eccOther) +{ + unsigned int i; + + unsigned char col_parity = 0; + unsigned line_parity = 0; + unsigned line_parity_prime = 0; + unsigned char b; + + for (i = 0; i < nBytes; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) { + /* odd number of bits in the byte */ + line_parity ^= i; + line_parity_prime ^= ~i; + } + + } + + eccOther->colParity = (col_parity >> 2) & 0x3f; + eccOther->lineParity = line_parity; + eccOther->lineParityPrime = line_parity_prime; +} + +int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, + yaffs_ECCOther * read_ecc, + const yaffs_ECCOther * test_ecc) +{ + unsigned char cDelta; /* column parity delta */ + unsigned lDelta; /* line parity delta */ + unsigned lDeltaPrime; /* line parity delta */ + unsigned bit; + + cDelta = read_ecc->colParity ^ test_ecc->colParity; + lDelta = read_ecc->lineParity ^ test_ecc->lineParity; + lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime; + + if ((cDelta | lDelta | lDeltaPrime) == 0) + return 0; /* no error */ + + if (lDelta == ~lDeltaPrime && + (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15)) + { + /* Single bit (recoverable) error in data */ + + bit = 0; + + if (cDelta & 0x20) + bit |= 0x04; + if (cDelta & 0x08) + bit |= 0x02; + if (cDelta & 0x02) + bit |= 0x01; + + if(lDelta >= nBytes) + return -1; + + data[lDelta] ^= (1 << bit); + + return 1; /* corrected */ + } + + if ((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) + + yaffs_CountBits(cDelta)) == 1) { + /* Reccoverable error in ecc */ + + *read_ecc = *test_ecc; + return 1; /* corrected */ + } + + /* Unrecoverable error */ + + return -1; + +} + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_ecc.h kernel.android/fs/yaffs2/yaffs_ecc.h --- linux-2.6.23/fs/yaffs2/yaffs_ecc.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_ecc.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + /* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC + * blocks are used on a 512-byte NAND page. + * + */ + +#ifndef __YAFFS_ECC_H__ +#define __YAFFS_ECC_H__ + +typedef struct { + unsigned char colParity; + unsigned lineParity; + unsigned lineParityPrime; +} yaffs_ECCOther; + +void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc); +int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc); + +void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, + yaffs_ECCOther * ecc); +int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, + yaffs_ECCOther * read_ecc, + const yaffs_ECCOther * test_ecc); +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_fs.c kernel.android/fs/yaffs2/yaffs_fs.c --- linux-2.6.23/fs/yaffs2/yaffs_fs.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_fs.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,2299 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * Acknowledgements: + * Luc van OostenRyck for numerous patches. + * Nick Bane for numerous patches. + * Nick Bane for 2.5/2.6 integration. + * Andras Toth for mknod rdev issue. + * Michael Fischer for finding the problem with inode inconsistency. + * Some code bodily lifted from JFFS + * + * 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 is the file system front-end to YAFFS that hooks it up to + * the VFS. + * + * Special notes: + * >> 2.4: sb->u.generic_sbp points to the yaffs_Device associated with + * this superblock + * >> 2.6: sb->s_fs_info points to the yaffs_Device associated with this + * superblock + * >> inode->u.generic_ip points to the associated yaffs_Object. + */ + +const char *yaffs_fs_c_version = + "$Id$"; +extern const char *yaffs_guts_c_version; + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + +#include /* Added NCB 15-8-2003 */ +#include +#define UnlockPage(p) unlock_page(p) +#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) + +/* FIXME: use sb->s_id instead ? */ +#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) + +#else + +#include +#define BDEVNAME_SIZE 0 +#define yaffs_devname(sb, buf) kdevname(sb->s_dev) + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ +#define __user +#endif + +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +#define WRITE_SIZE_STR "writesize" +#define WRITE_SIZE(mtd) (mtd)->writesize +#else +#define WRITE_SIZE_STR "oobblock" +#define WRITE_SIZE(mtd) (mtd)->oobblock +#endif + +#include + +#include "yportenv.h" +#include "yaffs_guts.h" + +#include +#include "yaffs_mtdif.h" +#include "yaffs_mtdif1.h" +#include "yaffs_mtdif2.h" + +unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS; +unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; + +/* Module Parameters */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +module_param(yaffs_traceMask,uint,0644); +module_param(yaffs_wr_attempts,uint,0644); +#else +MODULE_PARM(yaffs_traceMask,"i"); +MODULE_PARM(yaffs_wr_attempts,"i"); +#endif + +/*#define T(x) printk x */ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) +#define yaffs_InodeToObjectLV(iptr) (iptr)->i_private +#else +#define yaffs_InodeToObjectLV(iptr) (iptr)->u.generic_ip +#endif + +#define yaffs_InodeToObject(iptr) ((yaffs_Object *)(yaffs_InodeToObjectLV(iptr))) +#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode) + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->s_fs_info) +#else +#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->u.generic_sbp) +#endif + +static void yaffs_put_super(struct super_block *sb); + +static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, + loff_t * pos); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_file_flush(struct file *file, fl_owner_t id); +#else +static int yaffs_file_flush(struct file *file); +#endif + +static int yaffs_sync_object(struct file *file, struct dentry *dentry, + int datasync); + +static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *n); +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *n); +#else +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode); +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry); +#endif +static int yaffs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry); +static int yaffs_unlink(struct inode *dir, struct dentry *dentry); +static int yaffs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname); +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev); +#else +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + int dev); +#endif +static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +static int yaffs_setattr(struct dentry *dentry, struct iattr *attr); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_sync_fs(struct super_block *sb, int wait); +static void yaffs_write_super(struct super_block *sb); +#else +static int yaffs_sync_fs(struct super_block *sb); +static int yaffs_write_super(struct super_block *sb); +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf); +#else +static int yaffs_statfs(struct super_block *sb, struct statfs *buf); +#endif +static void yaffs_read_inode(struct inode *inode); + +static void yaffs_put_inode(struct inode *inode); +static void yaffs_delete_inode(struct inode *); +static void yaffs_clear_inode(struct inode *); + +static int yaffs_readpage(struct file *file, struct page *page); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_writepage(struct page *page, struct writeback_control *wbc); +#else +static int yaffs_writepage(struct page *page); +#endif +static int yaffs_prepare_write(struct file *f, struct page *pg, + unsigned offset, unsigned to); +static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, + unsigned to); + +static int yaffs_readlink(struct dentry *dentry, char __user * buffer, + int buflen); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); +#else +static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); +#endif + +static struct address_space_operations yaffs_file_address_operations = { + .readpage = yaffs_readpage, + .writepage = yaffs_writepage, + .prepare_write = yaffs_prepare_write, + .commit_write = yaffs_commit_write, +}; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)) +static struct file_operations yaffs_file_operations = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, +}; + +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) + +static struct file_operations yaffs_file_operations = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .sendfile = generic_file_sendfile, +}; + +#else + +static struct file_operations yaffs_file_operations = { + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) + .sendfile = generic_file_sendfile, +#endif +}; +#endif + +static struct inode_operations yaffs_file_inode_operations = { + .setattr = yaffs_setattr, +}; + +static struct inode_operations yaffs_symlink_inode_operations = { + .readlink = yaffs_readlink, + .follow_link = yaffs_follow_link, + .setattr = yaffs_setattr, +}; + +static struct inode_operations yaffs_dir_inode_operations = { + .create = yaffs_create, + .lookup = yaffs_lookup, + .link = yaffs_link, + .unlink = yaffs_unlink, + .symlink = yaffs_symlink, + .mkdir = yaffs_mkdir, + .rmdir = yaffs_unlink, + .mknod = yaffs_mknod, + .rename = yaffs_rename, + .setattr = yaffs_setattr, +}; + +static struct file_operations yaffs_dir_operations = { + .read = generic_read_dir, + .readdir = yaffs_readdir, + .fsync = yaffs_sync_object, +}; + +static struct super_operations yaffs_super_ops = { + .statfs = yaffs_statfs, + .read_inode = yaffs_read_inode, + .put_inode = yaffs_put_inode, + .put_super = yaffs_put_super, + .delete_inode = yaffs_delete_inode, + .clear_inode = yaffs_clear_inode, + .sync_fs = yaffs_sync_fs, + .write_super = yaffs_write_super, +}; + +static void yaffs_GrossLock(yaffs_Device * dev) +{ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs locking\n")); + + down(&dev->grossLock); +} + +static void yaffs_GrossUnlock(yaffs_Device * dev) +{ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs unlocking\n")); + up(&dev->grossLock); + +} + +static int yaffs_readlink(struct dentry *dentry, char __user * buffer, + int buflen) +{ + unsigned char *alias; + int ret; + + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + + yaffs_GrossLock(dev); + + alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); + + yaffs_GrossUnlock(dev); + + if (!alias) + return -ENOMEM; + + ret = vfs_readlink(dentry, buffer, buflen, alias); + kfree(alias); + return ret; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +#else +static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +#endif +{ + unsigned char *alias; + int ret; + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + + yaffs_GrossLock(dev); + + alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); + + yaffs_GrossUnlock(dev); + + if (!alias) + { + ret = -ENOMEM; + goto out; + } + + ret = vfs_follow_link(nd, alias); + kfree(alias); +out: +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) + return ERR_PTR (ret); +#else + return ret; +#endif +} + +struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, + yaffs_Object * obj); + +/* + * Lookup is used to find objects in the fs + */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *n) +#else +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) +#endif +{ + yaffs_Object *obj; + struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ + + yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev; + + yaffs_GrossLock(dev); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_lookup for %d:%s\n", + yaffs_InodeToObject(dir)->objectId, dentry->d_name.name)); + + obj = + yaffs_FindObjectByName(yaffs_InodeToObject(dir), + dentry->d_name.name); + + obj = yaffs_GetEquivalentObject(obj); /* in case it was a hardlink */ + + /* Can't hold gross lock when calling yaffs_get_inode() */ + yaffs_GrossUnlock(dev); + + if (obj) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_lookup found %d\n", obj->objectId)); + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + + if (inode) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_loookup dentry \n")); +/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to + * d_add even if NULL inode */ +#if 0 + /*dget(dentry); // try to solve directory bug */ + d_add(dentry, inode); + + /* return dentry; */ + return NULL; +#endif + } + + } else { + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_lookup not found\n")); + + } + +/* added NCB for 2.5/6 compatability - forces add even if inode is + * NULL which creates dentry hash */ + d_add(dentry, inode); + + return NULL; + /* return (ERR_PTR(-EIO)); */ + +} + +/* For now put inode is just for debugging + * Put inode is called when the inode **structure** is put. + */ +static void yaffs_put_inode(struct inode *inode) +{ + T(YAFFS_TRACE_OS, + ("yaffs_put_inode: ino %d, count %d\n", (int)inode->i_ino, + atomic_read(&inode->i_count))); + +} + +/* clear is called to tell the fs to release any per-inode data it holds */ +static void yaffs_clear_inode(struct inode *inode) +{ + yaffs_Object *obj; + yaffs_Device *dev; + + obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + ("yaffs_clear_inode: ino %d, count %d %s\n", (int)inode->i_ino, + atomic_read(&inode->i_count), + obj ? "object exists" : "null object")); + + if (obj) { + dev = obj->myDev; + yaffs_GrossLock(dev); + + /* Clear the association between the inode and + * the yaffs_Object. + */ + obj->myInode = NULL; + yaffs_InodeToObjectLV(inode) = NULL; + + /* If the object freeing was deferred, then the real + * free happens now. + * This should fix the inode inconsistency problem. + */ + + yaffs_HandleDeferedFree(obj); + + yaffs_GrossUnlock(dev); + } + +} + +/* delete is called when the link count is zero and the inode + * is put (ie. nobody wants to know about it anymore, time to + * delete the file). + * NB Must call clear_inode() + */ +static void yaffs_delete_inode(struct inode *inode) +{ + yaffs_Object *obj = yaffs_InodeToObject(inode); + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, + ("yaffs_delete_inode: ino %d, count %d %s\n", (int)inode->i_ino, + atomic_read(&inode->i_count), + obj ? "object exists" : "null object")); + + if (obj) { + dev = obj->myDev; + yaffs_GrossLock(dev); + yaffs_DeleteFile(obj); + yaffs_GrossUnlock(dev); + } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) + truncate_inode_pages (&inode->i_data, 0); +#endif + clear_inode(inode); +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_file_flush(struct file *file, fl_owner_t id) +#else +static int yaffs_file_flush(struct file *file) +#endif +{ + yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry); + + yaffs_Device *dev = obj->myDev; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_file_flush object %d (%s)\n", obj->objectId, + obj->dirty ? "dirty" : "clean")); + + yaffs_GrossLock(dev); + + yaffs_FlushFile(obj, 1); + + yaffs_GrossUnlock(dev); + + return 0; +} + +static int yaffs_readpage_nolock(struct file *f, struct page *pg) +{ + /* Lifted from jffs2 */ + + yaffs_Object *obj; + unsigned char *pg_buf; + int ret; + + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_readpage at %08x, size %08x\n", + (unsigned)(pg->index << PAGE_CACHE_SHIFT), + (unsigned)PAGE_CACHE_SIZE)); + + obj = yaffs_DentryToObject(f->f_dentry); + + dev = obj->myDev; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + BUG_ON(!PageLocked(pg)); +#else + if (!PageLocked(pg)) + PAGE_BUG(pg); +#endif + + pg_buf = kmap(pg); + /* FIXME: Can kmap fail? */ + + yaffs_GrossLock(dev); + + ret = + yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, + PAGE_CACHE_SIZE); + + yaffs_GrossUnlock(dev); + + if (ret >= 0) + ret = 0; + + if (ret) { + ClearPageUptodate(pg); + SetPageError(pg); + } else { + SetPageUptodate(pg); + ClearPageError(pg); + } + + flush_dcache_page(pg); + kunmap(pg); + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_readpage done\n")); + return ret; +} + +static int yaffs_readpage_unlock(struct file *f, struct page *pg) +{ + int ret = yaffs_readpage_nolock(f, pg); + UnlockPage(pg); + return ret; +} + +static int yaffs_readpage(struct file *f, struct page *pg) +{ + return yaffs_readpage_unlock(f, pg); +} + +/* writepage inspired by/stolen from smbfs */ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_writepage(struct page *page, struct writeback_control *wbc) +#else +static int yaffs_writepage(struct page *page) +#endif +{ + struct address_space *mapping = page->mapping; + loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT; + struct inode *inode; + unsigned long end_index; + char *buffer; + yaffs_Object *obj; + int nWritten = 0; + unsigned nBytes; + + if (!mapping) + BUG(); + inode = mapping->host; + if (!inode) + BUG(); + + if (offset > inode->i_size) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_writepage at %08x, inode size = %08x!!!\n", + (unsigned)(page->index << PAGE_CACHE_SHIFT), + (unsigned)inode->i_size)); + T(YAFFS_TRACE_OS, + (KERN_DEBUG " -> don't care!!\n")); + unlock_page(page); + return 0; + } + + end_index = inode->i_size >> PAGE_CACHE_SHIFT; + + /* easy case */ + if (page->index < end_index) { + nBytes = PAGE_CACHE_SIZE; + } else { + nBytes = inode->i_size & (PAGE_CACHE_SIZE - 1); + } + + get_page(page); + + buffer = kmap(page); + + obj = yaffs_InodeToObject(inode); + yaffs_GrossLock(obj->myDev); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_writepage at %08x, size %08x\n", + (unsigned)(page->index << PAGE_CACHE_SHIFT), nBytes)); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "writepag0: obj = %05x, ino = %05x\n", + (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); + + nWritten = + yaffs_WriteDataToFile(obj, buffer, page->index << PAGE_CACHE_SHIFT, + nBytes, 0); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "writepag1: obj = %05x, ino = %05x\n", + (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); + + yaffs_GrossUnlock(obj->myDev); + + kunmap(page); + SetPageUptodate(page); + UnlockPage(page); + put_page(page); + + return (nWritten == nBytes) ? 0 : -ENOSPC; +} + +static int yaffs_prepare_write(struct file *f, struct page *pg, + unsigned offset, unsigned to) +{ + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_prepair_write\n")); + if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE)) + return yaffs_readpage_nolock(f, pg); + + return 0; + +} + +static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, + unsigned to) +{ + + void *addr = page_address(pg) + offset; + loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; + int nBytes = to - offset; + int nWritten; + + unsigned spos = pos; + unsigned saddr = (unsigned)addr; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_commit_write addr %x pos %x nBytes %d\n", saddr, + spos, nBytes)); + + nWritten = yaffs_file_write(f, addr, nBytes, &pos); + + if (nWritten != nBytes) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_commit_write not same size nWritten %d nBytes %d\n", + nWritten, nBytes)); + SetPageError(pg); + ClearPageUptodate(pg); + } else { + SetPageUptodate(pg); + } + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_commit_write returning %d\n", + nWritten == nBytes ? 0 : nWritten)); + + return nWritten == nBytes ? 0 : nWritten; + +} + +static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object * obj) +{ + if (inode && obj) { + + + /* Check mode against the variant type and attempt to repair if broken. */ + __u32 mode = obj->yst_mode; + switch( obj->variantType ){ + case YAFFS_OBJECT_TYPE_FILE : + if( ! S_ISREG(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFREG; + } + + break; + case YAFFS_OBJECT_TYPE_SYMLINK : + if( ! S_ISLNK(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFLNK; + } + + break; + case YAFFS_OBJECT_TYPE_DIRECTORY : + if( ! S_ISDIR(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFDIR; + } + + break; + case YAFFS_OBJECT_TYPE_UNKNOWN : + case YAFFS_OBJECT_TYPE_HARDLINK : + case YAFFS_OBJECT_TYPE_SPECIAL : + default: + /* TODO? */ + break; + } + + inode->i_ino = obj->objectId; + inode->i_mode = obj->yst_mode; + inode->i_uid = obj->yst_uid; + inode->i_gid = obj->yst_gid; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + inode->i_blksize = inode->i_sb->s_blocksize; +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + + inode->i_rdev = old_decode_dev(obj->yst_rdev); + inode->i_atime.tv_sec = (time_t) (obj->yst_atime); + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; + inode->i_ctime.tv_nsec = 0; +#else + inode->i_rdev = obj->yst_rdev; + inode->i_atime = obj->yst_atime; + inode->i_mtime = obj->yst_mtime; + inode->i_ctime = obj->yst_ctime; +#endif + inode->i_size = yaffs_GetObjectFileLength(obj); + inode->i_blocks = (inode->i_size + 511) >> 9; + + inode->i_nlink = yaffs_GetObjectLinkCount(obj); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_FillInode mode %x uid %d gid %d size %d count %d\n", + inode->i_mode, inode->i_uid, inode->i_gid, + (int)inode->i_size, atomic_read(&inode->i_count))); + + switch (obj->yst_mode & S_IFMT) { + default: /* fifo, device or socket */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + init_special_inode(inode, obj->yst_mode, + old_decode_dev(obj->yst_rdev)); +#else + init_special_inode(inode, obj->yst_mode, + (dev_t) (obj->yst_rdev)); +#endif + break; + case S_IFREG: /* file */ + inode->i_op = &yaffs_file_inode_operations; + inode->i_fop = &yaffs_file_operations; + inode->i_mapping->a_ops = + &yaffs_file_address_operations; + break; + case S_IFDIR: /* directory */ + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + break; + case S_IFLNK: /* symlink */ + inode->i_op = &yaffs_symlink_inode_operations; + break; + } + + yaffs_InodeToObjectLV(inode) = obj; + + obj->myInode = inode; + + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_FileInode invalid parameters\n")); + } + +} + +struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, + yaffs_Object * obj) +{ + struct inode *inode; + + if (!sb) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for NULL super_block!!\n")); + return NULL; + + } + + if (!obj) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for NULL object!!\n")); + return NULL; + + } + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for object %d\n", obj->objectId)); + + inode = iget(sb, obj->objectId); + + /* NB Side effect: iget calls back to yaffs_read_inode(). */ + /* iget also increments the inode's i_count */ + /* NB You can't be holding grossLock or deadlock will happen! */ + + return inode; +} + +static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, + loff_t * pos) +{ + yaffs_Object *obj; + int nWritten, ipos; + struct inode *inode; + yaffs_Device *dev; + + obj = yaffs_DentryToObject(f->f_dentry); + + dev = obj->myDev; + + yaffs_GrossLock(dev); + + inode = f->f_dentry->d_inode; + + if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) { + ipos = inode->i_size; + } else { + ipos = *pos; + } + + if (!obj) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_file_write: hey obj is null!\n")); + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_file_write about to write writing %d bytes" + "to object %d at %d\n", + n, obj->objectId, ipos)); + } + + nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_file_write writing %d bytes, %d written at %d\n", + n, nWritten, ipos)); + if (nWritten > 0) { + ipos += nWritten; + *pos = ipos; + if (ipos > inode->i_size) { + inode->i_size = ipos; + inode->i_blocks = (ipos + 511) >> 9; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_file_write size updated to %d bytes, " + "%d blocks\n", + ipos, (int)(inode->i_blocks))); + } + + } + yaffs_GrossUnlock(dev); + return nWritten == 0 ? -ENOSPC : nWritten; +} + +static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) +{ + yaffs_Object *obj; + yaffs_Device *dev; + struct inode *inode = f->f_dentry->d_inode; + unsigned long offset, curoffs; + struct list_head *i; + yaffs_Object *l; + + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + obj = yaffs_DentryToObject(f->f_dentry); + dev = obj->myDev; + + yaffs_GrossLock(dev); + + offset = f->f_pos; + + T(YAFFS_TRACE_OS, ("yaffs_readdir: starting at %d\n", (int)offset)); + + if (offset == 0) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_readdir: entry . ino %d \n", + (int)inode->i_ino)); + if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) + < 0) { + goto out; + } + offset++; + f->f_pos++; + } + if (offset == 1) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_readdir: entry .. ino %d \n", + (int)f->f_dentry->d_parent->d_inode->i_ino)); + if (filldir + (dirent, "..", 2, offset, + f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { + goto out; + } + offset++; + f->f_pos++; + } + + curoffs = 1; + + /* If the directory has changed since the open or last call to + readdir, rewind to after the 2 canned entries. */ + + if (f->f_version != inode->i_version) { + offset = 2; + f->f_pos = offset; + f->f_version = inode->i_version; + } + + list_for_each(i, &obj->variant.directoryVariant.children) { + curoffs++; + if (curoffs >= offset) { + l = list_entry(i, yaffs_Object, siblings); + + yaffs_GetObjectName(l, name, + YAFFS_MAX_NAME_LENGTH + 1); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_readdir: %s inode %d\n", name, + yaffs_GetObjectInode(l))); + + if (filldir(dirent, + name, + strlen(name), + offset, + yaffs_GetObjectInode(l), + yaffs_GetObjectType(l)) + < 0) { + goto up_and_out; + } + + offset++; + f->f_pos++; + } + } + + up_and_out: + out: + + yaffs_GrossUnlock(dev); + + return 0; +} + +/* + * File creation. Allocate an inode, and we're done.. + */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t rdev) +#else +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + int rdev) +#endif +{ + struct inode *inode; + + yaffs_Object *obj = NULL; + yaffs_Device *dev; + + yaffs_Object *parent = yaffs_InodeToObject(dir); + + int error = -ENOSPC; + uid_t uid = current->fsuid; + gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + + if((dir->i_mode & S_ISGID) && S_ISDIR(mode)) + mode |= S_ISGID; + + if (parent) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod: parent object %d type %d\n", + parent->objectId, parent->variantType)); + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod: could not get parent object\n")); + return -EPERM; + } + + T(YAFFS_TRACE_OS, ("yaffs_mknod: making oject for %s, " + "mode %x dev %x\n", + dentry->d_name.name, mode, rdev)); + + dev = parent->myDev; + + yaffs_GrossLock(dev); + + switch (mode & S_IFMT) { + default: + /* Special (socket, fifo, device...) */ + T(YAFFS_TRACE_OS, (KERN_DEBUG + "yaffs_mknod: making special\n")); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + obj = + yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid, + gid, old_encode_dev(rdev)); +#else + obj = + yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid, + gid, rdev); +#endif + break; + case S_IFREG: /* file */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n")); + obj = + yaffs_MknodFile(parent, dentry->d_name.name, mode, uid, + gid); + break; + case S_IFDIR: /* directory */ + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod: making directory\n")); + obj = + yaffs_MknodDirectory(parent, dentry->d_name.name, mode, + uid, gid); + break; + case S_IFLNK: /* symlink */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n")); + obj = NULL; /* Do we ever get here? */ + break; + } + + /* Can not call yaffs_get_inode() with gross lock held */ + yaffs_GrossUnlock(dev); + + if (obj) { + inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); + d_instantiate(dentry, inode); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod created object %d count = %d\n", + obj->objectId, atomic_read(&inode->i_count))); + error = 0; + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod failed making object\n")); + error = -ENOMEM; + } + + return error; +} + +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int retVal; + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mkdir\n")); + retVal = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); +#if 0 + /* attempt to fix dir bug - didn't work */ + if (!retVal) { + dget(dentry); + } +#endif + return retVal; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *n) +#else +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_create\n")); + return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +static int yaffs_unlink(struct inode *dir, struct dentry *dentry) +{ + int retVal; + + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_unlink %d:%s\n", (int)(dir->i_ino), + dentry->d_name.name)); + + dev = yaffs_InodeToObject(dir)->myDev; + + yaffs_GrossLock(dev); + + retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name); + + if (retVal == YAFFS_OK) { + dentry->d_inode->i_nlink--; + dir->i_version++; + yaffs_GrossUnlock(dev); + mark_inode_dirty(dentry->d_inode); + return 0; + } + yaffs_GrossUnlock(dev); + return -ENOTEMPTY; +} + +/* + * Create a link... + */ +static int yaffs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + yaffs_Object *obj = NULL; + yaffs_Object *link = NULL; + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_link\n")); + + obj = yaffs_InodeToObject(inode); + dev = obj->myDev; + + yaffs_GrossLock(dev); + + if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ + { + link = + yaffs_Link(yaffs_InodeToObject(dir), dentry->d_name.name, + obj); + } + + if (link) { + old_dentry->d_inode->i_nlink = yaffs_GetObjectLinkCount(obj); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_link link count %d i_count %d\n", + old_dentry->d_inode->i_nlink, + atomic_read(&old_dentry->d_inode->i_count))); + + } + + yaffs_GrossUnlock(dev); + + if (link) { + + return 0; + } + + return -EPERM; +} + +static int yaffs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + yaffs_Object *obj; + yaffs_Device *dev; + uid_t uid = current->fsuid; + gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_symlink\n")); + + dev = yaffs_InodeToObject(dir)->myDev; + yaffs_GrossLock(dev); + obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, + S_IFLNK | S_IRWXUGO, uid, gid, symname); + yaffs_GrossUnlock(dev); + + if (obj) { + + struct inode *inode; + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + d_instantiate(dentry, inode); + T(YAFFS_TRACE_OS, (KERN_DEBUG "symlink created OK\n")); + return 0; + } else { + T(YAFFS_TRACE_OS, (KERN_DEBUG "symlink not created\n")); + + } + + return -ENOMEM; +} + +static int yaffs_sync_object(struct file *file, struct dentry *dentry, + int datasync) +{ + + yaffs_Object *obj; + yaffs_Device *dev; + + obj = yaffs_DentryToObject(dentry); + + dev = obj->myDev; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_object\n")); + yaffs_GrossLock(dev); + yaffs_FlushFile(obj, 1); + yaffs_GrossUnlock(dev); + return 0; +} + +/* + * The VFS layer already does all the dentry stuff for rename. + * + * NB: POSIX says you can rename an object over an old object of the same name + */ +static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + yaffs_Device *dev; + int retVal = YAFFS_FAIL; + yaffs_Object *target; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_rename\n")); + dev = yaffs_InodeToObject(old_dir)->myDev; + + yaffs_GrossLock(dev); + + /* Check if the target is an existing directory that is not empty. */ + target = + yaffs_FindObjectByName(yaffs_InodeToObject(new_dir), + new_dentry->d_name.name); + + + + if (target && + target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&target->variant.directoryVariant.children)) { + + T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n")); + + retVal = YAFFS_FAIL; + } else { + + /* Now does unlinking internally using shadowing mechanism */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "calling yaffs_RenameObject\n")); + + retVal = + yaffs_RenameObject(yaffs_InodeToObject(old_dir), + old_dentry->d_name.name, + yaffs_InodeToObject(new_dir), + new_dentry->d_name.name); + + } + yaffs_GrossUnlock(dev); + + if (retVal == YAFFS_OK) { + if(target) { + new_dentry->d_inode->i_nlink--; + mark_inode_dirty(new_dentry->d_inode); + } + + return 0; + } else { + return -ENOTEMPTY; + } + +} + +static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_setattr of object %d\n", + yaffs_InodeToObject(inode)->objectId)); + + if ((error = inode_change_ok(inode, attr)) == 0) { + + dev = yaffs_InodeToObject(inode)->myDev; + yaffs_GrossLock(dev); + if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) == + YAFFS_OK) { + error = 0; + } else { + error = -EPERM; + } + yaffs_GrossUnlock(dev); + if (!error) + error = inode_setattr(inode, attr); + } + return error; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + struct super_block *sb = dentry->d_sb; +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); +#else +static int yaffs_statfs(struct super_block *sb, struct statfs *buf) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); +#endif + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_statfs\n")); + + yaffs_GrossLock(dev); + + buf->f_type = YAFFS_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_namelen = 255; + if (sb->s_blocksize > dev->nDataBytesPerChunk) { + + buf->f_blocks = + (dev->endBlock - dev->startBlock + + 1) * dev->nChunksPerBlock / (sb->s_blocksize / + dev->nDataBytesPerChunk); + buf->f_bfree = + yaffs_GetNumberOfFreeChunks(dev) / (sb->s_blocksize / + dev->nDataBytesPerChunk); + } else { + + buf->f_blocks = + (dev->endBlock - dev->startBlock + + 1) * dev->nChunksPerBlock * (dev->nDataBytesPerChunk / + sb->s_blocksize); + buf->f_bfree = + yaffs_GetNumberOfFreeChunks(dev) * (dev->nDataBytesPerChunk / + sb->s_blocksize); + } + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_bavail = buf->f_bfree; + + yaffs_GrossUnlock(dev); + return 0; +} + + +/** +static int yaffs_do_sync_fs(struct super_block *sb) +{ + + yaffs_Device *dev = yaffs_SuperToDevice(sb); + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_do_sync_fs\n")); + + if(sb->s_dirt) { + yaffs_GrossLock(dev); + + if(dev) + yaffs_CheckpointSave(dev); + + yaffs_GrossUnlock(dev); + + sb->s_dirt = 0; + } + return 0; +} +**/ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static void yaffs_write_super(struct super_block *sb) +#else +static int yaffs_write_super(struct super_block *sb) +#endif +{ + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_write_super\n")); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + return 0; /* yaffs_do_sync_fs(sb);*/ +#endif +} + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_sync_fs(struct super_block *sb, int wait) +#else +static int yaffs_sync_fs(struct super_block *sb) +#endif +{ + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_fs\n")); + + return 0; /* yaffs_do_sync_fs(sb);*/ + +} + + +static void yaffs_read_inode(struct inode *inode) +{ + /* NB This is called as a side effect of other functions, but + * we had to release the lock to prevent deadlocks, so + * need to lock again. + */ + + yaffs_Object *obj; + yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_read_inode for %d\n", (int)inode->i_ino)); + + yaffs_GrossLock(dev); + + obj = yaffs_FindObjectByNumber(dev, inode->i_ino); + + yaffs_FillInodeFromObject(inode, obj); + + yaffs_GrossUnlock(dev); +} + +static LIST_HEAD(yaffs_dev_list); + +#if 0 // not used +static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); + + if( *flags & MS_RDONLY ) { + struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_remount_fs: %s: RO\n", dev->name )); + + yaffs_GrossLock(dev); + + yaffs_FlushEntireDeviceCache(dev); + + yaffs_CheckpointSave(dev); + + if (mtd->sync) + mtd->sync(mtd); + + yaffs_GrossUnlock(dev); + } + else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_remount_fs: %s: RW\n", dev->name )); + } + + return 0; +} +#endif + +static void yaffs_put_super(struct super_block *sb) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_put_super\n")); + + yaffs_GrossLock(dev); + + yaffs_FlushEntireDeviceCache(dev); + + yaffs_CheckpointSave(dev); + + if (dev->putSuperFunc) { + dev->putSuperFunc(sb); + } + + yaffs_Deinitialise(dev); + + yaffs_GrossUnlock(dev); + + /* we assume this is protected by lock_kernel() in mount/umount */ + list_del(&dev->devList); + + if(dev->spareBuffer){ + YFREE(dev->spareBuffer); + dev->spareBuffer = NULL; + } + + kfree(dev); +} + + +static void yaffs_MTDPutSuper(struct super_block *sb) +{ + + struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; + + if (mtd->sync) { + mtd->sync(mtd); + } + + put_mtd_device(mtd); +} + + +static void yaffs_MarkSuperBlockDirty(void *vsb) +{ + struct super_block *sb = (struct super_block *)vsb; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_MarkSuperBlockDirty() sb = %p\n",sb)); +// if(sb) +// sb->s_dirt = 1; +} + +typedef struct { + int inband_tags; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; +} yaffs_options; + +#define MAX_OPT_LEN 20 +static int yaffs_parse_options(yaffs_options *options, const char *options_str) +{ + char cur_opt[MAX_OPT_LEN+1]; + int p; + int error = 0; + + /* Parse through the options which is a comma seperated list */ + + while(options_str && *options_str && !error){ + memset(cur_opt,0,MAX_OPT_LEN+1); + p = 0; + + while(*options_str && *options_str != ','){ + if(p < MAX_OPT_LEN){ + cur_opt[p] = *options_str; + p++; + } + options_str++; + } + + if(!strcmp(cur_opt,"inband-tags")) + options->inband_tags = 1; + else if(!strcmp(cur_opt,"no-cache")) + options->no_cache = 1; + else if(!strcmp(cur_opt,"no-checkpoint-read")) + options->skip_checkpoint_read = 1; + else if(!strcmp(cur_opt,"no-checkpoint-write")) + options->skip_checkpoint_write = 1; + else if(!strcmp(cur_opt,"no-checkpoint")){ + options->skip_checkpoint_read = 1; + options->skip_checkpoint_write = 1; + } else { + printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n",cur_opt); + error = 1; + } + + } + + return error; +} + +static struct super_block *yaffs_internal_read_super(int yaffsVersion, + struct super_block *sb, + void *data, int silent) +{ + int nBlocks; + struct inode *inode = NULL; + struct dentry *root; + yaffs_Device *dev = 0; + char devname_buf[BDEVNAME_SIZE + 1]; + struct mtd_info *mtd; + int err; + char *data_str = (char *)data; + + yaffs_options options; + + sb->s_magic = YAFFS_MAGIC; + sb->s_op = &yaffs_super_ops; + + if (!sb) + printk(KERN_INFO "yaffs: sb is NULL\n"); + else if (!sb->s_dev) + printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); + else if (!yaffs_devname(sb, devname_buf)) + printk(KERN_INFO "yaffs: devname is NULL\n"); + else + printk(KERN_INFO "yaffs: dev is %d name is \"%s\"\n", + sb->s_dev, + yaffs_devname(sb, devname_buf)); + + if(!data_str) + data_str = ""; + + printk(KERN_INFO "yaffs: passed flags \"%s\"\n",data_str); + + memset(&options,0,sizeof(options)); + + if(yaffs_parse_options(&options,data_str)){ + /* Option parsing failed */ + return NULL; + } + + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + T(YAFFS_TRACE_OS, ("yaffs_read_super: Using yaffs%d\n", yaffsVersion)); + T(YAFFS_TRACE_OS, + ("yaffs_read_super: block size %d\n", (int)(sb->s_blocksize))); + +#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY + T(YAFFS_TRACE_OS, + ("yaffs: Write verification disabled. All guarantees " + "null and void\n")); +#endif + + T(YAFFS_TRACE_ALWAYS, ("yaffs: Attempting MTD mount on %u.%u, " + "\"%s\"\n", + MAJOR(sb->s_dev), MINOR(sb->s_dev), + yaffs_devname(sb, devname_buf))); + + /* Check it's an mtd device..... */ + if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { + return NULL; /* This isn't an mtd device */ + } + /* Get the device */ + mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); + if (!mtd) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device #%u doesn't appear to exist\n", + MINOR(sb->s_dev))); + return NULL; + } + /* Check it's NAND */ + if (mtd->type != MTD_NANDFLASH) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device is not NAND it's type %d\n", mtd->type)); + return NULL; + } + + T(YAFFS_TRACE_OS, (" erase %p\n", mtd->erase)); + T(YAFFS_TRACE_OS, (" read %p\n", mtd->read)); + T(YAFFS_TRACE_OS, (" write %p\n", mtd->write)); + T(YAFFS_TRACE_OS, (" readoob %p\n", mtd->read_oob)); + T(YAFFS_TRACE_OS, (" writeoob %p\n", mtd->write_oob)); + T(YAFFS_TRACE_OS, (" block_isbad %p\n", mtd->block_isbad)); + T(YAFFS_TRACE_OS, (" block_markbad %p\n", mtd->block_markbad)); + T(YAFFS_TRACE_OS, (" %s %d\n", WRITE_SIZE_STR, WRITE_SIZE(mtd))); + T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize)); + T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize)); + T(YAFFS_TRACE_OS, (" size %d\n", mtd->size)); + +#ifdef CONFIG_YAFFS_AUTO_YAFFS2 + + if (yaffsVersion == 1 && +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + mtd->writesize >= 2048) { +#else + mtd->oobblock >= 2048) { +#endif + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n")); + yaffsVersion = 2; + } + + /* Added NCB 26/5/2006 for completeness */ + if (yaffsVersion == 2 && +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + mtd->writesize == 512) { +#else + mtd->oobblock == 512) { +#endif + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs1\n")); + yaffsVersion = 1; + } + +#endif + + if (yaffsVersion == 2) { + /* Check for version 2 style functions */ + if (!mtd->erase || + !mtd->block_isbad || + !mtd->block_markbad || + !mtd->read || + !mtd->write || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + !mtd->read_oob || !mtd->write_oob) { +#else + !mtd->write_ecc || + !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { +#endif + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support required " + "functions\n"));; + return NULL; + } + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + if (mtd->writesize < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +#else + if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +#endif + mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not have the " + "right page sizes\n")); + return NULL; + } + } else { + /* Check for V1 style functions */ + if (!mtd->erase || + !mtd->read || + !mtd->write || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + !mtd->read_oob || !mtd->write_oob) { +#else + !mtd->write_ecc || + !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { +#endif + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support required " + "functions\n"));; + return NULL; + } + + if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || + mtd->oobsize != YAFFS_BYTES_PER_SPARE) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support have the " + "right page sizes\n")); + return NULL; + } + } + + /* OK, so if we got here, we have an MTD that's NAND and looks + * like it has the right capabilities + * Set the yaffs_Device up for mtd + */ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); +#else + sb->u.generic_sbp = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); +#endif + if (!dev) { + /* Deep shit could not allocate device structure */ + T(YAFFS_TRACE_ALWAYS, + ("yaffs_read_super: Failed trying to allocate " + "yaffs_Device. \n")); + return NULL; + } + + memset(dev, 0, sizeof(yaffs_Device)); + dev->genericDevice = mtd; + dev->name = mtd->name; + + /* Set up the memory size parameters.... */ + + nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + dev->startBlock = 0; + dev->endBlock = nBlocks - 1; + dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK; + dev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK; + dev->nReservedBlocks = 5; + dev->nShortOpCaches = (options.no_cache) ? 0 : 10; + + /* ... and the functions. */ + if (yaffsVersion == 2) { + dev->writeChunkWithTagsToNAND = + nandmtd2_WriteChunkWithTagsToNAND; + dev->readChunkWithTagsFromNAND = + nandmtd2_ReadChunkWithTagsFromNAND; + dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad; + dev->queryNANDBlock = nandmtd2_QueryNANDBlock; + dev->spareBuffer = YMALLOC(mtd->oobsize); + dev->isYaffs2 = 1; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + dev->nDataBytesPerChunk = mtd->writesize; + dev->nChunksPerBlock = mtd->erasesize / mtd->writesize; +#else + dev->nDataBytesPerChunk = mtd->oobblock; + dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; +#endif + nBlocks = mtd->size / mtd->erasesize; + + dev->nCheckpointReservedBlocks = CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS; + dev->startBlock = 0; + dev->endBlock = nBlocks - 1; + } else { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + /* use the MTD interface in yaffs_mtdif1.c */ + dev->writeChunkWithTagsToNAND = + nandmtd1_WriteChunkWithTagsToNAND; + dev->readChunkWithTagsFromNAND = + nandmtd1_ReadChunkWithTagsFromNAND; + dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad; + dev->queryNANDBlock = nandmtd1_QueryNANDBlock; +#else + dev->writeChunkToNAND = nandmtd_WriteChunkToNAND; + dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND; +#endif + dev->isYaffs2 = 0; + } + /* ... and common functions */ + dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND; + dev->initialiseNAND = nandmtd_InitialiseNAND; + + dev->putSuperFunc = yaffs_MTDPutSuper; + + dev->superBlock = (void *)sb; + dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty; + + +#ifndef CONFIG_YAFFS_DOES_ECC + dev->useNANDECC = 1; +#endif + +#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES + dev->wideTnodesDisabled = 1; +#endif + + dev->skipCheckpointRead = options.skip_checkpoint_read; + dev->skipCheckpointWrite = options.skip_checkpoint_write; + + /* we assume this is protected by lock_kernel() in mount/umount */ + list_add_tail(&dev->devList, &yaffs_dev_list); + + init_MUTEX(&dev->grossLock); + + yaffs_GrossLock(dev); + + err = yaffs_GutsInitialise(dev); + + T(YAFFS_TRACE_OS, + ("yaffs_read_super: guts initialised %s\n", + (err == YAFFS_OK) ? "OK" : "FAILED")); + + /* Release lock before yaffs_get_inode() */ + yaffs_GrossUnlock(dev); + + /* Create root inode */ + if (err == YAFFS_OK) + inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, + yaffs_Root(dev)); + + if (!inode) + return NULL; + + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + + T(YAFFS_TRACE_OS, ("yaffs_read_super: got root inode\n")); + + root = d_alloc_root(inode); + + T(YAFFS_TRACE_OS, ("yaffs_read_super: d_alloc_root done\n")); + + if (!root) { + iput(inode); + return NULL; + } + sb->s_root = root; + + T(YAFFS_TRACE_OS, ("yaffs_read_super: done\n")); + return sb; +} + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd, mnt); +} +#else +static struct super_block *yaffs_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd); +} +#endif + +static struct file_system_type yaffs_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs", + .get_sb = yaffs_read_super, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#else +static struct super_block *yaffs_read_super(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent); +} + +static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, + FS_REQUIRES_DEV); +#endif + + +#ifdef CONFIG_YAFFS_YAFFS2 + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs2_read_super(struct file_system_type *fs, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd, mnt); +} +#else +static struct super_block *yaffs2_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd); +} +#endif + +static struct file_system_type yaffs2_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs2", + .get_sb = yaffs2_read_super, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#else +static struct super_block *yaffs2_read_super(struct super_block *sb, + void *data, int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent); +} + +static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, + FS_REQUIRES_DEV); +#endif + +#endif /* CONFIG_YAFFS_YAFFS2 */ + +static struct proc_dir_entry *my_proc_entry; + +static char *yaffs_dump_dev(char *buf, yaffs_Device * dev) +{ + buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock); + buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock); + buf += sprintf(buf, "nDataBytesPerChunk. %d\n", dev->nDataBytesPerChunk); + buf += sprintf(buf, "chunkGroupBits..... %d\n", dev->chunkGroupBits); + buf += sprintf(buf, "chunkGroupSize..... %d\n", dev->chunkGroupSize); + buf += sprintf(buf, "nErasedBlocks...... %d\n", dev->nErasedBlocks); + buf += sprintf(buf, "nReservedBlocks.... %d\n", dev->nReservedBlocks); + buf += sprintf(buf, "nCheckptResBlocks.. %d\n", dev->nCheckpointReservedBlocks); + buf += sprintf(buf, "blocksInCheckpoint. %d\n", dev->blocksInCheckpoint); + buf += sprintf(buf, "nTnodesCreated..... %d\n", dev->nTnodesCreated); + buf += sprintf(buf, "nFreeTnodes........ %d\n", dev->nFreeTnodes); + buf += sprintf(buf, "nObjectsCreated.... %d\n", dev->nObjectsCreated); + buf += sprintf(buf, "nFreeObjects....... %d\n", dev->nFreeObjects); + buf += sprintf(buf, "nFreeChunks........ %d\n", dev->nFreeChunks); + buf += sprintf(buf, "nPageWrites........ %d\n", dev->nPageWrites); + buf += sprintf(buf, "nPageReads......... %d\n", dev->nPageReads); + buf += sprintf(buf, "nBlockErasures..... %d\n", dev->nBlockErasures); + buf += sprintf(buf, "nGCCopies.......... %d\n", dev->nGCCopies); + buf += + sprintf(buf, "garbageCollections. %d\n", dev->garbageCollections); + buf += + sprintf(buf, "passiveGCs......... %d\n", + dev->passiveGarbageCollections); + buf += sprintf(buf, "nRetriedWrites..... %d\n", dev->nRetriedWrites); + buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->nShortOpCaches); + buf += sprintf(buf, "nRetireBlocks...... %d\n", dev->nRetiredBlocks); + buf += sprintf(buf, "eccFixed........... %d\n", dev->eccFixed); + buf += sprintf(buf, "eccUnfixed......... %d\n", dev->eccUnfixed); + buf += sprintf(buf, "tagsEccFixed....... %d\n", dev->tagsEccFixed); + buf += sprintf(buf, "tagsEccUnfixed..... %d\n", dev->tagsEccUnfixed); + buf += sprintf(buf, "cacheHits.......... %d\n", dev->cacheHits); + buf += sprintf(buf, "nDeletedFiles...... %d\n", dev->nDeletedFiles); + buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles); + buf += + sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions); + buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC); + buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2); + + return buf; +} + +static int yaffs_proc_read(char *page, + char **start, + off_t offset, int count, int *eof, void *data) +{ + struct list_head *item; + char *buf = page; + int step = offset; + int n = 0; + + /* Get proc_file_read() to step 'offset' by one on each sucessive call. + * We use 'offset' (*ppos) to indicate where we are in devList. + * This also assumes the user has posted a read buffer large + * enough to hold the complete output; but that's life in /proc. + */ + + *(int *)start = 1; + + /* Print header first */ + if (step == 0) { + buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__ + "\n%s\n%s\n", yaffs_fs_c_version, + yaffs_guts_c_version); + } + + /* hold lock_kernel while traversing yaffs_dev_list */ + lock_kernel(); + + /* Locate and print the Nth entry. Order N-squared but N is small. */ + list_for_each(item, &yaffs_dev_list) { + yaffs_Device *dev = list_entry(item, yaffs_Device, devList); + if (n < step) { + n++; + continue; + } + buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name); + buf = yaffs_dump_dev(buf, dev); + break; + } + unlock_kernel(); + + return buf - page < count ? buf - page : count; +} + +/** + * Set the verbosity of the warnings and error messages. + * + * Note that the names can only be a..z or _ with the current code. + */ + +static struct { + char *mask_name; + unsigned mask_bitfield; +} mask_flags[] = { + {"allocate", YAFFS_TRACE_ALLOCATE}, + {"always", YAFFS_TRACE_ALWAYS}, + {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, + {"buffers", YAFFS_TRACE_BUFFERS}, + {"bug", YAFFS_TRACE_BUG}, + {"checkpt", YAFFS_TRACE_CHECKPOINT}, + {"deletion", YAFFS_TRACE_DELETION}, + {"erase", YAFFS_TRACE_ERASE}, + {"error", YAFFS_TRACE_ERROR}, + {"gc_detail", YAFFS_TRACE_GC_DETAIL}, + {"gc", YAFFS_TRACE_GC}, + {"mtd", YAFFS_TRACE_MTD}, + {"nandaccess", YAFFS_TRACE_NANDACCESS}, + {"os", YAFFS_TRACE_OS}, + {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, + {"scan", YAFFS_TRACE_SCAN}, + {"tracing", YAFFS_TRACE_TRACING}, + + {"verify", YAFFS_TRACE_VERIFY}, + {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, + {"verify_full", YAFFS_TRACE_VERIFY_FULL}, + {"verify_all", YAFFS_TRACE_VERIFY_ALL}, + + {"write", YAFFS_TRACE_WRITE}, + {"all", 0xffffffff}, + {"none", 0}, + {NULL, 0}, +}; + +#define MAX_MASK_NAME_LENGTH 40 +static int yaffs_proc_write(struct file *file, const char *buf, + unsigned long count, void *data) +{ + unsigned rg = 0, mask_bitfield; + char *end; + char *mask_name; + const char *x; + char substring[MAX_MASK_NAME_LENGTH+1]; + int i; + int done = 0; + int add, len = 0; + int pos = 0; + + rg = yaffs_traceMask; + + while (!done && (pos < count)) { + done = 1; + while ((pos < count) && isspace(buf[pos])) { + pos++; + } + + switch (buf[pos]) { + case '+': + case '-': + case '=': + add = buf[pos]; + pos++; + break; + + default: + add = ' '; + break; + } + mask_name = NULL; + + mask_bitfield = simple_strtoul(buf + pos, &end, 0); + if (end > buf + pos) { + mask_name = "numeral"; + len = end - (buf + pos); + pos += len; + done = 0; + } else { + for(x = buf + pos, i = 0; + (*x == '_' || (*x >='a' && *x <= 'z')) && + i write_proc = yaffs_proc_write; + my_proc_entry->read_proc = yaffs_proc_read; + my_proc_entry->data = NULL; + } else { + return -ENOMEM; + } + + /* Now add the file system entries */ + + fsinst = fs_to_install; + + while (fsinst->fst && !error) { + error = register_filesystem(fsinst->fst); + if (!error) { + fsinst->installed = 1; + } + fsinst++; + } + + /* Any errors? uninstall */ + if (error) { + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } + } + + return error; +} + +static void __exit exit_yaffs_fs(void) +{ + + struct file_system_to_install *fsinst; + + T(YAFFS_TRACE_ALWAYS, ("yaffs " __DATE__ " " __TIME__ + " removing. \n")); + + remove_proc_entry("yaffs", &proc_root); + + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } + +} + +module_init(init_yaffs_fs) +module_exit(exit_yaffs_fs) + +MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); +MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2006"); +MODULE_LICENSE("GPL"); diff -Nru linux-2.6.23/fs/yaffs2/yaffs_guts.c kernel.android/fs/yaffs2/yaffs_guts.c --- linux-2.6.23/fs/yaffs2/yaffs_guts.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_guts.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,7474 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + */ + +const char *yaffs_guts_c_version = + "$Id$"; + +#include "yportenv.h" + +#include "yaffsinterface.h" +#include "yaffs_guts.h" +#include "yaffs_tagsvalidity.h" + +#include "yaffs_tagscompat.h" +#ifndef CONFIG_YAFFS_USE_OWN_SORT +#include "yaffs_qsort.h" +#endif +#include "yaffs_nand.h" + +#include "yaffs_checkptrw.h" + +#include "yaffs_nand.h" +#include "yaffs_packedtags2.h" + + +#ifdef CONFIG_YAFFS_WINCE +void yfsd_LockYAFFS(BOOL fsLockOnly); +void yfsd_UnlockYAFFS(BOOL fsLockOnly); +#endif + +#define YAFFS_PASSIVE_GC_CHUNKS 2 + +#include "yaffs_ecc.h" + + +/* Robustification (if it ever comes about...) */ +static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND); +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk); +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags); +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_ExtendedTags * tags); + +/* Other local prototypes */ +static int yaffs_UnlinkObject( yaffs_Object *obj); +static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj); + +static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList); + +static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev, + const __u8 * buffer, + yaffs_ExtendedTags * tags, + int useReserve); +static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, + int chunkInNAND, int inScan); + +static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, + yaffs_ObjectType type); +static void yaffs_AddObjectToDirectory(yaffs_Object * directory, + yaffs_Object * obj); +static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, + int force, int isShrink, int shadows); +static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj); +static int yaffs_CheckStructures(void); +static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, + int chunkOffset, int *limit); +static int yaffs_DoGenericObjectDeletion(yaffs_Object * in); + +static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo); + +static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo); +static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, + int lineNo); + +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, + int chunkInNAND); + +static int yaffs_UnlinkWorker(yaffs_Object * obj); +static void yaffs_DestroyObject(yaffs_Object * obj); + +static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, + int chunkInObject); + +loff_t yaffs_GetFileSize(yaffs_Object * obj); + +static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr); + +static void yaffs_VerifyFreeChunks(yaffs_Device * dev); + +static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in); + +#ifdef YAFFS_PARANOID +static int yaffs_CheckFileSanity(yaffs_Object * in); +#else +#define yaffs_CheckFileSanity(in) +#endif + +static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in); +static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId); + +static void yaffs_InvalidateCheckpoint(yaffs_Device *dev); + +static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags); + +static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos); +static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId); + + +/* Function to calculate chunk and offset */ + +static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset) +{ + if(dev->chunkShift){ + /* Easy-peasy power of 2 case */ + *chunk = (__u32)(addr >> dev->chunkShift); + *offset = (__u32)(addr & dev->chunkMask); + } + else if(dev->crumbsPerChunk) + { + /* Case where we're using "crumbs" */ + *offset = (__u32)(addr & dev->crumbMask); + addr >>= dev->crumbShift; + *chunk = ((__u32)addr)/dev->crumbsPerChunk; + *offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift); + } + else + YBUG(); +} + +/* Function to return the number of shifts for a power of 2 greater than or equal + * to the given number + * Note we don't try to cater for all possible numbers and this does not have to + * be hellishly efficient. + */ + +static __u32 ShiftsGE(__u32 x) +{ + int extraBits; + int nShifts; + + nShifts = extraBits = 0; + + while(x>1){ + if(x & 1) extraBits++; + x>>=1; + nShifts++; + } + + if(extraBits) + nShifts++; + + return nShifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 + */ + +static __u32 ShiftDiv(__u32 x) +{ + int nShifts; + + nShifts = 0; + + if(!x) return 0; + + while( !(x&1)){ + x>>=1; + nShifts++; + } + + return nShifts; +} + + + +/* + * Temporary buffer manipulations. + */ + +static int yaffs_InitialiseTempBuffers(yaffs_Device *dev) +{ + int i; + __u8 *buf = (__u8 *)1; + + memset(dev->tempBuffer,0,sizeof(dev->tempBuffer)); + + for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { + dev->tempBuffer[i].line = 0; /* not in use */ + dev->tempBuffer[i].buffer = buf = + YMALLOC_DMA(dev->nDataBytesPerChunk); + } + + return buf ? YAFFS_OK : YAFFS_FAIL; + +} + +static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) +{ + int i, j; + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->tempBuffer[i].line == 0) { + dev->tempBuffer[i].line = lineNo; + if ((i + 1) > dev->maxTemp) { + dev->maxTemp = i + 1; + for (j = 0; j <= i; j++) + dev->tempBuffer[j].maxLine = + dev->tempBuffer[j].line; + } + + return dev->tempBuffer[i].buffer; + } + } + + T(YAFFS_TRACE_BUFFERS, + (TSTR("Out of temp buffers at line %d, other held by lines:"), + lineNo)); + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line)); + } + T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR))); + + /* + * If we got here then we have to allocate an unmanaged one + * This is not good. + */ + + dev->unmanagedTempAllocations++; + return YMALLOC(dev->nDataBytesPerChunk); + +} + +static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, + int lineNo) +{ + int i; + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->tempBuffer[i].buffer == buffer) { + dev->tempBuffer[i].line = 0; + return; + } + } + + if (buffer) { + /* assume it is an unmanaged one. */ + T(YAFFS_TRACE_BUFFERS, + (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR), + lineNo)); + YFREE(buffer); + dev->unmanagedTempDeallocations++; + } + +} + +/* + * Determine if we have a managed buffer. + */ +int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer) +{ + int i; + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->tempBuffer[i].buffer == buffer) + return 1; + + } + + for (i = 0; i < dev->nShortOpCaches; i++) { + if( dev->srCache[i].data == buffer ) + return 1; + + } + + if (buffer == dev->checkpointBuffer) + return 1; + + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR))); + return 0; +} + + + +/* + * Chunk bitmap manipulations + */ + +static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), + blk)); + YBUG(); + } + return dev->chunkBits + + (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); +} + +static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) +{ + if(blk < dev->internalStartBlock || blk > dev->internalEndBlock || + chunk < 0 || chunk >= dev->nChunksPerBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR),blk,chunk)); + YBUG(); + } +} + +static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + memset(blkBits, 0, dev->chunkBitmapStride); +} + +static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + yaffs_VerifyChunkBitId(dev,blk,chunk); + + blkBits[chunk / 8] &= ~(1 << (chunk & 7)); +} + +static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + yaffs_VerifyChunkBitId(dev,blk,chunk); + + blkBits[chunk / 8] |= (1 << (chunk & 7)); +} + +static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + yaffs_VerifyChunkBitId(dev,blk,chunk); + + return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; +} + +static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + int i; + for (i = 0; i < dev->chunkBitmapStride; i++) { + if (*blkBits) + return 1; + blkBits++; + } + return 0; +} + +static int yaffs_CountChunkBits(yaffs_Device * dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + int i; + int n = 0; + for (i = 0; i < dev->chunkBitmapStride; i++) { + __u8 x = *blkBits; + while(x){ + if(x & 1) + n++; + x >>=1; + } + + blkBits++; + } + return n; +} + +/* + * Verification code + */ + +static int yaffs_SkipVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipFullVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipNANDVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); +} + +static const char * blockStateName[] = { +"Unknown", +"Needs scanning", +"Scanning", +"Empty", +"Allocating", +"Full", +"Dirty", +"Checkpoint", +"Collecting", +"Dead" +}; + +static void yaffs_VerifyBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +{ + int actuallyUsed; + int inUse; + + if(yaffs_SkipVerification(dev)) + return; + + /* Report illegal runtime states */ + if(bi->blockState <0 || bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has undefined state %d"TENDSTR),n,bi->blockState)); + + switch(bi->blockState){ + case YAFFS_BLOCK_STATE_UNKNOWN: + case YAFFS_BLOCK_STATE_SCANNING: + case YAFFS_BLOCK_STATE_NEEDS_SCANNING: + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has bad run-state %s"TENDSTR), + n,blockStateName[bi->blockState])); + } + + /* Check pages in use and soft deletions are legal */ + + actuallyUsed = bi->pagesInUse - bi->softDeletions; + + if(bi->pagesInUse < 0 || bi->pagesInUse > dev->nChunksPerBlock || + bi->softDeletions < 0 || bi->softDeletions > dev->nChunksPerBlock || + actuallyUsed < 0 || actuallyUsed > dev->nChunksPerBlock) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR), + n,bi->pagesInUse,bi->softDeletions)); + + + /* Check chunk bitmap legal */ + inUse = yaffs_CountChunkBits(dev,n); + if(inUse != bi->pagesInUse) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR), + n,bi->pagesInUse,inUse)); + + /* Check that the sequence number is valid. + * Ten million is legal, but is very unlikely + */ + if(dev->isYaffs2 && + (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || bi->blockState == YAFFS_BLOCK_STATE_FULL) && + (bi->sequenceNumber < YAFFS_LOWEST_SEQUENCE_NUMBER || bi->sequenceNumber > 10000000 )) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has suspect sequence number of %d"TENDSTR), + n,bi->sequenceNumber)); + +} + +static void yaffs_VerifyCollectedBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +{ + yaffs_VerifyBlock(dev,bi,n); + + /* After collection the block should be in the erased state */ + /* TODO: This will need to change if we do partial gc */ + + if(bi->blockState != YAFFS_BLOCK_STATE_EMPTY){ + T(YAFFS_TRACE_ERROR,(TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), + n,bi->blockState)); + } +} + +static void yaffs_VerifyBlocks(yaffs_Device *dev) +{ + int i; + int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; + int nIllegalBlockStates = 0; + + + if(yaffs_SkipVerification(dev)) + return; + + memset(nBlocksPerState,0,sizeof(nBlocksPerState)); + + + for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + yaffs_VerifyBlock(dev,bi,i); + + if(bi->blockState >=0 && bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) + nBlocksPerState[bi->blockState]++; + else + nIllegalBlockStates++; + + } + + T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); + T(YAFFS_TRACE_VERIFY,(TSTR("Block summary"TENDSTR))); + + T(YAFFS_TRACE_VERIFY,(TSTR("%d blocks have illegal states"TENDSTR),nIllegalBlockStates)); + if(nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1) + T(YAFFS_TRACE_VERIFY,(TSTR("Too many allocating blocks"TENDSTR))); + + for(i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) + T(YAFFS_TRACE_VERIFY, + (TSTR("%s %d blocks"TENDSTR), + blockStateName[i],nBlocksPerState[i])); + + if(dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) + T(YAFFS_TRACE_VERIFY, + (TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), + dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); + + if(dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) + T(YAFFS_TRACE_VERIFY, + (TSTR("Erased block count wrong dev %d count %d"TENDSTR), + dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])); + + if(nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1) + T(YAFFS_TRACE_VERIFY, + (TSTR("Too many collecting blocks %d (max is 1)"TENDSTR), + nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING])); + + T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); + +} + +/* + * Verify the object header. oh must be valid, but obj and tags may be NULL in which + * case those tests will not be performed. + */ +static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck) +{ + if(yaffs_SkipVerification(obj->myDev)) + return; + + if(!(tags && obj && oh)){ + T(YAFFS_TRACE_VERIFY, + (TSTR("Verifying object header tags %x obj %x oh %x"TENDSTR), + (__u32)tags,(__u32)obj,(__u32)oh)); + return; + } + + if(oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || + oh->type > YAFFS_OBJECT_TYPE_MAX) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header type is illegal value 0x%x"TENDSTR), + tags->objectId, oh->type)); + + if(tags->objectId != obj->objectId) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch objectId %d"TENDSTR), + tags->objectId, obj->objectId)); + + + /* + * Check that the object's parent ids match if parentCheck requested. + * + * Tests do not apply to the root object. + */ + + if(parentCheck && tags->objectId > 1 && !obj->parent) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR), + tags->objectId, oh->parentObjectId)); + + + if(parentCheck && obj->parent && + oh->parentObjectId != obj->parent->objectId && + (oh->parentObjectId != YAFFS_OBJECTID_UNLINKED || + obj->parent->objectId != YAFFS_OBJECTID_DELETED)) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR), + tags->objectId, oh->parentObjectId, obj->parent->objectId)); + + + if(tags->objectId > 1 && oh->name[0] == 0) /* Null name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is NULL"TENDSTR), + obj->objectId)); + + if(tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is 0xFF"TENDSTR), + obj->objectId)); +} + + + +static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = obj->myDev; + int ok = 1; + int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + + if (tn) { + if (level > 0) { + + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ + if (tn->internal[i]) { + ok = yaffs_VerifyTnodeWorker(obj, + tn->internal[i], + level - 1, + (chunkOffset<objectId; + + chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; + + for(i = 0; i < YAFFS_NTNODES_LEVEL0; i++){ + __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + + if(theChunk > 0){ + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); + if(tags.objectId != objectId || tags.chunkId != chunkOffset){ + T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, chunkOffset, theChunk, + tags.objectId, tags.chunkId)); + } + } + chunkOffset++; + } + } + } + + return ok; + +} + + +static void yaffs_VerifyFile(yaffs_Object *obj) +{ + int requiredTallness; + int actualTallness; + __u32 lastChunk; + __u32 x; + __u32 i; + int ok; + yaffs_Device *dev; + yaffs_ExtendedTags tags; + yaffs_Tnode *tn; + __u32 objectId; + + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + dev = obj->myDev; + objectId = obj->objectId; + + /* Check file size is consistent with tnode depth */ + lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1; + x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (x> 0) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + actualTallness = obj->variant.fileVariant.topLevel; + + if(requiredTallness > actualTallness ) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR), + obj->objectId,actualTallness, requiredTallness)); + + + /* Check that the chunks in the tnode tree are all correct. + * We do this by scanning through the tnode tree and + * checking the tags for every chunk match. + */ + + if(yaffs_SkipNANDVerification(dev)) + return; + + for(i = 1; i <= lastChunk; i++){ + tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant,i); + + if (tn) { + __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if(theChunk > 0){ + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); + if(tags.objectId != objectId || tags.chunkId != i){ + T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, i, theChunk, + tags.objectId, tags.chunkId)); + } + } + } + + } + +} + +static void yaffs_VerifyDirectory(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + +} + +static void yaffs_VerifyHardLink(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + /* Verify sane equivalent object */ +} + +static void yaffs_VerifySymlink(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + /* Verify symlink string */ +} + +static void yaffs_VerifySpecial(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; +} + +static void yaffs_VerifyObject(yaffs_Object *obj) +{ + yaffs_Device *dev; + + __u32 chunkMin; + __u32 chunkMax; + + __u32 chunkIdOk; + __u32 chunkIsLive; + + if(!obj) + return; + + dev = obj->myDev; + + if(yaffs_SkipVerification(dev)) + return; + + /* Check sane object header chunk */ + + chunkMin = dev->internalStartBlock * dev->nChunksPerBlock; + chunkMax = (dev->internalEndBlock+1) * dev->nChunksPerBlock - 1; + + chunkIdOk = (obj->chunkId >= chunkMin && obj->chunkId <= chunkMax); + chunkIsLive = chunkIdOk && + yaffs_CheckChunkBit(dev, + obj->chunkId / dev->nChunksPerBlock, + obj->chunkId % dev->nChunksPerBlock); + if(!obj->fake && + (!chunkIdOk || !chunkIsLive)) { + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has chunkId %d %s %s"TENDSTR), + obj->objectId,obj->chunkId, + chunkIdOk ? "" : ",out of range", + chunkIsLive || !chunkIdOk ? "" : ",marked as deleted")); + } + + if(chunkIdOk && chunkIsLive &&!yaffs_SkipNANDVerification(dev)) { + yaffs_ExtendedTags tags; + yaffs_ObjectHeader *oh; + __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); + + oh = (yaffs_ObjectHeader *)buffer; + + yaffs_ReadChunkWithTagsFromNAND(dev, obj->chunkId,buffer, &tags); + + yaffs_VerifyObjectHeader(obj,oh,&tags,1); + + yaffs_ReleaseTempBuffer(dev,buffer,__LINE__); + } + + /* Verify it has a parent */ + if(obj && !obj->fake && + (!obj->parent || obj->parent->myDev != dev)){ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), + obj->objectId,obj->parent)); + } + + /* Verify parent is a directory */ + if(obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY){ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), + obj->objectId,obj->parent->variantType)); + } + + switch(obj->variantType){ + case YAFFS_OBJECT_TYPE_FILE: + yaffs_VerifyFile(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_VerifySymlink(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_VerifyDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_VerifyHardLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_VerifySpecial(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has illegaltype %d"TENDSTR), + obj->objectId,obj->variantType)); + break; + } + + +} + +static void yaffs_VerifyObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + int i; + struct list_head *lh; + + if(yaffs_SkipVerification(dev)) + return; + + /* Iterate through the objects in each hash entry */ + + for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++){ + list_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { + obj = list_entry(lh, yaffs_Object, hashLink); + yaffs_VerifyObject(obj); + } + } + } + +} + + +/* + * Simple hash function. Needs to have a reasonable spread + */ + +static Y_INLINE int yaffs_HashFunction(int n) +{ + n = abs(n); + return (n % YAFFS_NOBJECT_BUCKETS); +} + +/* + * Access functions to useful fake objects + */ + +yaffs_Object *yaffs_Root(yaffs_Device * dev) +{ + return dev->rootDir; +} + +yaffs_Object *yaffs_LostNFound(yaffs_Device * dev) +{ + return dev->lostNFoundDir; +} + + +/* + * Erased NAND checking functions + */ + +int yaffs_CheckFF(__u8 * buffer, int nBytes) +{ + /* Horrible, slow implementation */ + while (nBytes--) { + if (*buffer != 0xFF) + return 0; + buffer++; + } + return 1; +} + +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, + int chunkInNAND) +{ + + int retval = YAFFS_OK; + __u8 *data = yaffs_GetTempBuffer(dev, __LINE__); + yaffs_ExtendedTags tags; + int result; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); + + if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR) + retval = YAFFS_FAIL; + + + if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) { + T(YAFFS_TRACE_NANDACCESS, + (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND)); + retval = YAFFS_FAIL; + } + + yaffs_ReleaseTempBuffer(dev, data, __LINE__); + + return retval; + +} + +static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, + const __u8 * data, + yaffs_ExtendedTags * tags, + int useReserve) +{ + int attempts = 0; + int writeOk = 0; + int chunk; + + yaffs_InvalidateCheckpoint(dev); + + do { + yaffs_BlockInfo *bi = 0; + int erasedOk = 0; + + chunk = yaffs_AllocateChunk(dev, useReserve, &bi); + if (chunk < 0) { + /* no space */ + break; + } + + /* First check this chunk is erased, if it needs + * checking. The checking policy (unless forced + * always on) is as follows: + * + * Check the first page we try to write in a block. + * If the check passes then we don't need to check any + * more. If the check fails, we check again... + * If the block has been erased, we don't need to check. + * + * However, if the block has been prioritised for gc, + * then we think there might be something odd about + * this block and stop using it. + * + * Rationale: We should only ever see chunks that have + * not been erased if there was a partially written + * chunk due to power loss. This checking policy should + * catch that case with very few checks and thus save a + * lot of checks that are most likely not needed. + */ + if (bi->gcPrioritise) { + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + /* try another chunk */ + continue; + } + + /* let's give it a try */ + attempts++; + +#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED + bi->skipErasedCheck = 0; +#endif + if (!bi->skipErasedCheck) { + erasedOk = yaffs_CheckChunkErased(dev, chunk); + if (erasedOk != YAFFS_OK) { + T(YAFFS_TRACE_ERROR, + (TSTR ("**>> yaffs chunk %d was not erased" + TENDSTR), chunk)); + + /* try another chunk */ + continue; + } + bi->skipErasedCheck = 1; + } + + writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, + data, tags); + if (writeOk != YAFFS_OK) { + yaffs_HandleWriteChunkError(dev, chunk, erasedOk); + /* try another chunk */ + continue; + } + + /* Copy the data into the robustification buffer */ + yaffs_HandleWriteChunkOk(dev, chunk, data, tags); + + } while (writeOk != YAFFS_OK && + (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); + + if(!writeOk) + chunk = -1; + + if (attempts > 1) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs write required %d attempts" TENDSTR), + attempts)); + + dev->nRetriedWrites += (attempts - 1); + } + + return chunk; +} + +/* + * Block retiring for handling a broken block. + */ + +static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND) +{ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); + + yaffs_InvalidateCheckpoint(dev); + + yaffs_MarkBlockBad(dev, blockInNAND); + + bi->blockState = YAFFS_BLOCK_STATE_DEAD; + bi->gcPrioritise = 0; + bi->needsRetiring = 0; + + dev->nRetiredBlocks++; +} + +/* + * Functions for robustisizing TODO + * + */ + +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags) +{ +} + +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_ExtendedTags * tags) +{ +} + +void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi) +{ + if(!bi->gcPrioritise){ + bi->gcPrioritise = 1; + dev->hasPendingPrioritisedGCs = 1; + bi->chunkErrorStrikes ++; + + if(bi->chunkErrorStrikes > 3){ + bi->needsRetiring = 1; /* Too many stikes, so retire this */ + T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); + + } + + } +} + +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk) +{ + + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); + + yaffs_HandleChunkError(dev,bi); + + + if(erasedOk ) { + /* Was an actual write failure, so mark the block for retirement */ + bi->needsRetiring = 1; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND)); + + + } + + /* Delete the chunk */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); +} + + +/*---------------- Name handling functions ------------*/ + +static __u16 yaffs_CalcNameSum(const YCHAR * name) +{ + __u16 sum = 0; + __u16 i = 1; + + YUCHAR *bname = (YUCHAR *) name; + if (bname) { + while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) { + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE + sum += yaffs_toupper(*bname) * i; +#else + sum += (*bname) * i; +#endif + i++; + bname++; + } + } + return sum; +} + +static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name) +{ +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) { + yaffs_strcpy(obj->shortName, name); + } else { + obj->shortName[0] = _Y('\0'); + } +#endif + obj->sum = yaffs_CalcNameSum(name); +} + +/*-------------------- TNODES ------------------- + + * List of spare tnodes + * The list is hooked together using the first pointer + * in the tnode. + */ + +/* yaffs_CreateTnodes creates a bunch more tnodes and + * adds them to the tnode free list. + * Don't use this function directly + */ + +static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes) +{ + int i; + int tnodeSize; + yaffs_Tnode *newTnodes; + __u8 *mem; + yaffs_Tnode *curr; + yaffs_Tnode *next; + yaffs_TnodeList *tnl; + + if (nTnodes < 1) + return YAFFS_OK; + + /* Calculate the tnode size in bytes for variable width tnode support. + * Must be a multiple of 32-bits */ + tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + + /* make these things */ + + newTnodes = YMALLOC(nTnodes * tnodeSize); + mem = (__u8 *)newTnodes; + + if (!newTnodes) { + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs: Could not allocate Tnodes" TENDSTR))); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ +#if 0 + for (i = 0; i < nTnodes - 1; i++) { + newTnodes[i].internal[0] = &newTnodes[i + 1]; +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; +#endif + } + + newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; +#endif + dev->freeTnodes = newTnodes; +#else + /* New hookup for wide tnodes */ + for(i = 0; i < nTnodes -1; i++) { + curr = (yaffs_Tnode *) &mem[i * tnodeSize]; + next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize]; + curr->internal[0] = next; + } + + curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize]; + curr->internal[0] = dev->freeTnodes; + dev->freeTnodes = (yaffs_Tnode *)mem; + +#endif + + + dev->nFreeTnodes += nTnodes; + dev->nTnodesCreated += nTnodes; + + /* Now add this bunch of tnodes to a list for freeing up. + * NB If we can't add this to the management list it isn't fatal + * but it just means we can't free this bunch of tnodes later. + */ + + tnl = YMALLOC(sizeof(yaffs_TnodeList)); + if (!tnl) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs: Could not add tnodes to management list" TENDSTR))); + return YAFFS_FAIL; + + } else { + tnl->tnodes = newTnodes; + tnl->next = dev->allocatedTnodeList; + dev->allocatedTnodeList = tnl; + } + + T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR))); + + return YAFFS_OK; +} + +/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */ + +static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev) +{ + yaffs_Tnode *tn = NULL; + + /* If there are none left make more */ + if (!dev->freeTnodes) { + yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); + } + + if (dev->freeTnodes) { + tn = dev->freeTnodes; +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) { + /* Hoosterman, this thing looks like it isn't in the list */ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: Tnode list bug 1" TENDSTR))); + } +#endif + dev->freeTnodes = dev->freeTnodes->internal[0]; + dev->nFreeTnodes--; + } + + return tn; +} + +static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev) +{ + yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev); + + if(tn) + memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn) +{ + if (tn) { +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) { + /* Hoosterman, this thing looks like it is already in the list */ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: Tnode list bug 2" TENDSTR))); + } + tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1; +#endif + tn->internal[0] = dev->freeTnodes; + dev->freeTnodes = tn; + dev->nFreeTnodes++; + } +} + +static void yaffs_DeinitialiseTnodes(yaffs_Device * dev) +{ + /* Free the list of allocated tnodes */ + yaffs_TnodeList *tmp; + + while (dev->allocatedTnodeList) { + tmp = dev->allocatedTnodeList->next; + + YFREE(dev->allocatedTnodeList->tnodes); + YFREE(dev->allocatedTnodeList); + dev->allocatedTnodeList = tmp; + + } + + dev->freeTnodes = NULL; + dev->nFreeTnodes = 0; +} + +static void yaffs_InitialiseTnodes(yaffs_Device * dev) +{ + dev->allocatedTnodeList = NULL; + dev->freeTnodes = NULL; + dev->nFreeTnodes = 0; + dev->nTnodesCreated = 0; + +} + + +void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val) +{ + __u32 *map = (__u32 *)tn; + __u32 bitInMap; + __u32 bitInWord; + __u32 wordInMap; + __u32 mask; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + val >>= dev->chunkGroupBits; + + bitInMap = pos * dev->tnodeWidth; + wordInMap = bitInMap /32; + bitInWord = bitInMap & (32 -1); + + mask = dev->tnodeMask << bitInWord; + + map[wordInMap] &= ~mask; + map[wordInMap] |= (mask & (val << bitInWord)); + + if(dev->tnodeWidth > (32-bitInWord)) { + bitInWord = (32 - bitInWord); + wordInMap++;; + mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord); + map[wordInMap] &= ~mask; + map[wordInMap] |= (mask & (val >> bitInWord)); + } +} + +static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) +{ + __u32 *map = (__u32 *)tn; + __u32 bitInMap; + __u32 bitInWord; + __u32 wordInMap; + __u32 val; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + + bitInMap = pos * dev->tnodeWidth; + wordInMap = bitInMap /32; + bitInWord = bitInMap & (32 -1); + + val = map[wordInMap] >> bitInWord; + + if(dev->tnodeWidth > (32-bitInWord)) { + bitInWord = (32 - bitInWord); + wordInMap++;; + val |= (map[wordInMap] << bitInWord); + } + + val &= dev->tnodeMask; + val <<= dev->chunkGroupBits; + + return val; +} + +/* ------------------- End of individual tnode manipulation -----------------*/ + +/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ + * The look up tree is represented by the top tnode and the number of topLevel + * in the tree. 0 means only the level 0 tnode is in the tree. + */ + +/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ +static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId) +{ + + yaffs_Tnode *tn = fStruct->top; + __u32 i; + int requiredTallness; + int level = fStruct->topLevel; + + /* Check sane level and chunk Id */ + if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) { + return NULL; + } + + if (chunkId > YAFFS_MAX_CHUNK_ID) { + return NULL; + } + + /* First check we're tall enough (ie enough topLevel) */ + + i = chunkId >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (i) { + i >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + if (requiredTallness > fStruct->topLevel) { + /* Not tall enough, so we can't find it, return NULL. */ + return NULL; + } + + /* Traverse down to level 0 */ + while (level > 0 && tn) { + tn = tn-> + internal[(chunkId >> + ( YAFFS_TNODES_LEVEL0_BITS + + (level - 1) * + YAFFS_TNODES_INTERNAL_BITS) + ) & + YAFFS_TNODES_INTERNAL_MASK]; + level--; + + } + + return tn; +} + +/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. + * This happens in two steps: + * 1. If the tree isn't tall enough, then make it taller. + * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. + * + * Used when modifying the tree. + * + * If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will + * be plugged into the ttree. + */ + +static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId, + yaffs_Tnode *passedTn) +{ + + int requiredTallness; + int i; + int l; + yaffs_Tnode *tn; + + __u32 x; + + + /* Check sane level and page Id */ + if (fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) { + return NULL; + } + + if (chunkId > YAFFS_MAX_CHUNK_ID) { + return NULL; + } + + /* First check we're tall enough (ie enough topLevel) */ + + x = chunkId >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (x) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + + if (requiredTallness > fStruct->topLevel) { + /* Not tall enough,gotta make the tree taller */ + for (i = fStruct->topLevel; i < requiredTallness; i++) { + + tn = yaffs_GetTnode(dev); + + if (tn) { + tn->internal[0] = fStruct->top; + fStruct->top = tn; + } else { + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs: no more tnodes" TENDSTR))); + } + } + + fStruct->topLevel = requiredTallness; + } + + /* Traverse down to level 0, adding anything we need */ + + l = fStruct->topLevel; + tn = fStruct->top; + + if(l > 0) { + while (l > 0 && tn) { + x = (chunkId >> + ( YAFFS_TNODES_LEVEL0_BITS + + (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK; + + + if((l>1) && !tn->internal[x]){ + /* Add missing non-level-zero tnode */ + tn->internal[x] = yaffs_GetTnode(dev); + + } else if(l == 1) { + /* Looking from level 1 at level 0 */ + if (passedTn) { + /* If we already have one, then release it.*/ + if(tn->internal[x]) + yaffs_FreeTnode(dev,tn->internal[x]); + tn->internal[x] = passedTn; + + } else if(!tn->internal[x]) { + /* Don't have one, none passed in */ + tn->internal[x] = yaffs_GetTnode(dev); + } + } + + tn = tn->internal[x]; + l--; + } + } else { + /* top is level 0 */ + if(passedTn) { + memcpy(tn,passedTn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + yaffs_FreeTnode(dev,passedTn); + } + } + + return tn; +} + +static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk, + yaffs_ExtendedTags * tags, int objectId, + int chunkInInode) +{ + int j; + + for (j = 0; theChunk && j < dev->chunkGroupSize; j++) { + if (yaffs_CheckChunkBit + (dev, theChunk / dev->nChunksPerBlock, + theChunk % dev->nChunksPerBlock)) { + yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, + tags); + if (yaffs_TagsMatch(tags, objectId, chunkInInode)) { + /* found it; */ + return theChunk; + + } + } + theChunk++; + } + return -1; +} + + +/* DeleteWorker scans backwards through the tnode tree and deletes all the + * chunks and tnodes in the file + * Returns 1 if the tree was deleted. + * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete. + */ + +static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, + int chunkOffset, int *limit) +{ + int i; + int chunkInInode; + int theChunk; + yaffs_ExtendedTags tags; + int foundChunk; + yaffs_Device *dev = in->myDev; + + int allDone = 1; + + if (tn) { + if (level > 0) { + + for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; + i--) { + if (tn->internal[i]) { + if (limit && (*limit) < 0) { + allDone = 0; + } else { + allDone = + yaffs_DeleteWorker(in, + tn-> + internal + [i], + level - + 1, + (chunkOffset + << + YAFFS_TNODES_INTERNAL_BITS) + + i, + limit); + } + if (allDone) { + yaffs_FreeTnode(dev, + tn-> + internal[i]); + tn->internal[i] = NULL; + } + } + + } + return (allDone) ? 1 : 0; + } else if (level == 0) { + int hitLimit = 0; + + for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit; + i--) { + theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if (theChunk) { + + chunkInInode = + (chunkOffset << + YAFFS_TNODES_LEVEL0_BITS) + i; + + foundChunk = + yaffs_FindChunkInGroup(dev, + theChunk, + &tags, + in->objectId, + chunkInInode); + + if (foundChunk > 0) { + yaffs_DeleteChunk(dev, + foundChunk, 1, + __LINE__); + in->nDataChunks--; + if (limit) { + *limit = *limit - 1; + if (*limit <= 0) { + hitLimit = 1; + } + } + + } + + yaffs_PutLevel0Tnode(dev,tn,i,0); + } + + } + return (i < 0) ? 1 : 0; + + } + + } + + return 1; + +} + +static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) +{ + + yaffs_BlockInfo *theBlock; + + T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk)); + + theBlock = yaffs_GetBlockInfo(dev, chunk / dev->nChunksPerBlock); + if (theBlock) { + theBlock->softDeletions++; + dev->nFreeChunks++; + } +} + +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file. + * All soft deleting does is increment the block's softdelete count and pulls the chunk out + * of the tnode. + * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted. + */ + +static int yaffs_SoftDeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + int theChunk; + int allDone = 1; + yaffs_Device *dev = in->myDev; + + if (tn) { + if (level > 0) { + + for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; + i--) { + if (tn->internal[i]) { + allDone = + yaffs_SoftDeleteWorker(in, + tn-> + internal[i], + level - 1, + (chunkOffset + << + YAFFS_TNODES_INTERNAL_BITS) + + i); + if (allDone) { + yaffs_FreeTnode(dev, + tn-> + internal[i]); + tn->internal[i] = NULL; + } else { + /* Hoosterman... how could this happen? */ + } + } + } + return (allDone) ? 1 : 0; + } else if (level == 0) { + + for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { + theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if (theChunk) { + /* Note this does not find the real chunk, only the chunk group. + * We make an assumption that a chunk group is not larger than + * a block. + */ + yaffs_SoftDeleteChunk(dev, theChunk); + yaffs_PutLevel0Tnode(dev,tn,i,0); + } + + } + return 1; + + } + + } + + return 1; + +} + +static void yaffs_SoftDeleteFile(yaffs_Object * obj) +{ + if (obj->deleted && + obj->variantType == YAFFS_OBJECT_TYPE_FILE && !obj->softDeleted) { + if (obj->nDataChunks <= 0) { + /* Empty file with no duplicate object headers, just delete it immediately */ + yaffs_FreeTnode(obj->myDev, + obj->variant.fileVariant.top); + obj->variant.fileVariant.top = NULL; + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: Deleting empty file %d" TENDSTR), + obj->objectId)); + yaffs_DoGenericObjectDeletion(obj); + } else { + yaffs_SoftDeleteWorker(obj, + obj->variant.fileVariant.top, + obj->variant.fileVariant. + topLevel, 0); + obj->softDeleted = 1; + } + } +} + +/* Pruning removes any part of the file structure tree that is beyond the + * bounds of the file (ie that does not point to chunks). + * + * A file should only get pruned when its size is reduced. + * + * Before pruning, the chunks must be pulled from the tree and the + * level 0 tnode entries must be zeroed out. + * Could also use this for file deletion, but that's probably better handled + * by a special case. + */ + +static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device * dev, yaffs_Tnode * tn, + __u32 level, int del0) +{ + int i; + int hasData; + + if (tn) { + hasData = 0; + + for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i] && level > 0) { + tn->internal[i] = + yaffs_PruneWorker(dev, tn->internal[i], + level - 1, + (i == 0) ? del0 : 1); + } + + if (tn->internal[i]) { + hasData++; + } + } + + if (hasData == 0 && del0) { + /* Free and return NULL */ + + yaffs_FreeTnode(dev, tn); + tn = NULL; + } + + } + + return tn; + +} + +static int yaffs_PruneFileStructure(yaffs_Device * dev, + yaffs_FileStructure * fStruct) +{ + int i; + int hasData; + int done = 0; + yaffs_Tnode *tn; + + if (fStruct->topLevel > 0) { + fStruct->top = + yaffs_PruneWorker(dev, fStruct->top, fStruct->topLevel, 0); + + /* Now we have a tree with all the non-zero branches NULL but the height + * is the same as it was. + * Let's see if we can trim internal tnodes to shorten the tree. + * We can do this if only the 0th element in the tnode is in use + * (ie all the non-zero are NULL) + */ + + while (fStruct->topLevel && !done) { + tn = fStruct->top; + + hasData = 0; + for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i]) { + hasData++; + } + } + + if (!hasData) { + fStruct->top = tn->internal[0]; + fStruct->topLevel--; + yaffs_FreeTnode(dev, tn); + } else { + done = 1; + } + } + } + + return YAFFS_OK; +} + +/*-------------------- End of File Structure functions.-------------------*/ + +/* yaffs_CreateFreeObjects creates a bunch more objects and + * adds them to the object free list. + */ +static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects) +{ + int i; + yaffs_Object *newObjects; + yaffs_ObjectList *list; + + if (nObjects < 1) + return YAFFS_OK; + + /* make these things */ + newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); + list = YMALLOC(sizeof(yaffs_ObjectList)); + + if (!newObjects || !list) { + if(newObjects) + YFREE(newObjects); + if(list) + YFREE(list); + T(YAFFS_TRACE_ALLOCATE, + (TSTR("yaffs: Could not allocate more objects" TENDSTR))); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ + for (i = 0; i < nObjects - 1; i++) { + newObjects[i].siblings.next = + (struct list_head *)(&newObjects[i + 1]); + } + + newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects; + dev->freeObjects = newObjects; + dev->nFreeObjects += nObjects; + dev->nObjectsCreated += nObjects; + + /* Now add this bunch of Objects to a list for freeing up. */ + + list->objects = newObjects; + list->next = dev->allocatedObjectList; + dev->allocatedObjectList = list; + + return YAFFS_OK; +} + + +/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */ +static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev) +{ + yaffs_Object *tn = NULL; + + /* If there are none left make more */ + if (!dev->freeObjects) { + yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS); + } + + if (dev->freeObjects) { + tn = dev->freeObjects; + dev->freeObjects = + (yaffs_Object *) (dev->freeObjects->siblings.next); + dev->nFreeObjects--; + + /* Now sweeten it up... */ + + memset(tn, 0, sizeof(yaffs_Object)); + tn->myDev = dev; + tn->chunkId = -1; + tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; + INIT_LIST_HEAD(&(tn->hardLinks)); + INIT_LIST_HEAD(&(tn->hashLink)); + INIT_LIST_HEAD(&tn->siblings); + + /* Add it to the lost and found directory. + * NB Can't put root or lostNFound in lostNFound so + * check if lostNFound exists first + */ + if (dev->lostNFoundDir) { + yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn); + } + } + + return tn; +} + +static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number, + __u32 mode) +{ + + yaffs_Object *obj = + yaffs_CreateNewObject(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); + if (obj) { + obj->fake = 1; /* it is fake so it has no NAND presence... */ + obj->renameAllowed = 0; /* ... and we're not allowed to rename it... */ + obj->unlinkAllowed = 0; /* ... or unlink it */ + obj->deleted = 0; + obj->unlinked = 0; + obj->yst_mode = mode; + obj->myDev = dev; + obj->chunkId = 0; /* Not a valid chunk. */ + } + + return obj; + +} + +static void yaffs_UnhashObject(yaffs_Object * tn) +{ + int bucket; + yaffs_Device *dev = tn->myDev; + + /* If it is still linked into the bucket list, free from the list */ + if (!list_empty(&tn->hashLink)) { + list_del_init(&tn->hashLink); + bucket = yaffs_HashFunction(tn->objectId); + dev->objectBucket[bucket].count--; + } + +} + +/* FreeObject frees up a Object and puts it back on the free list */ +static void yaffs_FreeObject(yaffs_Object * tn) +{ + + yaffs_Device *dev = tn->myDev; + +#ifdef __KERNEL__ + if (tn->myInode) { + /* We're still hooked up to a cached inode. + * Don't delete now, but mark for later deletion + */ + tn->deferedFree = 1; + return; + } +#endif + + yaffs_UnhashObject(tn); + + /* Link into the free list. */ + tn->siblings.next = (struct list_head *)(dev->freeObjects); + dev->freeObjects = tn; + dev->nFreeObjects++; +} + +#ifdef __KERNEL__ + +void yaffs_HandleDeferedFree(yaffs_Object * obj) +{ + if (obj->deferedFree) { + yaffs_FreeObject(obj); + } +} + +#endif + +static void yaffs_DeinitialiseObjects(yaffs_Device * dev) +{ + /* Free the list of allocated Objects */ + + yaffs_ObjectList *tmp; + + while (dev->allocatedObjectList) { + tmp = dev->allocatedObjectList->next; + YFREE(dev->allocatedObjectList->objects); + YFREE(dev->allocatedObjectList); + + dev->allocatedObjectList = tmp; + } + + dev->freeObjects = NULL; + dev->nFreeObjects = 0; +} + +static void yaffs_InitialiseObjects(yaffs_Device * dev) +{ + int i; + + dev->allocatedObjectList = NULL; + dev->freeObjects = NULL; + dev->nFreeObjects = 0; + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + INIT_LIST_HEAD(&dev->objectBucket[i].list); + dev->objectBucket[i].count = 0; + } + +} + +static int yaffs_FindNiceObjectBucket(yaffs_Device * dev) +{ + static int x = 0; + int i; + int l = 999; + int lowest = 999999; + + /* First let's see if we can find one that's empty. */ + + for (i = 0; i < 10 && lowest > 0; i++) { + x++; + x %= YAFFS_NOBJECT_BUCKETS; + if (dev->objectBucket[x].count < lowest) { + lowest = dev->objectBucket[x].count; + l = x; + } + + } + + /* If we didn't find an empty list, then try + * looking a bit further for a short one + */ + + for (i = 0; i < 10 && lowest > 3; i++) { + x++; + x %= YAFFS_NOBJECT_BUCKETS; + if (dev->objectBucket[x].count < lowest) { + lowest = dev->objectBucket[x].count; + l = x; + } + + } + + return l; +} + +static int yaffs_CreateNewObjectNumber(yaffs_Device * dev) +{ + int bucket = yaffs_FindNiceObjectBucket(dev); + + /* Now find an object value that has not already been taken + * by scanning the list. + */ + + int found = 0; + struct list_head *i; + + __u32 n = (__u32) bucket; + + /* yaffs_CheckObjectHashSanity(); */ + + while (!found) { + found = 1; + n += YAFFS_NOBJECT_BUCKETS; + if (1 || dev->objectBucket[bucket].count > 0) { + list_for_each(i, &dev->objectBucket[bucket].list) { + /* If there is already one in the list */ + if (i + && list_entry(i, yaffs_Object, + hashLink)->objectId == n) { + found = 0; + } + } + } + } + + + return n; +} + +static void yaffs_HashObject(yaffs_Object * in) +{ + int bucket = yaffs_HashFunction(in->objectId); + yaffs_Device *dev = in->myDev; + + list_add(&in->hashLink, &dev->objectBucket[bucket].list); + dev->objectBucket[bucket].count++; + +} + +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number) +{ + int bucket = yaffs_HashFunction(number); + struct list_head *i; + yaffs_Object *in; + + list_for_each(i, &dev->objectBucket[bucket].list) { + /* Look if it is in the list */ + if (i) { + in = list_entry(i, yaffs_Object, hashLink); + if (in->objectId == number) { +#ifdef __KERNEL__ + /* Don't tell the VFS about this one if it is defered free */ + if (in->deferedFree) + return NULL; +#endif + + return in; + } + } + } + + return NULL; +} + +yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, + yaffs_ObjectType type) +{ + + yaffs_Object *theObject; + yaffs_Tnode *tn; + + if (number < 0) { + number = yaffs_CreateNewObjectNumber(dev); + } + + theObject = yaffs_AllocateEmptyObject(dev); + if(!theObject) + return NULL; + + if(type == YAFFS_OBJECT_TYPE_FILE){ + tn = yaffs_GetTnode(dev); + if(!tn){ + yaffs_FreeObject(theObject); + return NULL; + } + } + + + + if (theObject) { + theObject->fake = 0; + theObject->renameAllowed = 1; + theObject->unlinkAllowed = 1; + theObject->objectId = number; + yaffs_HashObject(theObject); + theObject->variantType = type; +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(theObject->win_atime); + theObject->win_ctime[0] = theObject->win_mtime[0] = + theObject->win_atime[0]; + theObject->win_ctime[1] = theObject->win_mtime[1] = + theObject->win_atime[1]; + +#else + + theObject->yst_atime = theObject->yst_mtime = + theObject->yst_ctime = Y_CURRENT_TIME; +#endif + switch (type) { + case YAFFS_OBJECT_TYPE_FILE: + theObject->variant.fileVariant.fileSize = 0; + theObject->variant.fileVariant.scannedFileSize = 0; + theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */ + theObject->variant.fileVariant.topLevel = 0; + theObject->variant.fileVariant.top = tn; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&theObject->variant.directoryVariant. + children); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + /* No action required */ + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* todo this should not happen */ + break; + } + } + + return theObject; +} + +static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device * dev, + int number, + yaffs_ObjectType type) +{ + yaffs_Object *theObject = NULL; + + if (number > 0) { + theObject = yaffs_FindObjectByNumber(dev, number); + } + + if (!theObject) { + theObject = yaffs_CreateNewObject(dev, number, type); + } + + return theObject; + +} + + +static YCHAR *yaffs_CloneString(const YCHAR * str) +{ + YCHAR *newStr = NULL; + + if (str && *str) { + newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); + if(newStr) + yaffs_strcpy(newStr, str); + } + + return newStr; + +} + +/* + * Mknod (create) a new object. + * equivalentObject only has meaning for a hard link; + * aliasString only has meaning for a sumlink. + * rdev only has meaning for devices (a subset of special objects) + */ + +static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, + yaffs_Object * parent, + const YCHAR * name, + __u32 mode, + __u32 uid, + __u32 gid, + yaffs_Object * equivalentObject, + const YCHAR * aliasString, __u32 rdev) +{ + yaffs_Object *in; + YCHAR *str; + + yaffs_Device *dev = parent->myDev; + + /* Check if the entry exists. If it does then fail the call since we don't want a dup.*/ + if (yaffs_FindObjectByName(parent, name)) { + return NULL; + } + + in = yaffs_CreateNewObject(dev, -1, type); + + if(type == YAFFS_OBJECT_TYPE_SYMLINK){ + str = yaffs_CloneString(aliasString); + if(!str){ + yaffs_FreeObject(in); + return NULL; + } + } + + + + if (in) { + in->chunkId = -1; + in->valid = 1; + in->variantType = type; + + in->yst_mode = mode; + +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(in->win_atime); + in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0]; + in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1]; + +#else + in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME; + + in->yst_rdev = rdev; + in->yst_uid = uid; + in->yst_gid = gid; +#endif + in->nDataChunks = 0; + + yaffs_SetObjectName(in, name); + in->dirty = 1; + + yaffs_AddObjectToDirectory(parent, in); + + in->myDev = parent->myDev; + + switch (type) { + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symLinkVariant.alias = str; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant.equivalentObject = + equivalentObject; + in->variant.hardLinkVariant.equivalentObjectId = + equivalentObject->objectId; + list_add(&in->hardLinks, &equivalentObject->hardLinks); + break; + case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_DIRECTORY: + case YAFFS_OBJECT_TYPE_SPECIAL: + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* do nothing */ + break; + } + + if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) { + /* Could not create the object header, fail the creation */ + yaffs_DestroyObject(in); + in = NULL; + } + + } + + return in; +} + +yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, + uid, gid, NULL, NULL, 0); +} + +yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, + mode, uid, gid, NULL, NULL, 0); +} + +yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, __u32 rdev) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, + uid, gid, NULL, NULL, rdev); +} + +yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, + const YCHAR * alias) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, + uid, gid, NULL, alias, 0); +} + +/* yaffs_Link returns the object id of the equivalent object.*/ +yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, + yaffs_Object * equivalentObject) +{ + /* Get the real object in case we were fed a hard link as an equivalent object */ + equivalentObject = yaffs_GetEquivalentObject(equivalentObject); + + if (yaffs_MknodObject + (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0, + equivalentObject, NULL, 0)) { + return equivalentObject; + } else { + return NULL; + } + +} + +static int yaffs_ChangeObjectName(yaffs_Object * obj, yaffs_Object * newDir, + const YCHAR * newName, int force, int shadows) +{ + int unlinkOp; + int deleteOp; + + yaffs_Object *existingTarget; + + if (newDir == NULL) { + newDir = obj->parent; /* use the old directory */ + } + + if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragendy: yaffs_ChangeObjectName: newDir is not a directory" + TENDSTR))); + YBUG(); + } + + /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ + if (obj->myDev->isYaffs2) { + unlinkOp = (newDir == obj->myDev->unlinkedDir); + } else { + unlinkOp = (newDir == obj->myDev->unlinkedDir + && obj->variantType == YAFFS_OBJECT_TYPE_FILE); + } + + deleteOp = (newDir == obj->myDev->deletedDir); + + existingTarget = yaffs_FindObjectByName(newDir, newName); + + /* If the object is a file going into the unlinked directory, + * then it is OK to just stuff it in since duplicate names are allowed. + * else only proceed if the new name does not exist and if we're putting + * it into a directory. + */ + if ((unlinkOp || + deleteOp || + force || + (shadows > 0) || + !existingTarget) && + newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_SetObjectName(obj, newName); + obj->dirty = 1; + + yaffs_AddObjectToDirectory(newDir, obj); + + if (unlinkOp) + obj->unlinked = 1; + + /* If it is a deletion then we mark it as a shrink for gc purposes. */ + if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows)>= 0) + return YAFFS_OK; + } + + return YAFFS_FAIL; +} + +int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, + yaffs_Object * newDir, const YCHAR * newName) +{ + yaffs_Object *obj; + yaffs_Object *existingTarget; + int force = 0; + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE + /* Special case for case insemsitive systems (eg. WinCE). + * While look-up is case insensitive, the name isn't. + * Therefore we might want to change x.txt to X.txt + */ + if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) { + force = 1; + } +#endif + + obj = yaffs_FindObjectByName(oldDir, oldName); + /* Check new name to long. */ + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK && + yaffs_strlen(newName) > YAFFS_MAX_ALIAS_LENGTH) + /* ENAMETOOLONG */ + return YAFFS_FAIL; + else if (obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK && + yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) + /* ENAMETOOLONG */ + return YAFFS_FAIL; + + if (obj && obj->renameAllowed) { + + /* Now do the handling for an existing target, if there is one */ + + existingTarget = yaffs_FindObjectByName(newDir, newName); + if (existingTarget && + existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&existingTarget->variant.directoryVariant.children)) { + /* There is a target that is a non-empty directory, so we fail */ + return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */ + } else if (existingTarget && existingTarget != obj) { + /* Nuke the target first, using shadowing, + * but only if it isn't the same object + */ + yaffs_ChangeObjectName(obj, newDir, newName, force, + existingTarget->objectId); + yaffs_UnlinkObject(existingTarget); + } + + return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); + } + return YAFFS_FAIL; +} + +/*------------------------- Block Management and Page Allocation ----------------*/ + +static int yaffs_InitialiseBlocks(yaffs_Device * dev) +{ + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + + dev->blockInfo = NULL; + dev->chunkBits = NULL; + + dev->allocationBlock = -1; /* force it to get a new one */ + + /* If the first allocation strategy fails, thry the alternate one */ + dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo)); + if(!dev->blockInfo){ + dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo)); + dev->blockInfoAlt = 1; + } + else + dev->blockInfoAlt = 0; + + if(dev->blockInfo){ + + /* Set up dynamic blockinfo stuff. */ + dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */ + dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks); + if(!dev->chunkBits){ + dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks); + dev->chunkBitsAlt = 1; + } + else + dev->chunkBitsAlt = 0; + } + + if (dev->blockInfo && dev->chunkBits) { + memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo)); + memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks); + return YAFFS_OK; + } + + return YAFFS_FAIL; + +} + +static void yaffs_DeinitialiseBlocks(yaffs_Device * dev) +{ + if(dev->blockInfoAlt && dev->blockInfo) + YFREE_ALT(dev->blockInfo); + else if(dev->blockInfo) + YFREE(dev->blockInfo); + + dev->blockInfoAlt = 0; + + dev->blockInfo = NULL; + + if(dev->chunkBitsAlt && dev->chunkBits) + YFREE_ALT(dev->chunkBits); + else if(dev->chunkBits) + YFREE(dev->chunkBits); + dev->chunkBitsAlt = 0; + dev->chunkBits = NULL; +} + +static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev, + yaffs_BlockInfo * bi) +{ + int i; + __u32 seq; + yaffs_BlockInfo *b; + + if (!dev->isYaffs2) + return 1; /* disqualification only applies to yaffs2. */ + + if (!bi->hasShrinkHeader) + return 1; /* can gc */ + + /* Find the oldest dirty sequence number if we don't know it and save it + * so we don't have to keep recomputing it. + */ + if (!dev->oldestDirtySequence) { + seq = dev->sequenceNumber; + + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; + i++) { + b = yaffs_GetBlockInfo(dev, i); + if (b->blockState == YAFFS_BLOCK_STATE_FULL && + (b->pagesInUse - b->softDeletions) < + dev->nChunksPerBlock && b->sequenceNumber < seq) { + seq = b->sequenceNumber; + } + } + dev->oldestDirtySequence = seq; + } + + /* Can't do gc of this block if there are any blocks older than this one that have + * discarded pages. + */ + return (bi->sequenceNumber <= dev->oldestDirtySequence); + +} + +/* FindDiretiestBlock is used to select the dirtiest block (or close enough) + * for garbage collection. + */ + +static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, + int aggressive) +{ + + int b = dev->currentDirtyChecker; + + int i; + int iterations; + int dirtiest = -1; + int pagesInUse = 0; + int prioritised=0; + yaffs_BlockInfo *bi; + int pendingPrioritisedExist = 0; + + /* First let's see if we need to grab a prioritised block */ + if(dev->hasPendingPrioritisedGCs){ + for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){ + + bi = yaffs_GetBlockInfo(dev, i); + //yaffs_VerifyBlock(dev,bi,i); + + if(bi->gcPrioritise) { + pendingPrioritisedExist = 1; + if(bi->blockState == YAFFS_BLOCK_STATE_FULL && + yaffs_BlockNotDisqualifiedFromGC(dev, bi)){ + pagesInUse = (bi->pagesInUse - bi->softDeletions); + dirtiest = i; + prioritised = 1; + aggressive = 1; /* Fool the non-aggressive skip logiv below */ + } + } + } + + if(!pendingPrioritisedExist) /* None found, so we can clear this */ + dev->hasPendingPrioritisedGCs = 0; + } + + /* If we're doing aggressive GC then we are happy to take a less-dirty block, and + * search harder. + * else (we're doing a leasurely gc), then we only bother to do this if the + * block has only a few pages in use. + */ + + dev->nonAggressiveSkip--; + + if (!aggressive && (dev->nonAggressiveSkip > 0)) { + return -1; + } + + if(!prioritised) + pagesInUse = + (aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1; + + if (aggressive) { + iterations = + dev->internalEndBlock - dev->internalStartBlock + 1; + } else { + iterations = + dev->internalEndBlock - dev->internalStartBlock + 1; + iterations = iterations / 16; + if (iterations > 200) { + iterations = 200; + } + } + + for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) { + b++; + if (b < dev->internalStartBlock || b > dev->internalEndBlock) { + b = dev->internalStartBlock; + } + + if (b < dev->internalStartBlock || b > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> Block %d is not valid" TENDSTR), b)); + YBUG(); + } + + bi = yaffs_GetBlockInfo(dev, b); + +#if 0 + if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) { + dirtiest = b; + pagesInUse = 0; + } + else +#endif + + if (bi->blockState == YAFFS_BLOCK_STATE_FULL && + (bi->pagesInUse - bi->softDeletions) < pagesInUse && + yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { + dirtiest = b; + pagesInUse = (bi->pagesInUse - bi->softDeletions); + } + } + + dev->currentDirtyChecker = b; + + if (dirtiest > 0) { + T(YAFFS_TRACE_GC, + (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest, + dev->nChunksPerBlock - pagesInUse,prioritised)); + } + + dev->oldestDirtySequence = 0; + + if (dirtiest > 0) { + dev->nonAggressiveSkip = 4; + } + + return dirtiest; +} + +static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo) +{ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo); + + int erasedOk = 0; + + /* If the block is still healthy erase it and mark as clean. + * If the block has had a data failure, then retire it. + */ + + T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, + (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR), + blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : "")); + + bi->blockState = YAFFS_BLOCK_STATE_DIRTY; + + if (!bi->needsRetiring) { + yaffs_InvalidateCheckpoint(dev); + erasedOk = yaffs_EraseBlockInNAND(dev, blockNo); + if (!erasedOk) { + dev->nErasureFailures++; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Erasure failed %d" TENDSTR), blockNo)); + } + } + + if (erasedOk && + ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) { + int i; + for (i = 0; i < dev->nChunksPerBlock; i++) { + if (!yaffs_CheckChunkErased + (dev, blockNo * dev->nChunksPerBlock + i)) { + T(YAFFS_TRACE_ERROR, + (TSTR + (">>Block %d erasure supposedly OK, but chunk %d not erased" + TENDSTR), blockNo, i)); + } + } + } + + if (erasedOk) { + /* Clean it up... */ + bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + bi->pagesInUse = 0; + bi->softDeletions = 0; + bi->hasShrinkHeader = 0; + bi->skipErasedCheck = 1; /* This is clean, so no need to check */ + bi->gcPrioritise = 0; + yaffs_ClearChunkBits(dev, blockNo); + + T(YAFFS_TRACE_ERASE, + (TSTR("Erased block %d" TENDSTR), blockNo)); + } else { + dev->nFreeChunks -= dev->nChunksPerBlock; /* We lost a block of free space */ + + yaffs_RetireBlock(dev, blockNo); + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Block %d retired" TENDSTR), blockNo)); + } +} + +static int yaffs_FindBlockForAllocation(yaffs_Device * dev) +{ + int i; + + yaffs_BlockInfo *bi; + + if (dev->nErasedBlocks < 1) { + /* Hoosterman we've got a problem. + * Can't get space to gc + */ + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR))); + + return -1; + } + + /* Find an empty block. */ + + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + dev->allocationBlockFinder++; + if (dev->allocationBlockFinder < dev->internalStartBlock + || dev->allocationBlockFinder > dev->internalEndBlock) { + dev->allocationBlockFinder = dev->internalStartBlock; + } + + bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder); + + if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) { + bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING; + dev->sequenceNumber++; + bi->sequenceNumber = dev->sequenceNumber; + dev->nErasedBlocks--; + T(YAFFS_TRACE_ALLOCATE, + (TSTR("Allocated block %d, seq %d, %d left" TENDSTR), + dev->allocationBlockFinder, dev->sequenceNumber, + dev->nErasedBlocks)); + return dev->allocationBlockFinder; + } + } + + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs tragedy: no more eraased blocks, but there should have been %d" + TENDSTR), dev->nErasedBlocks)); + + return -1; +} + + +// Check if there's space to allocate... +// Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()? +static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev) +{ + int reservedChunks; + int reservedBlocks = dev->nReservedBlocks; + int checkpointBlocks; + + checkpointBlocks = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; + if(checkpointBlocks < 0) + checkpointBlocks = 0; + + reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->nChunksPerBlock); + + return (dev->nFreeChunks > reservedChunks); +} + +static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr) +{ + int retVal; + yaffs_BlockInfo *bi; + + if (dev->allocationBlock < 0) { + /* Get next block to allocate off */ + dev->allocationBlock = yaffs_FindBlockForAllocation(dev); + dev->allocationPage = 0; + } + + if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) { + /* Not enough space to allocate unless we're allowed to use the reserve. */ + return -1; + } + + if (dev->nErasedBlocks < dev->nReservedBlocks + && dev->allocationPage == 0) { + T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR))); + } + + /* Next page please.... */ + if (dev->allocationBlock >= 0) { + bi = yaffs_GetBlockInfo(dev, dev->allocationBlock); + + retVal = (dev->allocationBlock * dev->nChunksPerBlock) + + dev->allocationPage; + bi->pagesInUse++; + yaffs_SetChunkBit(dev, dev->allocationBlock, + dev->allocationPage); + + dev->allocationPage++; + + dev->nFreeChunks--; + + /* If the block is full set the state to full */ + if (dev->allocationPage >= dev->nChunksPerBlock) { + bi->blockState = YAFFS_BLOCK_STATE_FULL; + dev->allocationBlock = -1; + } + + if(blockUsedPtr) + *blockUsedPtr = bi; + + return retVal; + } + + T(YAFFS_TRACE_ERROR, + (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); + + return -1; +} + +static int yaffs_GetErasedChunks(yaffs_Device * dev) +{ + int n; + + n = dev->nErasedBlocks * dev->nChunksPerBlock; + + if (dev->allocationBlock > 0) { + n += (dev->nChunksPerBlock - dev->allocationPage); + } + + return n; + +} + +static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) +{ + int oldChunk; + int newChunk; + int chunkInBlock; + int markNAND; + int retVal = YAFFS_OK; + int cleanups = 0; + int i; + int isCheckpointBlock; + int matchingChunk; + + int chunksBefore = yaffs_GetErasedChunks(dev); + int chunksAfter; + + yaffs_ExtendedTags tags; + + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block); + + yaffs_Object *object; + + isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT); + + bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; + + T(YAFFS_TRACE_TRACING, + (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block, + bi->pagesInUse, bi->hasShrinkHeader)); + + /*yaffs_VerifyFreeChunks(dev); */ + + bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */ + + /* Take off the number of soft deleted entries because + * they're going to get really deleted during GC. + */ + dev->nFreeChunks -= bi->softDeletions; + + dev->isDoingGC = 1; + + if (isCheckpointBlock || + !yaffs_StillSomeChunkBits(dev, block)) { + T(YAFFS_TRACE_TRACING, + (TSTR + ("Collecting block %d that has no chunks in use" TENDSTR), + block)); + yaffs_BlockBecameDirty(dev, block); + } else { + + __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); + + yaffs_VerifyBlock(dev,bi,block); + + for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock; + chunkInBlock < dev->nChunksPerBlock + && yaffs_StillSomeChunkBits(dev, block); + chunkInBlock++, oldChunk++) { + if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) { + + /* This page is in use and might need to be copied off */ + + markNAND = 1; + + yaffs_InitialiseTags(&tags); + + yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk, + buffer, &tags); + + object = + yaffs_FindObjectByNumber(dev, + tags.objectId); + + T(YAFFS_TRACE_GC_DETAIL, + (TSTR + ("Collecting page %d, %d %d %d " TENDSTR), + chunkInBlock, tags.objectId, tags.chunkId, + tags.byteCount)); + + if(object && !yaffs_SkipVerification(dev)){ + if(tags.chunkId == 0) + matchingChunk = object->chunkId; + else if(object->softDeleted) + matchingChunk = oldChunk; /* Defeat the test */ + else + matchingChunk = yaffs_FindChunkInFile(object,tags.chunkId,NULL); + + if(oldChunk != matchingChunk) + T(YAFFS_TRACE_ERROR, + (TSTR("gc: page in gc mismatch: %d %d %d %d"TENDSTR), + oldChunk,matchingChunk,tags.objectId, tags.chunkId)); + + } + + if (!object) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("page %d in gc has no object: %d %d %d " + TENDSTR), oldChunk, + tags.objectId, tags.chunkId, tags.byteCount)); + } + + if (object && object->deleted + && tags.chunkId != 0) { + /* Data chunk in a deleted file, throw it away + * It's a soft deleted data chunk, + * No need to copy this, just forget about it and + * fix up the object. + */ + + object->nDataChunks--; + + if (object->nDataChunks <= 0) { + /* remeber to clean up the object */ + dev->gcCleanupList[cleanups] = + tags.objectId; + cleanups++; + } + markNAND = 0; + } else if (0 + /* Todo object && object->deleted && object->nDataChunks == 0 */ + ) { + /* Deleted object header with no data chunks. + * Can be discarded and the file deleted. + */ + object->chunkId = 0; + yaffs_FreeTnode(object->myDev, + object->variant. + fileVariant.top); + object->variant.fileVariant.top = NULL; + yaffs_DoGenericObjectDeletion(object); + + } else if (object) { + /* It's either a data chunk in a live file or + * an ObjectHeader, so we're interested in it. + * NB Need to keep the ObjectHeaders of deleted files + * until the whole file has been deleted off + */ + tags.serialNumber++; + + dev->nGCCopies++; + + if (tags.chunkId == 0) { + /* It is an object Id, + * We need to nuke the shrinkheader flags first + * We no longer want the shrinkHeader flag since its work is done + * and if it is left in place it will mess up scanning. + * Also, clear out any shadowing stuff + */ + + yaffs_ObjectHeader *oh; + oh = (yaffs_ObjectHeader *)buffer; + oh->isShrink = 0; + oh->shadowsObject = -1; + tags.extraShadows = 0; + tags.extraIsShrinkHeader = 0; + + yaffs_VerifyObjectHeader(object,oh,&tags,1); + } + + newChunk = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); + + if (newChunk < 0) { + retVal = YAFFS_FAIL; + } else { + + /* Ok, now fix up the Tnodes etc. */ + + if (tags.chunkId == 0) { + /* It's a header */ + object->chunkId = newChunk; + object->serial = tags.serialNumber; + } else { + /* It's a data chunk */ + yaffs_PutChunkIntoFile + (object, + tags.chunkId, + newChunk, 0); + } + } + } + + yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__); + + } + } + + yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + + + /* Do any required cleanups */ + for (i = 0; i < cleanups; i++) { + /* Time to delete the file too */ + object = + yaffs_FindObjectByNumber(dev, + dev->gcCleanupList[i]); + if (object) { + yaffs_FreeTnode(dev, + object->variant.fileVariant. + top); + object->variant.fileVariant.top = NULL; + T(YAFFS_TRACE_GC, + (TSTR + ("yaffs: About to finally delete object %d" + TENDSTR), object->objectId)); + yaffs_DoGenericObjectDeletion(object); + object->myDev->nDeletedFiles--; + } + + } + + } + + yaffs_VerifyCollectedBlock(dev,bi,block); + + if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) { + T(YAFFS_TRACE_GC, + (TSTR + ("gc did not increase free chunks before %d after %d" + TENDSTR), chunksBefore, chunksAfter)); + } + + dev->isDoingGC = 0; + + return YAFFS_OK; +} + +/* New garbage collector + * If we're very low on erased blocks then we do aggressive garbage collection + * otherwise we do "leasurely" garbage collection. + * Aggressive gc looks further (whole array) and will accept less dirty blocks. + * Passive gc only inspects smaller areas and will only accept more dirty blocks. + * + * The idea is to help clear out space in a more spread-out manner. + * Dunno if it really does anything useful. + */ +static int yaffs_CheckGarbageCollection(yaffs_Device * dev) +{ + int block; + int aggressive; + int gcOk = YAFFS_OK; + int maxTries = 0; + + int checkpointBlockAdjust; + + if (dev->isDoingGC) { + /* Bail out so we don't get recursive gc */ + return YAFFS_OK; + } + + /* This loop should pass the first time. + * We'll only see looping here if the erase of the collected block fails. + */ + + do { + maxTries++; + + checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint); + if(checkpointBlockAdjust < 0) + checkpointBlockAdjust = 0; + + if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust + 2)) { + /* We need a block soon...*/ + aggressive = 1; + } else { + /* We're in no hurry */ + aggressive = 0; + } + + block = yaffs_FindBlockForGarbageCollection(dev, aggressive); + + if (block > 0) { + dev->garbageCollections++; + if (!aggressive) { + dev->passiveGarbageCollections++; + } + + T(YAFFS_TRACE_GC, + (TSTR + ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR), + dev->nErasedBlocks, aggressive)); + + gcOk = yaffs_GarbageCollectBlock(dev, block); + } + + if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) { + T(YAFFS_TRACE_GC, + (TSTR + ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" + TENDSTR), dev->nErasedBlocks, maxTries, block)); + } + } while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) + && (maxTries < 2)); + + return aggressive ? gcOk : YAFFS_OK; +} + +/*------------------------- TAGS --------------------------------*/ + +static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, + int chunkInObject) +{ + return (tags->chunkId == chunkInObject && + tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0; + +} + + +/*-------------------- Data file manipulation -----------------*/ + +static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags) +{ + /*Get the Tnode, then get the level 0 offset chunk offset */ + yaffs_Tnode *tn; + int theChunk = -1; + yaffs_ExtendedTags localTags; + int retVal = -1; + + yaffs_Device *dev = in->myDev; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &localTags; + } + + tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); + + if (tn) { + theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); + + retVal = + yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, + chunkInInode); + } + return retVal; +} + +static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags) +{ + /* Get the Tnode, then get the level 0 offset chunk offset */ + yaffs_Tnode *tn; + int theChunk = -1; + yaffs_ExtendedTags localTags; + + yaffs_Device *dev = in->myDev; + int retVal = -1; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &localTags; + } + + tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); + + if (tn) { + + theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); + + retVal = + yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, + chunkInInode); + + /* Delete the entry in the filestructure (if found) */ + if (retVal != -1) { + yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0); + } + } else { + /*T(("No level 0 found for %d\n", chunkInInode)); */ + } + + if (retVal == -1) { + /* T(("Could not find %d to delete\n",chunkInInode)); */ + } + return retVal; +} + +#ifdef YAFFS_PARANOID + +static int yaffs_CheckFileSanity(yaffs_Object * in) +{ + int chunk; + int nChunks; + int fSize; + int failed = 0; + int objId; + yaffs_Tnode *tn; + yaffs_Tags localTags; + yaffs_Tags *tags = &localTags; + int theChunk; + int chunkDeleted; + + if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { + /* T(("Object not a file\n")); */ + return YAFFS_FAIL; + } + + objId = in->objectId; + fSize = in->variant.fileVariant.fileSize; + nChunks = + (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; + + for (chunk = 1; chunk <= nChunks; chunk++) { + tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, + chunk); + + if (tn) { + + theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk); + + if (yaffs_CheckChunkBits + (dev, theChunk / dev->nChunksPerBlock, + theChunk % dev->nChunksPerBlock)) { + + yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk, + tags, + &chunkDeleted); + if (yaffs_TagsMatch + (tags, in->objectId, chunk, chunkDeleted)) { + /* found it; */ + + } + } else { + + failed = 1; + } + + } else { + /* T(("No level 0 found for %d\n", chunk)); */ + } + } + + return failed ? YAFFS_FAIL : YAFFS_OK; +} + +#endif + +static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, + int chunkInNAND, int inScan) +{ + /* NB inScan is zero unless scanning. + * For forward scanning, inScan is > 0; + * for backward scanning inScan is < 0 + */ + + yaffs_Tnode *tn; + yaffs_Device *dev = in->myDev; + int existingChunk; + yaffs_ExtendedTags existingTags; + yaffs_ExtendedTags newTags; + unsigned existingSerial, newSerial; + + if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { + /* Just ignore an attempt at putting a chunk into a non-file during scanning + * If it is not during Scanning then something went wrong! + */ + if (!inScan) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy:attempt to put data chunk into a non-file" + TENDSTR))); + YBUG(); + } + + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); + return YAFFS_OK; + } + + tn = yaffs_AddOrFindLevel0Tnode(dev, + &in->variant.fileVariant, + chunkInInode, + NULL); + if (!tn) { + return YAFFS_FAIL; + } + + existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); + + if (inScan != 0) { + /* If we're scanning then we need to test for duplicates + * NB This does not need to be efficient since it should only ever + * happen when the power fails during a write, then only one + * chunk should ever be affected. + * + * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO + * Update: For backward scanning we don't need to re-read tags so this is quite cheap. + */ + + if (existingChunk != 0) { + /* NB Right now existing chunk will not be real chunkId if the device >= 32MB + * thus we have to do a FindChunkInFile to get the real chunk id. + * + * We have a duplicate now we need to decide which one to use: + * + * Backwards scanning YAFFS2: The old one is what we use, dump the new one. + * Forward scanning YAFFS2: The new one is what we use, dump the old one. + * YAFFS1: Get both sets of tags and compare serial numbers. + */ + + if (inScan > 0) { + /* Only do this for forward scanning */ + yaffs_ReadChunkWithTagsFromNAND(dev, + chunkInNAND, + NULL, &newTags); + + /* Do a proper find */ + existingChunk = + yaffs_FindChunkInFile(in, chunkInInode, + &existingTags); + } + + if (existingChunk <= 0) { + /*Hoosterman - how did this happen? */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: existing chunk < 0 in scan" + TENDSTR))); + + } + + /* NB The deleted flags should be false, otherwise the chunks will + * not be loaded during a scan + */ + + newSerial = newTags.serialNumber; + existingSerial = existingTags.serialNumber; + + if ((inScan > 0) && + (in->myDev->isYaffs2 || + existingChunk <= 0 || + ((existingSerial + 1) & 3) == newSerial)) { + /* Forward scanning. + * Use new + * Delete the old one and drop through to update the tnode + */ + yaffs_DeleteChunk(dev, existingChunk, 1, + __LINE__); + } else { + /* Backward scanning or we want to use the existing one + * Use existing. + * Delete the new one and return early so that the tnode isn't changed + */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, + __LINE__); + return YAFFS_OK; + } + } + + } + + if (existingChunk == 0) { + in->nDataChunks++; + } + + yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND); + + return YAFFS_OK; +} + +static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode, + __u8 * buffer) +{ + int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL); + + if (chunkInNAND >= 0) { + return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND, + buffer,NULL); + } else { + T(YAFFS_TRACE_NANDACCESS, + (TSTR("Chunk %d not found zero instead" TENDSTR), + chunkInNAND)); + /* get sane (zero) data if you read a hole */ + memset(buffer, 0, in->myDev->nDataBytesPerChunk); + return 0; + } + +} + +void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn) +{ + int block; + int page; + yaffs_ExtendedTags tags; + yaffs_BlockInfo *bi; + + if (chunkId <= 0) + return; + + + dev->nDeletions++; + block = chunkId / dev->nChunksPerBlock; + page = chunkId % dev->nChunksPerBlock; + + + if(!yaffs_CheckChunkBit(dev,block,page)) + T(YAFFS_TRACE_VERIFY, + (TSTR("Deleting invalid chunk %d"TENDSTR), + chunkId)); + + bi = yaffs_GetBlockInfo(dev, block); + + T(YAFFS_TRACE_DELETION, + (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId)); + + if (markNAND && + bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) { + + yaffs_InitialiseTags(&tags); + + tags.chunkDeleted = 1; + + yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags); + yaffs_HandleUpdateChunk(dev, chunkId, &tags); + } else { + dev->nUnmarkedDeletions++; + } + + /* Pull out of the management area. + * If the whole block became dirty, this will kick off an erasure. + */ + if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || + bi->blockState == YAFFS_BLOCK_STATE_FULL || + bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { + dev->nFreeChunks++; + + yaffs_ClearChunkBit(dev, block, page); + + bi->pagesInUse--; + + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING && + bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + yaffs_BlockBecameDirty(dev, block); + } + + } else { + /* T(("Bad news deleting chunk %d\n",chunkId)); */ + } + +} + +static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode, + const __u8 * buffer, int nBytes, + int useReserve) +{ + /* Find old chunk Need to do this to get serial number + * Write new one and patch into tree. + * Invalidate old tags. + */ + + int prevChunkId; + yaffs_ExtendedTags prevTags; + + int newChunkId; + yaffs_ExtendedTags newTags; + + yaffs_Device *dev = in->myDev; + + yaffs_CheckGarbageCollection(dev); + + /* Get the previous chunk at this location in the file if it exists */ + prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags); + + /* Set up new tags */ + yaffs_InitialiseTags(&newTags); + + newTags.chunkId = chunkInInode; + newTags.objectId = in->objectId; + newTags.serialNumber = + (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; + newTags.byteCount = nBytes; + + newChunkId = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, + useReserve); + + if (newChunkId >= 0) { + yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0); + + if (prevChunkId >= 0) { + yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); + + } + + yaffs_CheckFileSanity(in); + } + return newChunkId; + +} + +/* UpdateObjectHeader updates the header on NAND for an object. + * If name is not NULL, then that new name is used. + */ +int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, + int isShrink, int shadows) +{ + + yaffs_BlockInfo *bi; + + yaffs_Device *dev = in->myDev; + + int prevChunkId; + int retVal = 0; + int result = 0; + + int newChunkId; + yaffs_ExtendedTags newTags; + yaffs_ExtendedTags oldTags; + + __u8 *buffer = NULL; + YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; + + yaffs_ObjectHeader *oh = NULL; + + yaffs_strcpy(oldName,"silly old name"); + + if (!in->fake || force) { + + yaffs_CheckGarbageCollection(dev); + yaffs_CheckObjectDetailsLoaded(in); + + buffer = yaffs_GetTempBuffer(in->myDev, __LINE__); + oh = (yaffs_ObjectHeader *) buffer; + + prevChunkId = in->chunkId; + + if (prevChunkId >= 0) { + result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, + buffer, &oldTags); + + yaffs_VerifyObjectHeader(in,oh,&oldTags,0); + + memcpy(oldName, oh->name, sizeof(oh->name)); + } + + memset(buffer, 0xFF, dev->nDataBytesPerChunk); + + oh->type = in->variantType; + oh->yst_mode = in->yst_mode; + oh->shadowsObject = shadows; + +#ifdef CONFIG_YAFFS_WINCE + oh->win_atime[0] = in->win_atime[0]; + oh->win_ctime[0] = in->win_ctime[0]; + oh->win_mtime[0] = in->win_mtime[0]; + oh->win_atime[1] = in->win_atime[1]; + oh->win_ctime[1] = in->win_ctime[1]; + oh->win_mtime[1] = in->win_mtime[1]; +#else + oh->yst_uid = in->yst_uid; + oh->yst_gid = in->yst_gid; + oh->yst_atime = in->yst_atime; + oh->yst_mtime = in->yst_mtime; + oh->yst_ctime = in->yst_ctime; + oh->yst_rdev = in->yst_rdev; +#endif + if (in->parent) { + oh->parentObjectId = in->parent->objectId; + } else { + oh->parentObjectId = 0; + } + + if (name && *name) { + memset(oh->name, 0, sizeof(oh->name)); + yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); + } else if (prevChunkId>=0) { + memcpy(oh->name, oldName, sizeof(oh->name)); + } else { + memset(oh->name, 0, sizeof(oh->name)); + } + + oh->isShrink = isShrink; + + switch (in->variantType) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Should not happen */ + break; + case YAFFS_OBJECT_TYPE_FILE: + oh->fileSize = + (oh->parentObjectId == YAFFS_OBJECTID_DELETED + || oh->parentObjectId == + YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant. + fileVariant.fileSize; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + oh->equivalentObjectId = + in->variant.hardLinkVariant.equivalentObjectId; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_strncpy(oh->alias, + in->variant.symLinkVariant.alias, + YAFFS_MAX_ALIAS_LENGTH); + oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; + break; + } + + /* Tags */ + yaffs_InitialiseTags(&newTags); + in->serial++; + newTags.chunkId = 0; + newTags.objectId = in->objectId; + newTags.serialNumber = in->serial; + + /* Add extra info for file header */ + + newTags.extraHeaderInfoAvailable = 1; + newTags.extraParentObjectId = oh->parentObjectId; + newTags.extraFileLength = oh->fileSize; + newTags.extraIsShrinkHeader = oh->isShrink; + newTags.extraEquivalentObjectId = oh->equivalentObjectId; + newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0; + newTags.extraObjectType = in->variantType; + + yaffs_VerifyObjectHeader(in,oh,&newTags,1); + + /* Create new chunk in NAND */ + newChunkId = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, + (prevChunkId >= 0) ? 1 : 0); + + if (newChunkId >= 0) { + + in->chunkId = newChunkId; + + if (prevChunkId >= 0) { + yaffs_DeleteChunk(dev, prevChunkId, 1, + __LINE__); + } + + if(!yaffs_ObjectHasCachedWriteData(in)) + in->dirty = 0; + + /* If this was a shrink, then mark the block that the chunk lives on */ + if (isShrink) { + bi = yaffs_GetBlockInfo(in->myDev, + newChunkId /in->myDev-> nChunksPerBlock); + bi->hasShrinkHeader = 1; + } + + } + + retVal = newChunkId; + + } + + if (buffer) + yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + + return retVal; +} + +/*------------------------ Short Operations Cache ---------------------------------------- + * In many situations where there is no high level buffering (eg WinCE) a lot of + * reads might be short sequential reads, and a lot of writes may be short + * sequential writes. eg. scanning/writing a jpeg file. + * In these cases, a short read/write cache can provide a huge perfomance benefit + * with dumb-as-a-rock code. + * In Linux, the page cache provides read buffering aand the short op cache provides write + * buffering. + * + * There are a limited number (~10) of cache chunks per device so that we don't + * need a very intelligent search. + */ + +static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj) +{ + yaffs_Device *dev = obj->myDev; + int i; + yaffs_ChunkCache *cache; + int nCaches = obj->myDev->nShortOpCaches; + + for(i = 0; i < nCaches; i++){ + cache = &dev->srCache[i]; + if (cache->object == obj && + cache->dirty) + return 1; + } + + return 0; +} + + +static void yaffs_FlushFilesChunkCache(yaffs_Object * obj) +{ + yaffs_Device *dev = obj->myDev; + int lowest = -99; /* Stop compiler whining. */ + int i; + yaffs_ChunkCache *cache; + int chunkWritten = 0; + int nCaches = obj->myDev->nShortOpCaches; + + if (nCaches > 0) { + do { + cache = NULL; + + /* Find the dirty cache for this object with the lowest chunk id. */ + for (i = 0; i < nCaches; i++) { + if (dev->srCache[i].object == obj && + dev->srCache[i].dirty) { + if (!cache + || dev->srCache[i].chunkId < + lowest) { + cache = &dev->srCache[i]; + lowest = cache->chunkId; + } + } + } + + if (cache && !cache->locked) { + /* Write it out and free it up */ + + chunkWritten = + yaffs_WriteChunkDataToObject(cache->object, + cache->chunkId, + cache->data, + cache->nBytes, + 1); + cache->dirty = 0; + cache->object = NULL; + } + + } while (cache && chunkWritten > 0); + + if (cache) { + /* Hoosterman, disk full while writing cache out. */ + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs tragedy: no space during cache write" TENDSTR))); + + } + } + +} + +/*yaffs_FlushEntireDeviceCache(dev) + * + * + */ + +void yaffs_FlushEntireDeviceCache(yaffs_Device *dev) +{ + yaffs_Object *obj; + int nCaches = dev->nShortOpCaches; + int i; + + /* Find a dirty object in the cache and flush it... + * until there are no further dirty objects. + */ + do { + obj = NULL; + for( i = 0; i < nCaches && !obj; i++) { + if (dev->srCache[i].object && + dev->srCache[i].dirty) + obj = dev->srCache[i].object; + + } + if(obj) + yaffs_FlushFilesChunkCache(obj); + + } while(obj); + +} + + +/* Grab us a cache chunk for use. + * First look for an empty one. + * Then look for the least recently used non-dirty one. + * Then look for the least recently used dirty one...., flush and look again. + */ +static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev) +{ + int i; + int usage; + int theOne; + + if (dev->nShortOpCaches > 0) { + for (i = 0; i < dev->nShortOpCaches; i++) { + if (!dev->srCache[i].object) + return &dev->srCache[i]; + } + + return NULL; + + theOne = -1; + usage = 0; /* just to stop the compiler grizzling */ + + for (i = 0; i < dev->nShortOpCaches; i++) { + if (!dev->srCache[i].dirty && + ((dev->srCache[i].lastUse < usage && theOne >= 0) || + theOne < 0)) { + usage = dev->srCache[i].lastUse; + theOne = i; + } + } + + + return theOne >= 0 ? &dev->srCache[theOne] : NULL; + } else { + return NULL; + } + +} + +static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev) +{ + yaffs_ChunkCache *cache; + yaffs_Object *theObj; + int usage; + int i; + int pushout; + + if (dev->nShortOpCaches > 0) { + /* Try find a non-dirty one... */ + + cache = yaffs_GrabChunkCacheWorker(dev); + + if (!cache) { + /* They were all dirty, find the last recently used object and flush + * its cache, then find again. + * NB what's here is not very accurate, we actually flush the object + * the last recently used page. + */ + + /* With locking we can't assume we can use entry zero */ + + theObj = NULL; + usage = -1; + cache = NULL; + pushout = -1; + + for (i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].object && + !dev->srCache[i].locked && + (dev->srCache[i].lastUse < usage || !cache)) + { + usage = dev->srCache[i].lastUse; + theObj = dev->srCache[i].object; + cache = &dev->srCache[i]; + pushout = i; + } + } + + if (!cache || cache->dirty) { + /* Flush and try again */ + yaffs_FlushFilesChunkCache(theObj); + cache = yaffs_GrabChunkCacheWorker(dev); + } + + } + return cache; + } else + return NULL; + +} + +/* Find a cached chunk */ +static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj, + int chunkId) +{ + yaffs_Device *dev = obj->myDev; + int i; + if (dev->nShortOpCaches > 0) { + for (i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].object == obj && + dev->srCache[i].chunkId == chunkId) { + dev->cacheHits++; + + return &dev->srCache[i]; + } + } + } + return NULL; +} + +/* Mark the chunk for the least recently used algorithym */ +static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache, + int isAWrite) +{ + + if (dev->nShortOpCaches > 0) { + if (dev->srLastUse < 0 || dev->srLastUse > 100000000) { + /* Reset the cache usages */ + int i; + for (i = 1; i < dev->nShortOpCaches; i++) { + dev->srCache[i].lastUse = 0; + } + dev->srLastUse = 0; + } + + dev->srLastUse++; + + cache->lastUse = dev->srLastUse; + + if (isAWrite) { + cache->dirty = 1; + } + } +} + +/* Invalidate a single cache page. + * Do this when a whole page gets written, + * ie the short cache for this page is no longer valid. + */ +static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId) +{ + if (object->myDev->nShortOpCaches > 0) { + yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId); + + if (cache) { + cache->object = NULL; + } + } +} + +/* Invalidate all the cache pages associated with this object + * Do this whenever ther file is deleted or resized. + */ +static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in) +{ + int i; + yaffs_Device *dev = in->myDev; + + if (dev->nShortOpCaches > 0) { + /* Invalidate it. */ + for (i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].object == in) { + dev->srCache[i].object = NULL; + } + } + } +} + +/*--------------------- Checkpointing --------------------*/ + + +static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head) +{ + yaffs_CheckpointValidity cp; + + memset(&cp,0,sizeof(cp)); + + cp.structType = sizeof(cp); + cp.magic = YAFFS_MAGIC; + cp.version = YAFFS_CHECKPOINT_VERSION; + cp.head = (head) ? 1 : 0; + + return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))? + 1 : 0; +} + +static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) +{ + yaffs_CheckpointValidity cp; + int ok; + + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + + if(ok) + ok = (cp.structType == sizeof(cp)) && + (cp.magic == YAFFS_MAGIC) && + (cp.version == YAFFS_CHECKPOINT_VERSION) && + (cp.head == ((head) ? 1 : 0)); + return ok ? 1 : 0; +} + +static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, + yaffs_Device *dev) +{ + cp->nErasedBlocks = dev->nErasedBlocks; + cp->allocationBlock = dev->allocationBlock; + cp->allocationPage = dev->allocationPage; + cp->nFreeChunks = dev->nFreeChunks; + + cp->nDeletedFiles = dev->nDeletedFiles; + cp->nUnlinkedFiles = dev->nUnlinkedFiles; + cp->nBackgroundDeletions = dev->nBackgroundDeletions; + cp->sequenceNumber = dev->sequenceNumber; + cp->oldestDirtySequence = dev->oldestDirtySequence; + +} + +static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev, + yaffs_CheckpointDevice *cp) +{ + dev->nErasedBlocks = cp->nErasedBlocks; + dev->allocationBlock = cp->allocationBlock; + dev->allocationPage = cp->allocationPage; + dev->nFreeChunks = cp->nFreeChunks; + + dev->nDeletedFiles = cp->nDeletedFiles; + dev->nUnlinkedFiles = cp->nUnlinkedFiles; + dev->nBackgroundDeletions = cp->nBackgroundDeletions; + dev->sequenceNumber = cp->sequenceNumber; + dev->oldestDirtySequence = cp->oldestDirtySequence; +} + + +static int yaffs_WriteCheckpointDevice(yaffs_Device *dev) +{ + yaffs_CheckpointDevice cp; + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; + + /* Write device runtime values*/ + yaffs_DeviceToCheckpointDevice(&cp,dev); + cp.structType = sizeof(cp); + + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); + + /* Write block info */ + if(ok) { + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + ok = (yaffs_CheckpointWrite(dev,dev->blockInfo,nBytes) == nBytes); + } + + /* Write chunk bits */ + if(ok) { + nBytes = nBlocks * dev->chunkBitmapStride; + ok = (yaffs_CheckpointWrite(dev,dev->chunkBits,nBytes) == nBytes); + } + return ok ? 1 : 0; + +} + +static int yaffs_ReadCheckpointDevice(yaffs_Device *dev) +{ + yaffs_CheckpointDevice cp; + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; + + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + if(!ok) + return 0; + + if(cp.structType != sizeof(cp)) + return 0; + + + yaffs_CheckpointDeviceToDevice(dev,&cp); + + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + + ok = (yaffs_CheckpointRead(dev,dev->blockInfo,nBytes) == nBytes); + + if(!ok) + return 0; + nBytes = nBlocks * dev->chunkBitmapStride; + + ok = (yaffs_CheckpointRead(dev,dev->chunkBits,nBytes) == nBytes); + + return ok ? 1 : 0; +} + +static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp, + yaffs_Object *obj) +{ + + cp->objectId = obj->objectId; + cp->parentId = (obj->parent) ? obj->parent->objectId : 0; + cp->chunkId = obj->chunkId; + cp->variantType = obj->variantType; + cp->deleted = obj->deleted; + cp->softDeleted = obj->softDeleted; + cp->unlinked = obj->unlinked; + cp->fake = obj->fake; + cp->renameAllowed = obj->renameAllowed; + cp->unlinkAllowed = obj->unlinkAllowed; + cp->serial = obj->serial; + cp->nDataChunks = obj->nDataChunks; + + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize; + else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId; +} + +static void yaffs_CheckpointObjectToObject( yaffs_Object *obj,yaffs_CheckpointObject *cp) +{ + + yaffs_Object *parent; + + obj->objectId = cp->objectId; + + if(cp->parentId) + parent = yaffs_FindOrCreateObjectByNumber( + obj->myDev, + cp->parentId, + YAFFS_OBJECT_TYPE_DIRECTORY); + else + parent = NULL; + + if(parent) + yaffs_AddObjectToDirectory(parent, obj); + + obj->chunkId = cp->chunkId; + obj->variantType = cp->variantType; + obj->deleted = cp->deleted; + obj->softDeleted = cp->softDeleted; + obj->unlinked = cp->unlinked; + obj->fake = cp->fake; + obj->renameAllowed = cp->renameAllowed; + obj->unlinkAllowed = cp->unlinkAllowed; + obj->serial = cp->serial; + obj->nDataChunks = cp->nDataChunks; + + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId; + else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId; + + if(obj->objectId >= YAFFS_NOBJECT_BUCKETS) + obj->lazyLoaded = 1; +} + + + +static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = in->myDev; + int ok = 1; + int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + + if (tn) { + if (level > 0) { + + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ + if (tn->internal[i]) { + ok = yaffs_CheckpointTnodeWorker(in, + tn->internal[i], + level - 1, + (chunkOffset<variantType == YAFFS_OBJECT_TYPE_FILE){ + ok = yaffs_CheckpointTnodeWorker(obj, + obj->variant.fileVariant.top, + obj->variant.fileVariant.topLevel, + 0); + if(ok) + ok = (yaffs_CheckpointWrite(obj->myDev,&endMarker,sizeof(endMarker)) == + sizeof(endMarker)); + } + + return ok ? 1 : 0; +} + +static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) +{ + __u32 baseChunk; + int ok = 1; + yaffs_Device *dev = obj->myDev; + yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; + yaffs_Tnode *tn; + int nread = 0; + + ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); + + while(ok && (~baseChunk)){ + nread++; + /* Read level 0 tnode */ + + + /* printf("read tnode at %d\n",baseChunk); */ + tn = yaffs_GetTnodeRaw(dev); + if(tn) + ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) == + (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + else + ok = 0; + + if(tn && ok){ + ok = yaffs_AddOrFindLevel0Tnode(dev, + fileStructPtr, + baseChunk, + tn) ? 1 : 0; + + } + + if(ok) + ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); + + } + + T(YAFFS_TRACE_CHECKPOINT,( + TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), + nread,baseChunk,ok)); + + return ok ? 1 : 0; +} + + +static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_CheckpointObject cp; + int i; + int ok = 1; + struct list_head *lh; + + + /* Iterate through the objects in each hash entry, + * dumping them to the checkpointing stream. + */ + + for(i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++){ + list_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { + obj = list_entry(lh, yaffs_Object, hashLink); + if (!obj->deferedFree) { + yaffs_ObjectToCheckpointObject(&cp,obj); + cp.structType = sizeof(cp); + + T(YAFFS_TRACE_CHECKPOINT,( + TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR), + cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj)); + + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); + + if(ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE){ + ok = yaffs_WriteCheckpointTnodes(obj); + } + } + } + } + } + + /* Dump end of list */ + memset(&cp,0xFF,sizeof(yaffs_CheckpointObject)); + cp.structType = sizeof(cp); + + if(ok) + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); + + return ok ? 1 : 0; +} + +static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_CheckpointObject cp; + int ok = 1; + int done = 0; + yaffs_Object *hardList = NULL; + + while(ok && !done) { + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + if(cp.structType != sizeof(cp)) { + T(YAFFS_TRACE_CHECKPOINT,(TSTR("struct size %d instead of %d ok %d"TENDSTR), + cp.structType,sizeof(cp),ok)); + ok = 0; + } + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR), + cp.objectId,cp.parentId,cp.variantType,cp.chunkId)); + + if(ok && cp.objectId == ~0) + done = 1; + else if(ok){ + obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType); + if(obj) { + yaffs_CheckpointObjectToObject(obj,&cp); + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) { + ok = yaffs_ReadCheckpointTnodes(obj); + } else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + obj->hardLinks.next = + (struct list_head *) + hardList; + hardList = obj; + } + + } + } + } + + if(ok) + yaffs_HardlinkFixup(dev,hardList); + + return ok ? 1 : 0; +} + +static int yaffs_WriteCheckpointSum(yaffs_Device *dev) +{ + __u32 checkpointSum; + int ok; + + yaffs_GetCheckpointSum(dev,&checkpointSum); + + ok = (yaffs_CheckpointWrite(dev,&checkpointSum,sizeof(checkpointSum)) == sizeof(checkpointSum)); + + if(!ok) + return 0; + + return 1; +} + +static int yaffs_ReadCheckpointSum(yaffs_Device *dev) +{ + __u32 checkpointSum0; + __u32 checkpointSum1; + int ok; + + yaffs_GetCheckpointSum(dev,&checkpointSum0); + + ok = (yaffs_CheckpointRead(dev,&checkpointSum1,sizeof(checkpointSum1)) == sizeof(checkpointSum1)); + + if(!ok) + return 0; + + if(checkpointSum0 != checkpointSum1) + return 0; + + return 1; +} + + +static int yaffs_WriteCheckpointData(yaffs_Device *dev) +{ + + int ok = 1; + + if(dev->skipCheckpointWrite || !dev->isYaffs2){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint write" TENDSTR))); + ok = 0; + } + + if(ok) + ok = yaffs_CheckpointOpen(dev,1); + + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs_WriteCheckpointValidityMarker(dev,1); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint device" TENDSTR))); + ok = yaffs_WriteCheckpointDevice(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint objects" TENDSTR))); + ok = yaffs_WriteCheckpointObjects(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs_WriteCheckpointValidityMarker(dev,0); + } + + if(ok){ + ok = yaffs_WriteCheckpointSum(dev); + } + + + if(!yaffs_CheckpointClose(dev)) + ok = 0; + + if(ok) + dev->isCheckpointed = 1; + else + dev->isCheckpointed = 0; + + return dev->isCheckpointed; +} + +static int yaffs_ReadCheckpointData(yaffs_Device *dev) +{ + int ok = 1; + + if(dev->skipCheckpointRead || !dev->isYaffs2){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint read" TENDSTR))); + ok = 0; + } + + if(ok) + ok = yaffs_CheckpointOpen(dev,0); /* open for read */ + + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs_ReadCheckpointValidityMarker(dev,1); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint device" TENDSTR))); + ok = yaffs_ReadCheckpointDevice(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint objects" TENDSTR))); + ok = yaffs_ReadCheckpointObjects(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs_ReadCheckpointValidityMarker(dev,0); + } + + if(ok){ + ok = yaffs_ReadCheckpointSum(dev); + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint checksum %d" TENDSTR),ok)); + } + + if(!yaffs_CheckpointClose(dev)) + ok = 0; + + if(ok) + dev->isCheckpointed = 1; + else + dev->isCheckpointed = 0; + + return ok ? 1 : 0; + +} + +static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) +{ + if(dev->isCheckpointed || + dev->blocksInCheckpoint > 0){ + dev->isCheckpointed = 0; + yaffs_CheckpointInvalidateStream(dev); + if(dev->superBlock && dev->markSuperBlockDirty) + dev->markSuperBlockDirty(dev->superBlock); + } +} + + +int yaffs_CheckpointSave(yaffs_Device *dev) +{ + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + + if(!dev->isCheckpointed) { + yaffs_InvalidateCheckpoint(dev); + yaffs_WriteCheckpointData(dev); + } + + T(YAFFS_TRACE_ALWAYS,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + return dev->isCheckpointed; +} + +int yaffs_CheckpointRestore(yaffs_Device *dev) +{ + int retval; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + retval = yaffs_ReadCheckpointData(dev); + + if(dev->isCheckpointed){ + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + } + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + return retval; +} + +/*--------------------- File read/write ------------------------ + * Read and write have very similar structures. + * In general the read/write has three parts to it + * An incomplete chunk to start with (if the read/write is not chunk-aligned) + * Some complete chunks + * An incomplete chunk to end off with + * + * Curve-balls: the first chunk might also be the last chunk. + */ + +int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, + int nBytes) +{ + + int chunk; + int start; + int nToCopy; + int n = nBytes; + int nDone = 0; + yaffs_ChunkCache *cache; + + yaffs_Device *dev; + + dev = in->myDev; + + while (n > 0) { + //chunk = offset / dev->nDataBytesPerChunk + 1; + //start = offset % dev->nDataBytesPerChunk; + yaffs_AddrToChunk(dev,offset,&chunk,&start); + chunk++; + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + if ((start + n) < dev->nDataBytesPerChunk) { + nToCopy = n; + } else { + nToCopy = dev->nDataBytesPerChunk - start; + } + + cache = yaffs_FindChunkCache(in, chunk); + + /* If the chunk is already in the cache or it is less than a whole chunk + * then use the cache (if there is caching) + * else bypass the cache. + */ + if (cache || nToCopy != dev->nDataBytesPerChunk) { + if (dev->nShortOpCaches > 0) { + + /* If we can't find the data in the cache, then load it up. */ + + if (!cache) { + cache = yaffs_GrabChunkCache(in->myDev); + cache->object = in; + cache->chunkId = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_ReadChunkDataFromObject(in, chunk, + cache-> + data); + cache->nBytes = 0; + } + + yaffs_UseChunkCache(dev, cache, 0); + + cache->locked = 1; + +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(buffer, &cache->data[start], nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + cache->locked = 0; + } else { + /* Read into the local buffer then copy..*/ + + __u8 *localBuffer = + yaffs_GetTempBuffer(dev, __LINE__); + yaffs_ReadChunkDataFromObject(in, chunk, + localBuffer); +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(buffer, &localBuffer[start], nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + yaffs_ReleaseTempBuffer(dev, localBuffer, + __LINE__); + } + + } else { +#ifdef CONFIG_YAFFS_WINCE + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); + + /* Under WinCE can't do direct transfer. Need to use a local buffer. + * This is because we otherwise screw up WinCE's memory mapper + */ + yaffs_ReadChunkDataFromObject(in, chunk, localBuffer); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(buffer, localBuffer, dev->nDataBytesPerChunk); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); +#endif + +#else + /* A full chunk. Read directly into the supplied buffer. */ + yaffs_ReadChunkDataFromObject(in, chunk, buffer); +#endif + } + + n -= nToCopy; + offset += nToCopy; + buffer += nToCopy; + nDone += nToCopy; + + } + + return nDone; +} + +int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, + int nBytes, int writeThrough) +{ + + int chunk; + int start; + int nToCopy; + int n = nBytes; + int nDone = 0; + int nToWriteBack; + int startOfWrite = offset; + int chunkWritten = 0; + int nBytesRead; + + yaffs_Device *dev; + + dev = in->myDev; + + while (n > 0 && chunkWritten >= 0) { + //chunk = offset / dev->nDataBytesPerChunk + 1; + //start = offset % dev->nDataBytesPerChunk; + yaffs_AddrToChunk(dev,offset,&chunk,&start); + chunk++; + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + + if ((start + n) < dev->nDataBytesPerChunk) { + nToCopy = n; + + /* Now folks, to calculate how many bytes to write back.... + * If we're overwriting and not writing to then end of file then + * we need to write back as much as was there before. + */ + + nBytesRead = + in->variant.fileVariant.fileSize - + ((chunk - 1) * dev->nDataBytesPerChunk); + + if (nBytesRead > dev->nDataBytesPerChunk) { + nBytesRead = dev->nDataBytesPerChunk; + } + + nToWriteBack = + (nBytesRead > + (start + n)) ? nBytesRead : (start + n); + + } else { + nToCopy = dev->nDataBytesPerChunk - start; + nToWriteBack = dev->nDataBytesPerChunk; + } + + if (nToCopy != dev->nDataBytesPerChunk) { + /* An incomplete start or end chunk (or maybe both start and end chunk) */ + if (dev->nShortOpCaches > 0) { + yaffs_ChunkCache *cache; + /* If we can't find the data in the cache, then load the cache */ + cache = yaffs_FindChunkCache(in, chunk); + + if (!cache + && yaffs_CheckSpaceForAllocation(in-> + myDev)) { + cache = yaffs_GrabChunkCache(in->myDev); + cache->object = in; + cache->chunkId = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_ReadChunkDataFromObject(in, chunk, + cache-> + data); + } + else if(cache && + !cache->dirty && + !yaffs_CheckSpaceForAllocation(in->myDev)){ + /* Drop the cache if it was a read cache item and + * no space check has been made for it. + */ + cache = NULL; + } + + if (cache) { + yaffs_UseChunkCache(dev, cache, 1); + cache->locked = 1; +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + + memcpy(&cache->data[start], buffer, + nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + cache->locked = 0; + cache->nBytes = nToWriteBack; + + if (writeThrough) { + chunkWritten = + yaffs_WriteChunkDataToObject + (cache->object, + cache->chunkId, + cache->data, cache->nBytes, + 1); + cache->dirty = 0; + } + + } else { + chunkWritten = -1; /* fail the write */ + } + } else { + /* An incomplete start or end chunk (or maybe both start and end chunk) + * Read into the local buffer then copy, then copy over and write back. + */ + + __u8 *localBuffer = + yaffs_GetTempBuffer(dev, __LINE__); + + yaffs_ReadChunkDataFromObject(in, chunk, + localBuffer); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + + memcpy(&localBuffer[start], buffer, nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + chunkWritten = + yaffs_WriteChunkDataToObject(in, chunk, + localBuffer, + nToWriteBack, + 0); + + yaffs_ReleaseTempBuffer(dev, localBuffer, + __LINE__); + + } + + } else { + +#ifdef CONFIG_YAFFS_WINCE + /* Under WinCE can't do direct transfer. Need to use a local buffer. + * This is because we otherwise screw up WinCE's memory mapper + */ + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(localBuffer, buffer, dev->nDataBytesPerChunk); +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + chunkWritten = + yaffs_WriteChunkDataToObject(in, chunk, localBuffer, + dev->nDataBytesPerChunk, + 0); + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); +#else + /* A full chunk. Write directly from the supplied buffer. */ + chunkWritten = + yaffs_WriteChunkDataToObject(in, chunk, buffer, + dev->nDataBytesPerChunk, + 0); +#endif + /* Since we've overwritten the cached data, we better invalidate it. */ + yaffs_InvalidateChunkCache(in, chunk); + } + + if (chunkWritten >= 0) { + n -= nToCopy; + offset += nToCopy; + buffer += nToCopy; + nDone += nToCopy; + } + + } + + /* Update file object */ + + if ((startOfWrite + nDone) > in->variant.fileVariant.fileSize) { + in->variant.fileVariant.fileSize = (startOfWrite + nDone); + } + + in->dirty = 1; + + return nDone; +} + + +/* ---------------------- File resizing stuff ------------------ */ + +static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize) +{ + + yaffs_Device *dev = in->myDev; + int oldFileSize = in->variant.fileVariant.fileSize; + + int lastDel = 1 + (oldFileSize - 1) / dev->nDataBytesPerChunk; + + int startDel = 1 + (newSize + dev->nDataBytesPerChunk - 1) / + dev->nDataBytesPerChunk; + int i; + int chunkId; + + /* Delete backwards so that we don't end up with holes if + * power is lost part-way through the operation. + */ + for (i = lastDel; i >= startDel; i--) { + /* NB this could be optimised somewhat, + * eg. could retrieve the tags and write them without + * using yaffs_DeleteChunk + */ + + chunkId = yaffs_FindAndDeleteChunkInFile(in, i, NULL); + if (chunkId > 0) { + if (chunkId < + (dev->internalStartBlock * dev->nChunksPerBlock) + || chunkId >= + ((dev->internalEndBlock + + 1) * dev->nChunksPerBlock)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("Found daft chunkId %d for %d" TENDSTR), + chunkId, i)); + } else { + in->nDataChunks--; + yaffs_DeleteChunk(dev, chunkId, 1, __LINE__); + } + } + } + +} + +int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) +{ + + int oldFileSize = in->variant.fileVariant.fileSize; + int newSizeOfPartialChunk; + int newFullChunks; + + yaffs_Device *dev = in->myDev; + + yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk); + + yaffs_FlushFilesChunkCache(in); + yaffs_InvalidateWholeChunkCache(in); + + yaffs_CheckGarbageCollection(dev); + + if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { + return yaffs_GetFileSize(in); + } + + if (newSize == oldFileSize) { + return oldFileSize; + } + + if (newSize < oldFileSize) { + + yaffs_PruneResizedChunks(in, newSize); + + if (newSizeOfPartialChunk != 0) { + int lastChunk = 1 + newFullChunks; + + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); + + /* Got to read and rewrite the last chunk with its new size and zero pad */ + yaffs_ReadChunkDataFromObject(in, lastChunk, + localBuffer); + + memset(localBuffer + newSizeOfPartialChunk, 0, + dev->nDataBytesPerChunk - newSizeOfPartialChunk); + + yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, + newSizeOfPartialChunk, 1); + + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); + } + + in->variant.fileVariant.fileSize = newSize; + + yaffs_PruneFileStructure(dev, &in->variant.fileVariant); + } else { + /* newsSize > oldFileSize */ + in->variant.fileVariant.fileSize = newSize; + } + + + + /* Write a new object header. + * show we've shrunk the file, if need be + * Do this only if the file is not in the deleted directories. + */ + if (in->parent->objectId != YAFFS_OBJECTID_UNLINKED && + in->parent->objectId != YAFFS_OBJECTID_DELETED) { + yaffs_UpdateObjectHeader(in, NULL, 0, + (newSize < oldFileSize) ? 1 : 0, 0); + } + + return newSize; +} + +loff_t yaffs_GetFileSize(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + return obj->variant.fileVariant.fileSize; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_strlen(obj->variant.symLinkVariant.alias); + default: + return 0; + } +} + + + +int yaffs_FlushFile(yaffs_Object * in, int updateTime) +{ + int retVal; + if (in->dirty) { + yaffs_FlushFilesChunkCache(in); + if (updateTime) { +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(in->win_mtime); +#else + + in->yst_mtime = Y_CURRENT_TIME; + +#endif + } + + retVal = + (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= + 0) ? YAFFS_OK : YAFFS_FAIL; + } else { + retVal = YAFFS_OK; + } + + return retVal; + +} + +static int yaffs_DoGenericObjectDeletion(yaffs_Object * in) +{ + + /* First off, invalidate the file's data in the cache, without flushing. */ + yaffs_InvalidateWholeChunkCache(in); + + if (in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir)) { + /* Move to the unlinked directory so we have a record that it was deleted. */ + yaffs_ChangeObjectName(in, in->myDev->deletedDir,"deleted", 0, 0); + + } + + yaffs_RemoveObjectFromDirectory(in); + yaffs_DeleteChunk(in->myDev, in->chunkId, 1, __LINE__); + in->chunkId = -1; + + yaffs_FreeObject(in); + return YAFFS_OK; + +} + +/* yaffs_DeleteFile deletes the whole file data + * and the inode associated with the file. + * It does not delete the links associated with the file. + */ +static int yaffs_UnlinkFile(yaffs_Object * in) +{ + + int retVal; + int immediateDeletion = 0; + + if (1) { +#ifdef __KERNEL__ + if (!in->myInode) { + immediateDeletion = 1; + + } +#else + if (in->inUse <= 0) { + immediateDeletion = 1; + + } +#endif + if (immediateDeletion) { + retVal = + yaffs_ChangeObjectName(in, in->myDev->deletedDir, + "deleted", 0, 0); + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: immediate deletion of file %d" TENDSTR), + in->objectId)); + in->deleted = 1; + in->myDev->nDeletedFiles++; + if (0 && in->myDev->isYaffs2) { + yaffs_ResizeFile(in, 0); + } + yaffs_SoftDeleteFile(in); + } else { + retVal = + yaffs_ChangeObjectName(in, in->myDev->unlinkedDir, + "unlinked", 0, 0); + } + + } + return retVal; +} + +int yaffs_DeleteFile(yaffs_Object * in) +{ + int retVal = YAFFS_OK; + + if (in->nDataChunks > 0) { + /* Use soft deletion if there is data in the file */ + if (!in->unlinked) { + retVal = yaffs_UnlinkFile(in); + } + if (retVal == YAFFS_OK && in->unlinked && !in->deleted) { + in->deleted = 1; + in->myDev->nDeletedFiles++; + yaffs_SoftDeleteFile(in); + } + return in->deleted ? YAFFS_OK : YAFFS_FAIL; + } else { + /* The file has no data chunks so we toss it immediately */ + yaffs_FreeTnode(in->myDev, in->variant.fileVariant.top); + in->variant.fileVariant.top = NULL; + yaffs_DoGenericObjectDeletion(in); + + return YAFFS_OK; + } +} + +static int yaffs_DeleteDirectory(yaffs_Object * in) +{ + /* First check that the directory is empty. */ + if (list_empty(&in->variant.directoryVariant.children)) { + return yaffs_DoGenericObjectDeletion(in); + } + + return YAFFS_FAIL; + +} + +static int yaffs_DeleteSymLink(yaffs_Object * in) +{ + YFREE(in->variant.symLinkVariant.alias); + + return yaffs_DoGenericObjectDeletion(in); +} + +static int yaffs_DeleteHardLink(yaffs_Object * in) +{ + /* remove this hardlink from the list assocaited with the equivalent + * object + */ + list_del(&in->hardLinks); + return yaffs_DoGenericObjectDeletion(in); +} + +static void yaffs_DestroyObject(yaffs_Object * obj) +{ + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + yaffs_DeleteFile(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_DeleteDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_DeleteSymLink(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_DeleteHardLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_DoGenericObjectDeletion(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + break; /* should not happen. */ + } +} + +static int yaffs_UnlinkWorker(yaffs_Object * obj) +{ + + if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + return yaffs_DeleteHardLink(obj); + } else if (!list_empty(&obj->hardLinks)) { + /* Curve ball: We're unlinking an object that has a hardlink. + * + * This problem arises because we are not strictly following + * The Linux link/inode model. + * + * We can't really delete the object. + * Instead, we do the following: + * - Select a hardlink. + * - Unhook it from the hard links + * - Unhook it from its parent directory (so that the rename can work) + * - Rename the object to the hardlink's name. + * - Delete the hardlink + */ + + yaffs_Object *hl; + int retVal; + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + hl = list_entry(obj->hardLinks.next, yaffs_Object, hardLinks); + + list_del_init(&hl->hardLinks); + list_del_init(&hl->siblings); + + yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1); + + retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0); + + if (retVal == YAFFS_OK) { + retVal = yaffs_DoGenericObjectDeletion(hl); + } + return retVal; + + } else { + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + return yaffs_UnlinkFile(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return yaffs_DeleteDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_DeleteSymLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + return yaffs_DoGenericObjectDeletion(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + return YAFFS_FAIL; + } + } +} + + +static int yaffs_UnlinkObject( yaffs_Object *obj) +{ + + if (obj && obj->unlinkAllowed) { + return yaffs_UnlinkWorker(obj); + } + + return YAFFS_FAIL; + +} +int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name) +{ + yaffs_Object *obj; + + obj = yaffs_FindObjectByName(dir, name); + return yaffs_UnlinkObject(obj); +} + +/*----------------------- Initialisation Scanning ---------------------- */ + +static void yaffs_HandleShadowedObject(yaffs_Device * dev, int objId, + int backwardScanning) +{ + yaffs_Object *obj; + + if (!backwardScanning) { + /* Handle YAFFS1 forward scanning case + * For YAFFS1 we always do the deletion + */ + + } else { + /* Handle YAFFS2 case (backward scanning) + * If the shadowed object exists then ignore. + */ + if (yaffs_FindObjectByNumber(dev, objId)) { + return; + } + } + + /* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc. + * We put it in unlinked dir to be cleaned up after the scanning + */ + obj = + yaffs_FindOrCreateObjectByNumber(dev, objId, + YAFFS_OBJECT_TYPE_FILE); + yaffs_AddObjectToDirectory(dev->unlinkedDir, obj); + obj->variant.fileVariant.shrinkSize = 0; + obj->valid = 1; /* So that we don't read any other info for this file */ + +} + +typedef struct { + int seq; + int block; +} yaffs_BlockIndex; + + +static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) +{ + yaffs_Object *hl; + yaffs_Object *in; + + while (hardList) { + hl = hardList; + hardList = (yaffs_Object *) (hardList->hardLinks.next); + + in = yaffs_FindObjectByNumber(dev, + hl->variant.hardLinkVariant. + equivalentObjectId); + + if (in) { + /* Add the hardlink pointers */ + hl->variant.hardLinkVariant.equivalentObject = in; + list_add(&hl->hardLinks, &in->hardLinks); + } else { + /* Todo Need to report/handle this better. + * Got a problem... hardlink to a non-existant object + */ + hl->variant.hardLinkVariant.equivalentObject = NULL; + INIT_LIST_HEAD(&hl->hardLinks); + + } + + } + +} + + + + + +static int ybicmp(const void *a, const void *b){ + register int aseq = ((yaffs_BlockIndex *)a)->seq; + register int bseq = ((yaffs_BlockIndex *)b)->seq; + register int ablock = ((yaffs_BlockIndex *)a)->block; + register int bblock = ((yaffs_BlockIndex *)b)->block; + if( aseq == bseq ) + return ablock - bblock; + else + return aseq - bseq; + +} + +static int yaffs_Scan(yaffs_Device * dev) +{ + yaffs_ExtendedTags tags; + int blk; + int blockIterator; + int startIterator; + int endIterator; + int nBlocksToScan = 0; + int result; + + int chunk; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_BlockInfo *bi; + int sequenceNumber; + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + + int alloc_failed = 0; + + + __u8 *chunkData; + + yaffs_BlockIndex *blockIndex = NULL; + + if (dev->isYaffs2) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan is not for YAFFS2!" TENDSTR))); + return YAFFS_FAIL; + } + + //TODO Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format. + + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR), + dev->internalStartBlock, dev->internalEndBlock)); + + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + if (dev->isYaffs2) { + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); + if(!blockIndex) + return YAFFS_FAIL; + } + + /* Scan all the blocks to determine their state */ + for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { + bi = yaffs_GetBlockInfo(dev, blk); + yaffs_ClearChunkBits(dev, blk); + bi->pagesInUse = 0; + bi->softDeletions = 0; + + yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); + + bi->blockState = state; + bi->sequenceNumber = sequenceNumber; + + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + + if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block empty " TENDSTR))); + dev->nErasedBlocks++; + dev->nFreeChunks += dev->nChunksPerBlock; + } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + + /* Determine the highest sequence number */ + if (dev->isYaffs2 && + sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && + sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + + blockIndex[nBlocksToScan].seq = sequenceNumber; + blockIndex[nBlocksToScan].block = blk; + + nBlocksToScan++; + + if (sequenceNumber >= dev->sequenceNumber) { + dev->sequenceNumber = sequenceNumber; + } + } else if (dev->isYaffs2) { + /* TODO: Nasty sequence number! */ + T(YAFFS_TRACE_SCAN, + (TSTR + ("Block scanning block %d has bad sequence number %d" + TENDSTR), blk, sequenceNumber)); + + } + } + } + + /* Sort the blocks + * Dungy old bubble sort for now... + */ + if (dev->isYaffs2) { + yaffs_BlockIndex temp; + int i; + int j; + + for (i = 0; i < nBlocksToScan; i++) + for (j = i + 1; j < nBlocksToScan; j++) + if (blockIndex[i].seq > blockIndex[j].seq) { + temp = blockIndex[j]; + blockIndex[j] = blockIndex[i]; + blockIndex[i] = temp; + } + } + + /* Now scan the blocks looking at the data. */ + if (dev->isYaffs2) { + startIterator = 0; + endIterator = nBlocksToScan - 1; + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); + } else { + startIterator = dev->internalStartBlock; + endIterator = dev->internalEndBlock; + } + + /* For each block.... */ + for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; + blockIterator++) { + + if (dev->isYaffs2) { + /* get the block to scan in the correct order */ + blk = blockIndex[blockIterator].block; + } else { + blk = blockIterator; + } + + bi = yaffs_GetBlockInfo(dev, blk); + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning....*/ + for (c = 0; !alloc_failed && c < dev->nChunksPerBlock && + state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { + /* Read the tags and decide what to do */ + chunk = blk * dev->nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (!dev->isYaffs2 && tags.chunkDeleted) { + /* YAFFS1 only... + * A deleted chunk + */ + deleted++; + dev->nFreeChunks++; + /*T((" %d %d deleted\n",blk,c)); */ + } else if (!tags.chunkUsed) { + /* An unassigned chunk in the block + * This means that either the block is empty or + * this is the one being allocated from + */ + + if (c == 0) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } else { + /* this is the block being allocated from */ + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" TENDSTR), + blk, c)); + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + dev->allocationBlockFinder = blk; + /* Set it to here to encourage the allocator to go forth from here. */ + + /* Yaffs2 sanity check: + * This should be the one with the highest sequence number + */ + if (dev->isYaffs2 + && (dev->sequenceNumber != + bi->sequenceNumber)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs: Allocation block %d was not highest sequence id:" + " block seq = %d, dev seq = %d" + TENDSTR), blk,bi->sequenceNumber,dev->sequenceNumber)); + } + } + + dev->nFreeChunks += (dev->nChunksPerBlock - c); + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + YAFFS_OBJECT_TYPE_FILE); + /* PutChunkIntoFile checks for a clash (two data chunks with + * the same chunkId). + */ + + if(!in) + alloc_failed = 1; + + if(in){ + if(!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,1)) + alloc_failed = 1; + } + + endpos = + (tags.chunkId - 1) * dev->nDataBytesPerChunk + + tags.byteCount; + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE + && in->variant.fileVariant.scannedFileSize < + endpos) { + in->variant.fileVariant. + scannedFileSize = endpos; + if (!dev->useHeaderFileSize) { + in->variant.fileVariant. + fileSize = + in->variant.fileVariant. + scannedFileSize; + } + + } + /* T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); */ + } else { + /* chunkId == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make the object + */ + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, + chunkData, + NULL); + + oh = (yaffs_ObjectHeader *) chunkData; + + in = yaffs_FindObjectByNumber(dev, + tags.objectId); + if (in && in->variantType != oh->type) { + /* This should not happen, but somehow + * Wev'e ended up with an objectId that has been reused but not yet + * deleted, and worse still it has changed type. Delete the old object. + */ + + yaffs_DestroyObject(in); + + in = 0; + } + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + oh->type); + + if(!in) + alloc_failed = 1; + + if (in && oh->shadowsObject > 0) { + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 0); + } + + if (in && in->valid) { + /* We have already filled this one. We have a duplicate and need to resolve it. */ + + unsigned existingSerial = in->serial; + unsigned newSerial = tags.serialNumber; + + if (dev->isYaffs2 || + ((existingSerial + 1) & 3) == + newSerial) { + /* Use new one - destroy the exisiting one */ + yaffs_DeleteChunk(dev, + in->chunkId, + 1, __LINE__); + in->valid = 0; + } else { + /* Use existing - destroy this one. */ + yaffs_DeleteChunk(dev, chunk, 1, + __LINE__); + } + } + + if (in && !in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle with directory structure */ + in->valid = 1; + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + in->chunkId = chunk; + + } else if (in && !in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + in->chunkId = chunk; + + yaffs_SetObjectName(in, oh->name); + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + parent = + yaffs_FindOrCreateObjectByNumber + (dev, oh->parentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + if (parent->variantType == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variantType = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent->variant. + directoryVariant. + children); + } else if (parent->variantType != + YAFFS_OBJECT_TYPE_DIRECTORY) + { + /* Hoosterman, another problem.... + * We're trying to use a non-directory as a directory + */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: attempting to use non-directory as" + " a directory in scan. Put in lost+found." + TENDSTR))); + parent = dev->lostNFoundDir; + } + + yaffs_AddObjectToDirectory(parent, in); + + if (0 && (parent == dev->deletedDir || + parent == dev->unlinkedDir)) { + in->deleted = 1; /* If it is unlinked at start up then it wants deleting */ + dev->nDeletedFiles++; + } + /* Note re hardlinks. + * Since we might scan a hardlink before its equivalent object is scanned + * we put them all in a list. + * After scanning is complete, we should have all the objects, so we run through this + * list and fix up all the chains. + */ + + switch (in->variantType) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + if (dev->isYaffs2 + && oh->isShrink) { + /* Prune back the shrunken chunks */ + yaffs_PruneResizedChunks + (in, oh->fileSize); + /* Mark the block as having a shrinkHeader */ + bi->hasShrinkHeader = 1; + } + + if (dev->useHeaderFileSize) + + in->variant.fileVariant. + fileSize = + oh->fileSize; + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant. + equivalentObjectId = + oh->equivalentObjectId; + in->hardLinks.next = + (struct list_head *) + hardList; + hardList = in; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; + break; + } + + if (parent == dev->deletedDir) { + yaffs_DestroyObject(in); + bi->hasShrinkHeader = 1; + } + } + } + } + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + /* If we got this far while scanning, then the block is fully allocated.*/ + state = YAFFS_BLOCK_STATE_FULL; + } + + bi->blockState = state; + + /* Now let's see if it was dirty */ + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { + yaffs_BlockBecameDirty(dev, blk); + } + + } + + if (blockIndex) { + YFREE(blockIndex); + } + + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + + yaffs_HardlinkFixup(dev,hardList); + + /* Handle the unlinked files. Since they were left in an unlinked state we should + * just delete them. + */ + { + struct list_head *i; + struct list_head *n; + + yaffs_Object *l; + /* Soft delete all the unlinked files */ + list_for_each_safe(i, n, + &dev->unlinkedDir->variant.directoryVariant. + children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + yaffs_DestroyObject(l); + } + } + } + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if(alloc_failed){ + return YAFFS_FAIL; + } + + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); + + + return YAFFS_OK; +} + +static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) +{ + __u8 *chunkData; + yaffs_ObjectHeader *oh; + yaffs_Device *dev = in->myDev; + yaffs_ExtendedTags tags; + int result; + int alloc_failed = 0; + + if(!in) + return; + +#if 0 + T(YAFFS_TRACE_SCAN,(TSTR("details for object %d %s loaded" TENDSTR), + in->objectId, + in->lazyLoaded ? "not yet" : "already")); +#endif + + if(in->lazyLoaded){ + in->lazyLoaded = 0; + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + result = yaffs_ReadChunkWithTagsFromNAND(dev,in->chunkId,chunkData,&tags); + oh = (yaffs_ObjectHeader *) chunkData; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; + +#endif + yaffs_SetObjectName(in, oh->name); + + if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK){ + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; /* Not returned to caller */ + } + + yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__); + } +} + +static int yaffs_ScanBackwards(yaffs_Device * dev) +{ + yaffs_ExtendedTags tags; + int blk; + int blockIterator; + int startIterator; + int endIterator; + int nBlocksToScan = 0; + + int chunk; + int result; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_BlockInfo *bi; + int sequenceNumber; + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + int itsUnlinked; + __u8 *chunkData; + + int fileSize; + int isShrink; + int foundChunksInBlock; + int equivalentObjectId; + int alloc_failed = 0; + + + yaffs_BlockIndex *blockIndex = NULL; + int altBlockIndex = 0; + + if (!dev->isYaffs2) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR))); + return YAFFS_FAIL; + } + + T(YAFFS_TRACE_SCAN, + (TSTR + ("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." + TENDSTR), dev->internalStartBlock, dev->internalEndBlock)); + + + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); + + if(!blockIndex) { + blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex)); + altBlockIndex = 1; + } + + if(!blockIndex) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR))); + return YAFFS_FAIL; + } + + dev->blocksInCheckpoint = 0; + + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + /* Scan all the blocks to determine their state */ + for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { + bi = yaffs_GetBlockInfo(dev, blk); + yaffs_ClearChunkBits(dev, blk); + bi->pagesInUse = 0; + bi->softDeletions = 0; + + yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); + + bi->blockState = state; + bi->sequenceNumber = sequenceNumber; + + if(bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) + bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT; + + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + + + if(state == YAFFS_BLOCK_STATE_CHECKPOINT){ + dev->blocksInCheckpoint++; + + } else if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block empty " TENDSTR))); + dev->nErasedBlocks++; + dev->nFreeChunks += dev->nChunksPerBlock; + } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + + /* Determine the highest sequence number */ + if (dev->isYaffs2 && + sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && + sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + + blockIndex[nBlocksToScan].seq = sequenceNumber; + blockIndex[nBlocksToScan].block = blk; + + nBlocksToScan++; + + if (sequenceNumber >= dev->sequenceNumber) { + dev->sequenceNumber = sequenceNumber; + } + } else if (dev->isYaffs2) { + /* TODO: Nasty sequence number! */ + T(YAFFS_TRACE_SCAN, + (TSTR + ("Block scanning block %d has bad sequence number %d" + TENDSTR), blk, sequenceNumber)); + + } + } + } + + T(YAFFS_TRACE_SCAN, + (TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan)); + + + + YYIELD(); + + /* Sort the blocks */ +#ifndef CONFIG_YAFFS_USE_OWN_SORT + { + /* Use qsort now. */ + yaffs_qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); + } +#else + { + /* Dungy old bubble sort... */ + + yaffs_BlockIndex temp; + int i; + int j; + + for (i = 0; i < nBlocksToScan; i++) + for (j = i + 1; j < nBlocksToScan; j++) + if (blockIndex[i].seq > blockIndex[j].seq) { + temp = blockIndex[j]; + blockIndex[j] = blockIndex[i]; + blockIndex[i] = temp; + } + } +#endif + + YYIELD(); + + T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR))); + + /* Now scan the blocks looking at the data. */ + startIterator = 0; + endIterator = nBlocksToScan - 1; + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); + + /* For each block.... backwards */ + for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; + blockIterator--) { + /* Cooperative multitasking! This loop can run for so + long that watchdog timers expire. */ + YYIELD(); + + /* get the block to scan in the correct order */ + blk = blockIndex[blockIterator].block; + + bi = yaffs_GetBlockInfo(dev, blk); + + + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning.... */ + foundChunksInBlock = 0; + for (c = dev->nChunksPerBlock - 1; + !alloc_failed && c >= 0 && + (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { + /* Scan backwards... + * Read the tags and decide what to do + */ + + chunk = blk * dev->nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (!tags.chunkUsed) { + /* An unassigned chunk in the block. + * If there are used chunks after this one, then + * it is a chunk that was skipped due to failing the erased + * check. Just skip it so that it can be deleted. + * But, more typically, We get here when this is an unallocated + * chunk and his means that either the block is empty or + * this is the one being allocated from + */ + + if(foundChunksInBlock) + { + /* This is a chunk that was skipped due to failing the erased check */ + + } else if (c == 0) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } else { + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING) { + if(dev->sequenceNumber == bi->sequenceNumber) { + /* this is the block being allocated from */ + + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" + TENDSTR), blk, c)); + + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + dev->allocationBlockFinder = blk; + } + else { + /* This is a partially written block that is not + * the current allocation block. This block must have + * had a write failure, so set up for retirement. + */ + + bi->needsRetiring = 1; + bi->gcPrioritise = 1; + + T(YAFFS_TRACE_ALWAYS, + (TSTR("Partially written block %d being set for retirement" TENDSTR), + blk)); + } + + } + + } + + dev->nFreeChunks++; + + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + __u32 chunkBase = + (tags.chunkId - 1) * dev->nDataBytesPerChunk; + + foundChunksInBlock = 1; + + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + YAFFS_OBJECT_TYPE_FILE); + if(!in){ + /* Out of memory */ + alloc_failed = 1; + } + + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE + && chunkBase < + in->variant.fileVariant.shrinkSize) { + /* This has not been invalidated by a resize */ + if(!yaffs_PutChunkIntoFile(in, tags.chunkId, + chunk, -1)){ + alloc_failed = 1; + } + + /* File size is calculated by looking at the data chunks if we have not + * seen an object header yet. Stop this practice once we find an object header. + */ + endpos = + (tags.chunkId - + 1) * dev->nDataBytesPerChunk + + tags.byteCount; + + if (!in->valid && /* have not got an object header yet */ + in->variant.fileVariant. + scannedFileSize < endpos) { + in->variant.fileVariant. + scannedFileSize = endpos; + in->variant.fileVariant. + fileSize = + in->variant.fileVariant. + scannedFileSize; + } + + } else if(in) { + /* This chunk has been invalidated by a resize, so delete */ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + + } + } else { + /* chunkId == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make the object + */ + foundChunksInBlock = 1; + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + oh = NULL; + in = NULL; + + if (tags.extraHeaderInfoAvailable) { + in = yaffs_FindOrCreateObjectByNumber + (dev, tags.objectId, + tags.extraObjectType); + } + + if (!in || +#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD + !in->valid || +#endif + tags.extraShadows || + (!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) + ) { + + /* If we don't have valid info then we need to read the chunk + * TODO In future we can probably defer reading the chunk and + * living with invalid data until needed. + */ + + result = yaffs_ReadChunkWithTagsFromNAND(dev, + chunk, + chunkData, + NULL); + + oh = (yaffs_ObjectHeader *) chunkData; + + if (!in) + in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type); + + } + + if (!in) { + /* TODO Hoosterman we have a problem! */ + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: Could not make object for object %d " + "at chunk %d during scan" + TENDSTR), tags.objectId, chunk)); + + } + + if (in->valid) { + /* We have already filled this one. + * We have a duplicate that will be discarded, but + * we first have to suck out resize info if it is a file. + */ + + if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && + ((oh && + oh-> type == YAFFS_OBJECT_TYPE_FILE)|| + (tags.extraHeaderInfoAvailable && + tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE)) + ) { + __u32 thisSize = + (oh) ? oh->fileSize : tags. + extraFileLength; + __u32 parentObjectId = + (oh) ? oh-> + parentObjectId : tags. + extraParentObjectId; + unsigned isShrink = + (oh) ? oh->isShrink : tags. + extraIsShrinkHeader; + + /* If it is deleted (unlinked at start also means deleted) + * we treat the file size as being zeroed at this point. + */ + if (parentObjectId == + YAFFS_OBJECTID_DELETED + || parentObjectId == + YAFFS_OBJECTID_UNLINKED) { + thisSize = 0; + isShrink = 1; + } + + if (isShrink && + in->variant.fileVariant. + shrinkSize > thisSize) { + in->variant.fileVariant. + shrinkSize = + thisSize; + } + + if (isShrink) { + bi->hasShrinkHeader = 1; + } + + } + /* Use existing - destroy this one. */ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + + } + + if (!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == + YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle with directory structure */ + in->valid = 1; + + if(oh) { + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; + +#endif + } else { + in->variantType = tags.extraObjectType; + in->lazyLoaded = 1; + } + + in->chunkId = chunk; + + } else if (!in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->chunkId = chunk; + + if(oh) { + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + + if (oh->shadowsObject > 0) + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 1); + + + yaffs_SetObjectName(in, oh->name); + parent = + yaffs_FindOrCreateObjectByNumber + (dev, oh->parentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + + fileSize = oh->fileSize; + isShrink = oh->isShrink; + equivalentObjectId = oh->equivalentObjectId; + + } + else { + in->variantType = tags.extraObjectType; + parent = + yaffs_FindOrCreateObjectByNumber + (dev, tags.extraParentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + fileSize = tags.extraFileLength; + isShrink = tags.extraIsShrinkHeader; + equivalentObjectId = tags.extraEquivalentObjectId; + in->lazyLoaded = 1; + + } + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + if (parent->variantType == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variantType = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent->variant. + directoryVariant. + children); + } else if (parent->variantType != + YAFFS_OBJECT_TYPE_DIRECTORY) + { + /* Hoosterman, another problem.... + * We're trying to use a non-directory as a directory + */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: attempting to use non-directory as" + " a directory in scan. Put in lost+found." + TENDSTR))); + parent = dev->lostNFoundDir; + } + + yaffs_AddObjectToDirectory(parent, in); + + itsUnlinked = (parent == dev->deletedDir) || + (parent == dev->unlinkedDir); + + if (isShrink) { + /* Mark the block as having a shrinkHeader */ + bi->hasShrinkHeader = 1; + } + + /* Note re hardlinks. + * Since we might scan a hardlink before its equivalent object is scanned + * we put them all in a list. + * After scanning is complete, we should have all the objects, so we run + * through this list and fix up all the chains. + */ + + switch (in->variantType) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + + if (in->variant.fileVariant. + scannedFileSize < fileSize) { + /* This covers the case where the file size is greater + * than where the data is + * This will happen if the file is resized to be larger + * than its current data extents. + */ + in->variant.fileVariant.fileSize = fileSize; + in->variant.fileVariant.scannedFileSize = + in->variant.fileVariant.fileSize; + } + + if (isShrink && + in->variant.fileVariant.shrinkSize > fileSize) { + in->variant.fileVariant.shrinkSize = fileSize; + } + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + if(!itsUnlinked) { + in->variant.hardLinkVariant.equivalentObjectId = + equivalentObjectId; + in->hardLinks.next = + (struct list_head *) hardList; + hardList = in; + } + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + if(oh){ + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh-> + alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; + } + break; + } + + } + + } + + } /* End of scanning for each chunk */ + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + /* If we got this far while scanning, then the block is fully allocated. */ + state = YAFFS_BLOCK_STATE_FULL; + } + + bi->blockState = state; + + /* Now let's see if it was dirty */ + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { + yaffs_BlockBecameDirty(dev, blk); + } + + } + + if (altBlockIndex) + YFREE_ALT(blockIndex); + else + YFREE(blockIndex); + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + yaffs_HardlinkFixup(dev,hardList); + + + /* + * Sort out state of unlinked and deleted objects. + */ + { + struct list_head *i; + struct list_head *n; + + yaffs_Object *l; + + /* Soft delete all the unlinked files */ + list_for_each_safe(i, n, + &dev->unlinkedDir->variant.directoryVariant. + children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + yaffs_DestroyObject(l); + } + } + + /* Soft delete all the deletedDir files */ + list_for_each_safe(i, n, + &dev->deletedDir->variant.directoryVariant. + children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + yaffs_DestroyObject(l); + + } + } + } + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if(alloc_failed){ + return YAFFS_FAIL; + } + + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); + + return YAFFS_OK; +} + +/*------------------------------ Directory Functions ----------------------------- */ + +static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj) +{ + yaffs_Device *dev = obj->myDev; + + if(dev && dev->removeObjectCallback) + dev->removeObjectCallback(obj); + + list_del_init(&obj->siblings); + obj->parent = NULL; +} + + +static void yaffs_AddObjectToDirectory(yaffs_Object * directory, + yaffs_Object * obj) +{ + + if (!directory) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: Trying to add an object to a null pointer directory" + TENDSTR))); + YBUG(); + } + if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: Trying to add an object to a non-directory" + TENDSTR))); + YBUG(); + } + + if (obj->siblings.prev == NULL) { + /* Not initialised */ + INIT_LIST_HEAD(&obj->siblings); + + } else if (!list_empty(&obj->siblings)) { + /* If it is holed up somewhere else, un hook it */ + yaffs_RemoveObjectFromDirectory(obj); + } + /* Now add it */ + list_add(&obj->siblings, &directory->variant.directoryVariant.children); + obj->parent = directory; + + if (directory == obj->myDev->unlinkedDir + || directory == obj->myDev->deletedDir) { + obj->unlinked = 1; + obj->myDev->nUnlinkedFiles++; + obj->renameAllowed = 0; + } +} + +yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory, + const YCHAR * name) +{ + int sum; + + struct list_head *i; + YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; + + yaffs_Object *l; + + if (!name) { + return NULL; + } + + if (!directory) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: null pointer directory" + TENDSTR))); + YBUG(); + } + if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); + YBUG(); + } + + sum = yaffs_CalcNameSum(name); + + list_for_each(i, &directory->variant.directoryVariant.children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + + yaffs_CheckObjectDetailsLoaded(l); + + /* Special case for lost-n-found */ + if (l->objectId == YAFFS_OBJECTID_LOSTNFOUND) { + if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) { + return l; + } + } else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0) + { + /* LostnFound cunk called Objxxx + * Do a real check + */ + yaffs_GetObjectName(l, buffer, + YAFFS_MAX_NAME_LENGTH); + if (yaffs_strncmp(name, buffer,YAFFS_MAX_NAME_LENGTH) == 0) { + return l; + } + + } + } + } + + return NULL; +} + + +#if 0 +int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, + int (*fn) (yaffs_Object *)) +{ + struct list_head *i; + yaffs_Object *l; + + if (!theDir) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: null pointer directory" + TENDSTR))); + YBUG(); + } + if (theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); + YBUG(); + } + + list_for_each(i, &theDir->variant.directoryVariant.children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + if (l && !fn(l)) { + return YAFFS_FAIL; + } + } + } + + return YAFFS_OK; + +} +#endif + +/* GetEquivalentObject dereferences any hard links to get to the + * actual object. + */ + +yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj) +{ + if (obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + /* We want the object id of the equivalent object, not this one */ + obj = obj->variant.hardLinkVariant.equivalentObject; + yaffs_CheckObjectDetailsLoaded(obj); + } + return obj; + +} + +int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize) +{ + memset(name, 0, buffSize * sizeof(YCHAR)); + + yaffs_CheckObjectDetailsLoaded(obj); + + if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) { + yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1); + } else if (obj->chunkId <= 0) { + YCHAR locName[20]; + /* make up a name */ + yaffs_sprintf(locName, _Y("%s%d"), YAFFS_LOSTNFOUND_PREFIX, + obj->objectId); + yaffs_strncpy(name, locName, buffSize - 1); + + } +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + else if (obj->shortName[0]) { + yaffs_strcpy(name, obj->shortName); + } +#endif + else { + int result; + __u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__); + + yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer; + + memset(buffer, 0, obj->myDev->nDataBytesPerChunk); + + if (obj->chunkId >= 0) { + result = yaffs_ReadChunkWithTagsFromNAND(obj->myDev, + obj->chunkId, buffer, + NULL); + } + yaffs_strncpy(name, oh->name, buffSize - 1); + + yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__); + } + + return yaffs_strlen(name); +} + +int yaffs_GetObjectFileLength(yaffs_Object * obj) +{ + + /* Dereference any hard linking */ + obj = yaffs_GetEquivalentObject(obj); + + if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { + return obj->variant.fileVariant.fileSize; + } + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { + return yaffs_strlen(obj->variant.symLinkVariant.alias); + } else { + /* Only a directory should drop through to here */ + return obj->myDev->nDataBytesPerChunk; + } +} + +int yaffs_GetObjectLinkCount(yaffs_Object * obj) +{ + int count = 0; + struct list_head *i; + + if (!obj->unlinked) { + count++; /* the object itself */ + } + list_for_each(i, &obj->hardLinks) { + count++; /* add the hard links; */ + } + return count; + +} + +int yaffs_GetObjectInode(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + return obj->objectId; +} + +unsigned yaffs_GetObjectType(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return DT_DIR; + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return DT_LNK; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + if (S_ISFIFO(obj->yst_mode)) + return DT_FIFO; + if (S_ISCHR(obj->yst_mode)) + return DT_CHR; + if (S_ISBLK(obj->yst_mode)) + return DT_BLK; + if (S_ISSOCK(obj->yst_mode)) + return DT_SOCK; + default: + return DT_REG; + break; + } +} + +YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { + return yaffs_CloneString(obj->variant.symLinkVariant.alias); + } else { + return yaffs_CloneString(_Y("")); + } +} + +#ifndef CONFIG_YAFFS_WINCE + +int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr) +{ + unsigned int valid = attr->ia_valid; + + if (valid & ATTR_MODE) + obj->yst_mode = attr->ia_mode; + if (valid & ATTR_UID) + obj->yst_uid = attr->ia_uid; + if (valid & ATTR_GID) + obj->yst_gid = attr->ia_gid; + + if (valid & ATTR_ATIME) + obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); + if (valid & ATTR_CTIME) + obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); + if (valid & ATTR_MTIME) + obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); + + if (valid & ATTR_SIZE) + yaffs_ResizeFile(obj, attr->ia_size); + + yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0); + + return YAFFS_OK; + +} +int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr) +{ + unsigned int valid = 0; + + attr->ia_mode = obj->yst_mode; + valid |= ATTR_MODE; + attr->ia_uid = obj->yst_uid; + valid |= ATTR_UID; + attr->ia_gid = obj->yst_gid; + valid |= ATTR_GID; + + Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; + valid |= ATTR_ATIME; + Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; + valid |= ATTR_CTIME; + Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; + valid |= ATTR_MTIME; + + attr->ia_size = yaffs_GetFileSize(obj); + valid |= ATTR_SIZE; + + attr->ia_valid = valid; + + return YAFFS_OK; + +} + +#endif + +#if 0 +int yaffs_DumpObject(yaffs_Object * obj) +{ + YCHAR name[257]; + + yaffs_GetObjectName(obj, name, 256); + + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d" + " chunk %d type %d size %d\n" + TENDSTR), obj->objectId, yaffs_GetObjectInode(obj), name, + obj->dirty, obj->valid, obj->serial, obj->sum, obj->chunkId, + yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj))); + + return YAFFS_OK; +} +#endif + +/*---------------------------- Initialisation code -------------------------------------- */ + +static int yaffs_CheckDevFunctions(const yaffs_Device * dev) +{ + + /* Common functions, gotta have */ + if (!dev->eraseBlockInNAND || !dev->initialiseNAND) + return 0; + +#ifdef CONFIG_YAFFS_YAFFS2 + + /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ + if (dev->writeChunkWithTagsToNAND && + dev->readChunkWithTagsFromNAND && + !dev->writeChunkToNAND && + !dev->readChunkFromNAND && + dev->markNANDBlockBad && dev->queryNANDBlock) + return 1; +#endif + + /* Can use the "spare" style interface for yaffs1 */ + if (!dev->isYaffs2 && + !dev->writeChunkWithTagsToNAND && + !dev->readChunkWithTagsFromNAND && + dev->writeChunkToNAND && + dev->readChunkFromNAND && + !dev->markNANDBlockBad && !dev->queryNANDBlock) + return 1; + + return 0; /* bad */ +} + + +static int yaffs_CreateInitialDirectories(yaffs_Device *dev) +{ + /* Initialise the unlinked, deleted, root and lost and found directories */ + + dev->lostNFoundDir = dev->rootDir = NULL; + dev->unlinkedDir = dev->deletedDir = NULL; + + dev->unlinkedDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); + + dev->deletedDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); + + dev->rootDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT, + YAFFS_ROOT_MODE | S_IFDIR); + dev->lostNFoundDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND, + YAFFS_LOSTNFOUND_MODE | S_IFDIR); + + if(dev->lostNFoundDir && dev->rootDir && dev->unlinkedDir && dev->deletedDir){ + yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); + return YAFFS_OK; + } + + return YAFFS_FAIL; +} + +int yaffs_GutsInitialise(yaffs_Device * dev) +{ + int init_failed = 0; + unsigned x; + int bits; + + T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR))); + + /* Check stuff that must be set */ + + if (!dev) { + T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR))); + return YAFFS_FAIL; + } + + dev->internalStartBlock = dev->startBlock; + dev->internalEndBlock = dev->endBlock; + dev->blockOffset = 0; + dev->chunkOffset = 0; + dev->nFreeChunks = 0; + + if (dev->startBlock == 0) { + dev->internalStartBlock = dev->startBlock + 1; + dev->internalEndBlock = dev->endBlock + 1; + dev->blockOffset = 1; + dev->chunkOffset = dev->nChunksPerBlock; + } + + /* Check geometry parameters. */ + + if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || + (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || + dev->nChunksPerBlock < 2 || + dev->nReservedBlocks < 2 || + dev->internalStartBlock <= 0 || + dev->internalEndBlock <= 0 || + dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2) // otherwise it is too small + ) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " + TENDSTR), dev->nDataBytesPerChunk, dev->isYaffs2 ? "2" : "")); + return YAFFS_FAIL; + } + + if (yaffs_InitialiseNAND(dev) != YAFFS_OK) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: InitialiseNAND failed" TENDSTR))); + return YAFFS_FAIL; + } + + /* Got the right mix of functions? */ + if (!yaffs_CheckDevFunctions(dev)) { + /* Function missing */ + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs: device function(s) missing or wrong\n" TENDSTR))); + + return YAFFS_FAIL; + } + + /* This is really a compilation check. */ + if (!yaffs_CheckStructures()) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs_CheckStructures failed\n" TENDSTR))); + return YAFFS_FAIL; + } + + if (dev->isMounted) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: device already mounted\n" TENDSTR))); + return YAFFS_FAIL; + } + + /* Finished with most checks. One or two more checks happen later on too. */ + + dev->isMounted = 1; + + + + /* OK now calculate a few things for the device */ + + /* + * Calculate all the chunk size manipulation numbers: + */ + /* Start off assuming it is a power of 2 */ + dev->chunkShift = ShiftDiv(dev->nDataBytesPerChunk); + dev->chunkMask = (1<chunkShift) - 1; + + if(dev->nDataBytesPerChunk == (dev->chunkMask + 1)){ + /* Yes it is a power of 2, disable crumbs */ + dev->crumbMask = 0; + dev->crumbShift = 0; + dev->crumbsPerChunk = 0; + } else { + /* Not a power of 2, use crumbs instead */ + dev->crumbShift = ShiftDiv(sizeof(yaffs_PackedTags2TagsPart)); + dev->crumbMask = (1<crumbShift)-1; + dev->crumbsPerChunk = dev->nDataBytesPerChunk/(1 << dev->crumbShift); + dev->chunkShift = 0; + dev->chunkMask = 0; + } + + + /* + * Calculate chunkGroupBits. + * We need to find the next power of 2 > than internalEndBlock + */ + + x = dev->nChunksPerBlock * (dev->internalEndBlock + 1); + + bits = ShiftsGE(x); + + /* Set up tnode width if wide tnodes are enabled. */ + if(!dev->wideTnodesDisabled){ + /* bits must be even so that we end up with 32-bit words */ + if(bits & 1) + bits++; + if(bits < 16) + dev->tnodeWidth = 16; + else + dev->tnodeWidth = bits; + } + else + dev->tnodeWidth = 16; + + dev->tnodeMask = (1<tnodeWidth)-1; + + /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), + * so if the bitwidth of the + * chunk range we're using is greater than 16 we need + * to figure out chunk shift and chunkGroupSize + */ + + if (bits <= dev->tnodeWidth) + dev->chunkGroupBits = 0; + else + dev->chunkGroupBits = bits - dev->tnodeWidth; + + + dev->chunkGroupSize = 1 << dev->chunkGroupBits; + + if (dev->nChunksPerBlock < dev->chunkGroupSize) { + /* We have a problem because the soft delete won't work if + * the chunk group size > chunks per block. + * This can be remedied by using larger "virtual blocks". + */ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: chunk group too large\n" TENDSTR))); + + return YAFFS_FAIL; + } + + /* OK, we've finished verifying the device, lets continue with initialisation */ + + /* More device initialisation */ + dev->garbageCollections = 0; + dev->passiveGarbageCollections = 0; + dev->currentDirtyChecker = 0; + dev->bufferedBlock = -1; + dev->doingBufferedBlockRewrite = 0; + dev->nDeletedFiles = 0; + dev->nBackgroundDeletions = 0; + dev->nUnlinkedFiles = 0; + dev->eccFixed = 0; + dev->eccUnfixed = 0; + dev->tagsEccFixed = 0; + dev->tagsEccUnfixed = 0; + dev->nErasureFailures = 0; + dev->nErasedBlocks = 0; + dev->isDoingGC = 0; + dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ + + /* Initialise temporary buffers and caches. */ + if(!yaffs_InitialiseTempBuffers(dev)) + init_failed = 1; + + dev->srCache = NULL; + dev->gcCleanupList = NULL; + + + if (!init_failed && + dev->nShortOpCaches > 0) { + int i; + __u8 *buf; + int srCacheBytes = dev->nShortOpCaches * sizeof(yaffs_ChunkCache); + + if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) { + dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; + } + + buf = dev->srCache = YMALLOC(srCacheBytes); + + if(dev->srCache) + memset(dev->srCache,0,srCacheBytes); + + for (i = 0; i < dev->nShortOpCaches && buf; i++) { + dev->srCache[i].object = NULL; + dev->srCache[i].lastUse = 0; + dev->srCache[i].dirty = 0; + dev->srCache[i].data = buf = YMALLOC_DMA(dev->nDataBytesPerChunk); + } + if(!buf) + init_failed = 1; + + dev->srLastUse = 0; + } + + dev->cacheHits = 0; + + if(!init_failed){ + dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); + if(!dev->gcCleanupList) + init_failed = 1; + } + + if (dev->isYaffs2) { + dev->useHeaderFileSize = 1; + } + if(!init_failed && !yaffs_InitialiseBlocks(dev)) + init_failed = 1; + + yaffs_InitialiseTnodes(dev); + yaffs_InitialiseObjects(dev); + + if(!init_failed && !yaffs_CreateInitialDirectories(dev)) + init_failed = 1; + + + if(!init_failed){ + /* Now scan the flash. */ + if (dev->isYaffs2) { + if(yaffs_CheckpointRestore(dev)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: restored from checkpoint" TENDSTR))); + } else { + + /* Clean up the mess caused by an aborted checkpoint load + * and scan backwards. + */ + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(dev); + + + dev->nErasedBlocks = 0; + dev->nFreeChunks = 0; + dev->allocationBlock = -1; + dev->allocationPage = -1; + dev->nDeletedFiles = 0; + dev->nUnlinkedFiles = 0; + dev->nBackgroundDeletions = 0; + dev->oldestDirtySequence = 0; + + if(!init_failed && !yaffs_InitialiseBlocks(dev)) + init_failed = 1; + + yaffs_InitialiseTnodes(dev); + yaffs_InitialiseObjects(dev); + + if(!init_failed && !yaffs_CreateInitialDirectories(dev)) + init_failed = 1; + + if(!init_failed && !yaffs_ScanBackwards(dev)) + init_failed = 1; + } + }else + if(!yaffs_Scan(dev)) + init_failed = 1; + } + + if(init_failed){ + /* Clean up the mess */ + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: yaffs_GutsInitialise() aborted.\n" TENDSTR))); + + yaffs_Deinitialise(dev); + return YAFFS_FAIL; + } + + /* Zero out stats */ + dev->nPageReads = 0; + dev->nPageWrites = 0; + dev->nBlockErasures = 0; + dev->nGCCopies = 0; + dev->nRetriedWrites = 0; + + dev->nRetiredBlocks = 0; + + yaffs_VerifyFreeChunks(dev); + yaffs_VerifyBlocks(dev); + + + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); + return YAFFS_OK; + +} + +void yaffs_Deinitialise(yaffs_Device * dev) +{ + if (dev->isMounted) { + int i; + + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(dev); + if (dev->nShortOpCaches > 0 && + dev->srCache) { + + for (i = 0; i < dev->nShortOpCaches; i++) { + if(dev->srCache[i].data) + YFREE(dev->srCache[i].data); + dev->srCache[i].data = NULL; + } + + YFREE(dev->srCache); + dev->srCache = NULL; + } + + YFREE(dev->gcCleanupList); + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + YFREE(dev->tempBuffer[i].buffer); + } + + dev->isMounted = 0; + } + +} + +static int yaffs_CountFreeChunks(yaffs_Device * dev) +{ + int nFree; + int b; + + yaffs_BlockInfo *blk; + + for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; + b++) { + blk = yaffs_GetBlockInfo(dev, b); + + switch (blk->blockState) { + case YAFFS_BLOCK_STATE_EMPTY: + case YAFFS_BLOCK_STATE_ALLOCATING: + case YAFFS_BLOCK_STATE_COLLECTING: + case YAFFS_BLOCK_STATE_FULL: + nFree += + (dev->nChunksPerBlock - blk->pagesInUse + + blk->softDeletions); + break; + default: + break; + } + + } + + return nFree; +} + +int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev) +{ + /* This is what we report to the outside world */ + + int nFree; + int nDirtyCacheChunks; + int blocksForCheckpoint; + +#if 1 + nFree = dev->nFreeChunks; +#else + nFree = yaffs_CountFreeChunks(dev); +#endif + + nFree += dev->nDeletedFiles; + + /* Now count the number of dirty chunks in the cache and subtract those */ + + { + int i; + for (nDirtyCacheChunks = 0, i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].dirty) + nDirtyCacheChunks++; + } + } + + nFree -= nDirtyCacheChunks; + + nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock); + + /* Now we figure out how much to reserve for the checkpoint and report that... */ + blocksForCheckpoint = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; + if(blocksForCheckpoint < 0) + blocksForCheckpoint = 0; + + nFree -= (blocksForCheckpoint * dev->nChunksPerBlock); + + if (nFree < 0) + nFree = 0; + + return nFree; + +} + +static int yaffs_freeVerificationFailures; + +static void yaffs_VerifyFreeChunks(yaffs_Device * dev) +{ + int counted; + int difference; + + if(yaffs_SkipVerification(dev)) + return; + + counted = yaffs_CountFreeChunks(dev); + + difference = dev->nFreeChunks - counted; + + if (difference) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("Freechunks verification failure %d %d %d" TENDSTR), + dev->nFreeChunks, counted, difference)); + yaffs_freeVerificationFailures++; + } +} + +/*---------------------------------------- YAFFS test code ----------------------*/ + +#define yaffs_CheckStruct(structure,syze, name) \ + if(sizeof(structure) != syze) \ + { \ + T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),\ + name,syze,sizeof(structure))); \ + return YAFFS_FAIL; \ + } + +static int yaffs_CheckStructures(void) +{ +/* yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */ +/* yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */ +/* yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */ +#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG + yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode") +#endif + yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader") + + return YAFFS_OK; +} diff -Nru linux-2.6.23/fs/yaffs2/yaffs_guts.h kernel.android/fs/yaffs2/yaffs_guts.h --- linux-2.6.23/fs/yaffs2/yaffs_guts.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_guts.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,902 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_GUTS_H__ +#define __YAFFS_GUTS_H__ + +#include "devextras.h" +#include "yportenv.h" + +#define YAFFS_OK 1 +#define YAFFS_FAIL 0 + +/* Give us a Y=0x59, + * Give us an A=0x41, + * Give us an FF=0xFF + * Give us an S=0x53 + * And what have we got... + */ +#define YAFFS_MAGIC 0x5941FF53 + +#define YAFFS_NTNODES_LEVEL0 16 +#define YAFFS_TNODES_LEVEL0_BITS 4 +#define YAFFS_TNODES_LEVEL0_MASK 0xf + +#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) +#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) +#define YAFFS_TNODES_INTERNAL_MASK 0x7 +#define YAFFS_TNODES_MAX_LEVEL 6 + +#ifndef CONFIG_YAFFS_NO_YAFFS1 +#define YAFFS_BYTES_PER_SPARE 16 +#define YAFFS_BYTES_PER_CHUNK 512 +#define YAFFS_CHUNK_SIZE_SHIFT 9 +#define YAFFS_CHUNKS_PER_BLOCK 32 +#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) +#endif + +#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 +#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 + +#define YAFFS_MAX_CHUNK_ID 0x000FFFFF + +#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF + +#define YAFFS_ALLOCATION_NOBJECTS 100 +#define YAFFS_ALLOCATION_NTNODES 100 +#define YAFFS_ALLOCATION_NLINKS 100 + +#define YAFFS_NOBJECT_BUCKETS 256 + + +#define YAFFS_OBJECT_SPACE 0x40000 + +#define YAFFS_CHECKPOINT_VERSION 3 + +#ifdef CONFIG_YAFFS_UNICODE +#define YAFFS_MAX_NAME_LENGTH 127 +#define YAFFS_MAX_ALIAS_LENGTH 79 +#else +#define YAFFS_MAX_NAME_LENGTH 255 +#define YAFFS_MAX_ALIAS_LENGTH 159 +#endif + +#define YAFFS_SHORT_NAME_LENGTH 15 + +/* Some special object ids for pseudo objects */ +#define YAFFS_OBJECTID_ROOT 1 +#define YAFFS_OBJECTID_LOSTNFOUND 2 +#define YAFFS_OBJECTID_UNLINKED 3 +#define YAFFS_OBJECTID_DELETED 4 + +/* Sseudo object ids for checkpointing */ +#define YAFFS_OBJECTID_SB_HEADER 0x10 +#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 +#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 + +/* */ + +#define YAFFS_MAX_SHORT_OP_CACHES 20 + +#define YAFFS_N_TEMP_BUFFERS 4 + +/* We limit the number attempts at sucessfully saving a chunk of data. + * Small-page devices have 32 pages per block; large-page devices have 64. + * Default to something in the order of 5 to 10 blocks worth of chunks. + */ +#define YAFFS_WR_ATTEMPTS (5*64) + +/* Sequence numbers are used in YAFFS2 to determine block allocation order. + * The range is limited slightly to help distinguish bad numbers from good. + * This also allows us to perhaps in the future use special numbers for + * special purposes. + * EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years, + * and is a larger number than the lifetime of a 2GB device. + */ +#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 +#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00 + +/* ChunkCache is used for short read/write operations.*/ +typedef struct { + struct yaffs_ObjectStruct *object; + int chunkId; + int lastUse; + int dirty; + int nBytes; /* Only valid if the cache is dirty */ + int locked; /* Can't push out or flush while locked. */ +#ifdef CONFIG_YAFFS_YAFFS2 + __u8 *data; +#else + __u8 data[YAFFS_BYTES_PER_CHUNK]; +#endif +} yaffs_ChunkCache; + + + +/* Tags structures in RAM + * NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise + * the structure size will get blown out. + */ + +#ifndef CONFIG_YAFFS_NO_YAFFS1 +typedef struct { + unsigned chunkId:20; + unsigned serialNumber:2; + unsigned byteCount:10; + unsigned objectId:18; + unsigned ecc:12; + unsigned unusedStuff:2; + +} yaffs_Tags; + +typedef union { + yaffs_Tags asTags; + __u8 asBytes[8]; +} yaffs_TagsUnion; + +#endif + +/* Stuff used for extended tags in YAFFS2 */ + +typedef enum { + YAFFS_ECC_RESULT_UNKNOWN, + YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_ECC_RESULT_FIXED, + YAFFS_ECC_RESULT_UNFIXED +} yaffs_ECCResult; + +typedef enum { + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +} yaffs_ObjectType; + +#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL + +typedef struct { + + unsigned validMarker0; + unsigned chunkUsed; /* Status of the chunk: used or unused */ + unsigned objectId; /* If 0 then this is not part of an object (unused) */ + unsigned chunkId; /* If 0 then this is a header, else a data chunk */ + unsigned byteCount; /* Only valid for data chunks */ + + /* The following stuff only has meaning when we read */ + yaffs_ECCResult eccResult; + unsigned blockBad; + + /* YAFFS 1 stuff */ + unsigned chunkDeleted; /* The chunk is marked deleted */ + unsigned serialNumber; /* Yaffs1 2-bit serial number */ + + /* YAFFS2 stuff */ + unsigned sequenceNumber; /* The sequence number of this block */ + + /* Extra info if this is an object header (YAFFS2 only) */ + + unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */ + unsigned extraParentObjectId; /* The parent object */ + unsigned extraIsShrinkHeader; /* Is it a shrink header? */ + unsigned extraShadows; /* Does this shadow another object? */ + + yaffs_ObjectType extraObjectType; /* What object type? */ + + unsigned extraFileLength; /* Length if it is a file */ + unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */ + + unsigned validMarker1; + +} yaffs_ExtendedTags; + +/* Spare structure for YAFFS1 */ +typedef struct { + __u8 tagByte0; + __u8 tagByte1; + __u8 tagByte2; + __u8 tagByte3; + __u8 pageStatus; /* set to 0 to delete the chunk */ + __u8 blockStatus; + __u8 tagByte4; + __u8 tagByte5; + __u8 ecc1[3]; + __u8 tagByte6; + __u8 tagByte7; + __u8 ecc2[3]; +} yaffs_Spare; + +/*Special structure for passing through to mtd */ +struct yaffs_NANDSpare { + yaffs_Spare spare; + int eccres1; + int eccres2; +}; + +/* Block data in RAM */ + +typedef enum { + YAFFS_BLOCK_STATE_UNKNOWN = 0, + + YAFFS_BLOCK_STATE_SCANNING, + YAFFS_BLOCK_STATE_NEEDS_SCANNING, + /* The block might have something on it (ie it is allocating or full, perhaps empty) + * but it needs to be scanned to determine its true state. + * This state is only valid during yaffs_Scan. + * NB We tolerate empty because the pre-scanner might be incapable of deciding + * However, if this state is returned on a YAFFS2 device, then we expect a sequence number + */ + + YAFFS_BLOCK_STATE_EMPTY, + /* This block is empty */ + + YAFFS_BLOCK_STATE_ALLOCATING, + /* This block is partially allocated. + * At least one page holds valid data. + * This is the one currently being used for page + * allocation. Should never be more than one of these + */ + + YAFFS_BLOCK_STATE_FULL, + /* All the pages in this block have been allocated. + */ + + YAFFS_BLOCK_STATE_DIRTY, + /* All pages have been allocated and deleted. + * Erase me, reuse me. + */ + + YAFFS_BLOCK_STATE_CHECKPOINT, + /* This block is assigned to holding checkpoint data. + */ + + YAFFS_BLOCK_STATE_COLLECTING, + /* This block is being garbage collected */ + + YAFFS_BLOCK_STATE_DEAD + /* This block has failed and is not in use */ +} yaffs_BlockState; + +#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) + + +typedef struct { + + int softDeletions:10; /* number of soft deleted pages */ + int pagesInUse:10; /* number of pages in use */ + unsigned blockState:4; /* One of the above block states. NB use unsigned because enum is sometimes an int */ + __u32 needsRetiring:1; /* Data has failed on this block, need to get valid data off */ + /* and retire the block. */ + __u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */ + __u32 gcPrioritise: 1; /* An ECC check or blank check has failed on this block. + It should be prioritised for GC */ + __u32 chunkErrorStrikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */ + +#ifdef CONFIG_YAFFS_YAFFS2 + __u32 hasShrinkHeader:1; /* This block has at least one shrink object header */ + __u32 sequenceNumber; /* block sequence number for yaffs2 */ +#endif + +} yaffs_BlockInfo; + +/* -------------------------- Object structure -------------------------------*/ +/* This is the object structure as stored on NAND */ + +typedef struct { + yaffs_ObjectType type; + + /* Apply to everything */ + int parentObjectId; + __u16 sum__NoLongerUsed; /* checksum of name. No longer used */ + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + /* Thes following apply to directories, files, symlinks - not hard links */ + __u32 yst_mode; /* protection */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 notForWinCE[5]; +#else + __u32 yst_uid; + __u32 yst_gid; + __u32 yst_atime; + __u32 yst_mtime; + __u32 yst_ctime; +#endif + + /* File size applies to files only */ + int fileSize; + + /* Equivalent object id applies to hard links only. */ + int equivalentObjectId; + + /* Alias is for symlinks only. */ + YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + __u32 yst_rdev; /* device stuff for block and char devices (major/min) */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 win_ctime[2]; + __u32 win_atime[2]; + __u32 win_mtime[2]; + __u32 roomToGrow[4]; +#else + __u32 roomToGrow[10]; +#endif + + int shadowsObject; /* This object header shadows the specified object if > 0 */ + + /* isShrink applies to object headers written when we shrink the file (ie resize) */ + __u32 isShrink; + +} yaffs_ObjectHeader; + +/*--------------------------- Tnode -------------------------- */ + +union yaffs_Tnode_union { +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL + 1]; +#else + union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL]; +#endif +/* __u16 level0[YAFFS_NTNODES_LEVEL0]; */ + +}; + +typedef union yaffs_Tnode_union yaffs_Tnode; + +struct yaffs_TnodeList_struct { + struct yaffs_TnodeList_struct *next; + yaffs_Tnode *tnodes; +}; + +typedef struct yaffs_TnodeList_struct yaffs_TnodeList; + +/*------------------------ Object -----------------------------*/ +/* An object can be one of: + * - a directory (no data, has children links + * - a regular file (data.... not prunes :->). + * - a symlink [symbolic link] (the alias). + * - a hard link + */ + +typedef struct { + __u32 fileSize; + __u32 scannedFileSize; + __u32 shrinkSize; + int topLevel; + yaffs_Tnode *top; +} yaffs_FileStructure; + +typedef struct { + struct list_head children; /* list of child links */ +} yaffs_DirectoryStructure; + +typedef struct { + YCHAR *alias; +} yaffs_SymLinkStructure; + +typedef struct { + struct yaffs_ObjectStruct *equivalentObject; + __u32 equivalentObjectId; +} yaffs_HardLinkStructure; + +typedef union { + yaffs_FileStructure fileVariant; + yaffs_DirectoryStructure directoryVariant; + yaffs_SymLinkStructure symLinkVariant; + yaffs_HardLinkStructure hardLinkVariant; +} yaffs_ObjectVariant; + +struct yaffs_ObjectStruct { + __u8 deleted:1; /* This should only apply to unlinked files. */ + __u8 softDeleted:1; /* it has also been soft deleted */ + __u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory.*/ + __u8 fake:1; /* A fake object has no presence on NAND. */ + __u8 renameAllowed:1; /* Some objects are not allowed to be renamed. */ + __u8 unlinkAllowed:1; + __u8 dirty:1; /* the object needs to be written to flash */ + __u8 valid:1; /* When the file system is being loaded up, this + * object might be created before the data + * is available (ie. file data records appear before the header). + */ + __u8 lazyLoaded:1; /* This object has been lazy loaded and is missing some detail */ + + __u8 deferedFree:1; /* For Linux kernel. Object is removed from NAND, but is + * still in the inode cache. Free of object is defered. + * until the inode is released. + */ + + __u8 serial; /* serial number of chunk in NAND. Cached here */ + __u16 sum; /* sum of the name to speed searching */ + + struct yaffs_DeviceStruct *myDev; /* The device I'm on */ + + struct list_head hashLink; /* list of objects in this hash bucket */ + + struct list_head hardLinks; /* all the equivalent hard linked objects */ + + /* directory structure stuff */ + /* also used for linking up the free list */ + struct yaffs_ObjectStruct *parent; + struct list_head siblings; + + /* Where's my object header in NAND? */ + int chunkId; + + int nDataChunks; /* Number of data chunks attached to the file. */ + + __u32 objectId; /* the object id value */ + + __u32 yst_mode; + +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + YCHAR shortName[YAFFS_SHORT_NAME_LENGTH + 1]; +#endif + +#ifndef __KERNEL__ + __u32 inUse; +#endif + +#ifdef CONFIG_YAFFS_WINCE + __u32 win_ctime[2]; + __u32 win_mtime[2]; + __u32 win_atime[2]; +#else + __u32 yst_uid; + __u32 yst_gid; + __u32 yst_atime; + __u32 yst_mtime; + __u32 yst_ctime; +#endif + + __u32 yst_rdev; + +#ifdef __KERNEL__ + struct inode *myInode; + +#endif + + yaffs_ObjectType variantType; + + yaffs_ObjectVariant variant; + +}; + +typedef struct yaffs_ObjectStruct yaffs_Object; + +struct yaffs_ObjectList_struct { + yaffs_Object *objects; + struct yaffs_ObjectList_struct *next; +}; + +typedef struct yaffs_ObjectList_struct yaffs_ObjectList; + +typedef struct { + struct list_head list; + int count; +} yaffs_ObjectBucket; + + +/* yaffs_CheckpointObject holds the definition of an object as dumped + * by checkpointing. + */ + +typedef struct { + int structType; + __u32 objectId; + __u32 parentId; + int chunkId; + + yaffs_ObjectType variantType:3; + __u8 deleted:1; + __u8 softDeleted:1; + __u8 unlinked:1; + __u8 fake:1; + __u8 renameAllowed:1; + __u8 unlinkAllowed:1; + __u8 serial; + + int nDataChunks; + __u32 fileSizeOrEquivalentObjectId; + +}yaffs_CheckpointObject; + +/*--------------------- Temporary buffers ---------------- + * + * These are chunk-sized working buffers. Each device has a few + */ + +typedef struct { + __u8 *buffer; + int line; /* track from whence this buffer was allocated */ + int maxLine; +} yaffs_TempBuffer; + +/*----------------- Device ---------------------------------*/ + +struct yaffs_DeviceStruct { + struct list_head devList; + const char *name; + + /* Entry parameters set up way early. Yaffs sets up the rest.*/ + int nDataBytesPerChunk; /* Should be a power of 2 >= 512 */ + int nChunksPerBlock; /* does not need to be a power of 2 */ + int nBytesPerSpare; /* spare area size */ + int startBlock; /* Start block we're allowed to use */ + int endBlock; /* End block we're allowed to use */ + int nReservedBlocks; /* We want this tuneable so that we can reduce */ + /* reserved blocks on NOR and RAM. */ + + + /* Stuff used by the shared space checkpointing mechanism */ + /* If this value is zero, then this mechanism is disabled */ + + int nCheckpointReservedBlocks; /* Blocks to reserve for checkpoint data */ + + + + + int nShortOpCaches; /* If <= 0, then short op caching is disabled, else + * the number of short op caches (don't use too many) + */ + + int useHeaderFileSize; /* Flag to determine if we should use file sizes from the header */ + + int useNANDECC; /* Flag to decide whether or not to use NANDECC */ + + void *genericDevice; /* Pointer to device context + * On an mtd this holds the mtd pointer. + */ + void *superBlock; + + /* NAND access functions (Must be set before calling YAFFS)*/ + + int (*writeChunkToNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, const __u8 * data, + const yaffs_Spare * spare); + int (*readChunkFromNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, __u8 * data, + yaffs_Spare * spare); + int (*eraseBlockInNAND) (struct yaffs_DeviceStruct * dev, + int blockInNAND); + int (*initialiseNAND) (struct yaffs_DeviceStruct * dev); + +#ifdef CONFIG_YAFFS_YAFFS2 + int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, const __u8 * data, + const yaffs_ExtendedTags * tags); + int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, __u8 * data, + yaffs_ExtendedTags * tags); + int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo); + int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); +#endif + + int isYaffs2; + + /* The removeObjectCallback function must be supplied by OS flavours that + * need it. The Linux kernel does not use this, but yaffs direct does use + * it to implement the faster readdir + */ + void (*removeObjectCallback)(struct yaffs_ObjectStruct *obj); + + /* Callback to mark the superblock dirsty */ + void (*markSuperBlockDirty)(void * superblock); + + int wideTnodesDisabled; /* Set to disable wide tnodes */ + + + /* End of stuff that must be set before initialisation. */ + + /* Checkpoint control. Can be set before or after initialisation */ + __u8 skipCheckpointRead; + __u8 skipCheckpointWrite; + + /* Runtime parameters. Set up by YAFFS. */ + + __u16 chunkGroupBits; /* 0 for devices <= 32MB. else log2(nchunks) - 16 */ + __u16 chunkGroupSize; /* == 2^^chunkGroupBits */ + + /* Stuff to support wide tnodes */ + __u32 tnodeWidth; + __u32 tnodeMask; + + /* Stuff to support various file offses to chunk/offset translations */ + /* "Crumbs" for nDataBytesPerChunk not being a power of 2 */ + __u32 crumbMask; + __u32 crumbShift; + __u32 crumbsPerChunk; + + /* Straight shifting for nDataBytesPerChunk being a power of 2 */ + __u32 chunkShift; + __u32 chunkMask; + + +#ifdef __KERNEL__ + + struct semaphore sem; /* Semaphore for waiting on erasure.*/ + struct semaphore grossLock; /* Gross locking semaphore */ + __u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer + * at compile time so we have to allocate it. + */ + void (*putSuperFunc) (struct super_block * sb); +#endif + + int isMounted; + + int isCheckpointed; + + + /* Stuff to support block offsetting to support start block zero */ + int internalStartBlock; + int internalEndBlock; + int blockOffset; + int chunkOffset; + + + /* Runtime checkpointing stuff */ + int checkpointPageSequence; /* running sequence number of checkpoint pages */ + int checkpointByteCount; + int checkpointByteOffset; + __u8 *checkpointBuffer; + int checkpointOpenForWrite; + int blocksInCheckpoint; + int checkpointCurrentChunk; + int checkpointCurrentBlock; + int checkpointNextBlock; + int *checkpointBlockList; + int checkpointMaxBlocks; + __u32 checkpointSum; + __u32 checkpointXor; + + /* Block Info */ + yaffs_BlockInfo *blockInfo; + __u8 *chunkBits; /* bitmap of chunks in use */ + unsigned blockInfoAlt:1; /* was allocated using alternative strategy */ + unsigned chunkBitsAlt:1; /* was allocated using alternative strategy */ + int chunkBitmapStride; /* Number of bytes of chunkBits per block. + * Must be consistent with nChunksPerBlock. + */ + + int nErasedBlocks; + int allocationBlock; /* Current block being allocated off */ + __u32 allocationPage; + int allocationBlockFinder; /* Used to search for next allocation block */ + + /* Runtime state */ + int nTnodesCreated; + yaffs_Tnode *freeTnodes; + int nFreeTnodes; + yaffs_TnodeList *allocatedTnodeList; + + int isDoingGC; + + int nObjectsCreated; + yaffs_Object *freeObjects; + int nFreeObjects; + + yaffs_ObjectList *allocatedObjectList; + + yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS]; + + int nFreeChunks; + + int currentDirtyChecker; /* Used to find current dirtiest block */ + + __u32 *gcCleanupList; /* objects to delete at the end of a GC. */ + int nonAggressiveSkip; /* GC state/mode */ + + /* Statistcs */ + int nPageWrites; + int nPageReads; + int nBlockErasures; + int nErasureFailures; + int nGCCopies; + int garbageCollections; + int passiveGarbageCollections; + int nRetriedWrites; + int nRetiredBlocks; + int eccFixed; + int eccUnfixed; + int tagsEccFixed; + int tagsEccUnfixed; + int nDeletions; + int nUnmarkedDeletions; + + int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */ + + /* Special directories */ + yaffs_Object *rootDir; + yaffs_Object *lostNFoundDir; + + /* Buffer areas for storing data to recover from write failures TODO + * __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK]; + * yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK]; + */ + + int bufferedBlock; /* Which block is buffered here? */ + int doingBufferedBlockRewrite; + + yaffs_ChunkCache *srCache; + int srLastUse; + + int cacheHits; + + /* Stuff for background deletion and unlinked files.*/ + yaffs_Object *unlinkedDir; /* Directory where unlinked and deleted files live. */ + yaffs_Object *deletedDir; /* Directory where deleted objects are sent to disappear. */ + yaffs_Object *unlinkedDeletion; /* Current file being background deleted.*/ + int nDeletedFiles; /* Count of files awaiting deletion;*/ + int nUnlinkedFiles; /* Count of unlinked files. */ + int nBackgroundDeletions; /* Count of background deletions. */ + + + yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS]; + int maxTemp; + int unmanagedTempAllocations; + int unmanagedTempDeallocations; + + /* yaffs2 runtime stuff */ + unsigned sequenceNumber; /* Sequence number of currently allocating block */ + unsigned oldestDirtySequence; + +}; + +typedef struct yaffs_DeviceStruct yaffs_Device; + +/* The static layout of bllock usage etc is stored in the super block header */ +typedef struct { + int StructType; + int version; + int checkpointStartBlock; + int checkpointEndBlock; + int startBlock; + int endBlock; + int rfu[100]; +} yaffs_SuperBlockHeader; + +/* The CheckpointDevice structure holds the device information that changes at runtime and + * must be preserved over unmount/mount cycles. + */ +typedef struct { + int structType; + int nErasedBlocks; + int allocationBlock; /* Current block being allocated off */ + __u32 allocationPage; + int nFreeChunks; + + int nDeletedFiles; /* Count of files awaiting deletion;*/ + int nUnlinkedFiles; /* Count of unlinked files. */ + int nBackgroundDeletions; /* Count of background deletions. */ + + /* yaffs2 runtime stuff */ + unsigned sequenceNumber; /* Sequence number of currently allocating block */ + unsigned oldestDirtySequence; + +} yaffs_CheckpointDevice; + + +typedef struct { + int structType; + __u32 magic; + __u32 version; + __u32 head; +} yaffs_CheckpointValidity; + +/* Function to manipulate block info */ +static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR), + blk)); + YBUG(); + } + return &dev->blockInfo[blk - dev->internalStartBlock]; +} + +/*----------------------- YAFFS Functions -----------------------*/ + +int yaffs_GutsInitialise(yaffs_Device * dev); +void yaffs_Deinitialise(yaffs_Device * dev); + +int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev); + +int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, + yaffs_Object * newDir, const YCHAR * newName); + +int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name); +int yaffs_DeleteFile(yaffs_Object * obj); + +int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize); +int yaffs_GetObjectFileLength(yaffs_Object * obj); +int yaffs_GetObjectInode(yaffs_Object * obj); +unsigned yaffs_GetObjectType(yaffs_Object * obj); +int yaffs_GetObjectLinkCount(yaffs_Object * obj); + +int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr); +int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr); + +/* File operations */ +int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, loff_t offset, + int nBytes); +int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, loff_t offset, + int nBytes, int writeThrough); +int yaffs_ResizeFile(yaffs_Object * obj, loff_t newSize); + +yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid); +int yaffs_FlushFile(yaffs_Object * obj, int updateTime); + +/* Flushing and checkpointing */ +void yaffs_FlushEntireDeviceCache(yaffs_Device *dev); + +int yaffs_CheckpointSave(yaffs_Device *dev); +int yaffs_CheckpointRestore(yaffs_Device *dev); + +/* Directory operations */ +yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid); +yaffs_Object *yaffs_FindObjectByName(yaffs_Object * theDir, const YCHAR * name); +int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, + int (*fn) (yaffs_Object *)); + +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number); + +/* Link operations */ +yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, + yaffs_Object * equivalentObject); + +yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj); + +/* Symlink operations */ +yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, + const YCHAR * alias); +YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj); + +/* Special inodes (fifos, sockets and devices) */ +yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, __u32 rdev); + +/* Special directories */ +yaffs_Object *yaffs_Root(yaffs_Device * dev); +yaffs_Object *yaffs_LostNFound(yaffs_Device * dev); + +#ifdef CONFIG_YAFFS_WINCE +/* CONFIG_YAFFS_WINCE special stuff */ +void yfsd_WinFileTimeNow(__u32 target[2]); +#endif + +#ifdef __KERNEL__ + +void yaffs_HandleDeferedFree(yaffs_Object * obj); +#endif + +/* Debug dump */ +int yaffs_DumpObject(yaffs_Object * obj); + +void yaffs_GutsTest(yaffs_Device * dev); + +/* A few useful functions */ +void yaffs_InitialiseTags(yaffs_ExtendedTags * tags); +void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn); +int yaffs_CheckFF(__u8 * buffer, int nBytes); +void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_mtdif.c kernel.android/fs/yaffs2/yaffs_mtdif.c --- linux-2.6.23/fs/yaffs2/yaffs_mtdif.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_mtdif.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,241 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + */ + +const char *yaffs_mtdif_c_version = + "$Id$"; + +#include "yportenv.h" + + +#include "yaffs_mtdif.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" +#include "linux/mtd/nand.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static struct nand_oobinfo yaffs_oobinfo = { + .useecc = 1, + .eccbytes = 6, + .eccpos = {8, 9, 10, 13, 14, 15} +}; + +static struct nand_oobinfo yaffs_noeccinfo = { + .useecc = 0, +}; +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob) +{ + oob[0] = spare->tagByte0; + oob[1] = spare->tagByte1; + oob[2] = spare->tagByte2; + oob[3] = spare->tagByte3; + oob[4] = spare->tagByte4; + oob[5] = spare->tagByte5 & 0x3f; + oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80; + oob[5] |= spare->pageStatus == 0 ? 0: 0x40; + oob[6] = spare->tagByte6; + oob[7] = spare->tagByte7; +} + +static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob) +{ + struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare; + spare->tagByte0 = oob[0]; + spare->tagByte1 = oob[1]; + spare->tagByte2 = oob[2]; + spare->tagByte3 = oob[3]; + spare->tagByte4 = oob[4]; + spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f; + spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y'; + spare->pageStatus = oob[5] & 0x40 ? 0xff : 0; + spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff; + spare->tagByte6 = oob[6]; + spare->tagByte7 = oob[7]; + spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff; + + nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */ +} +#endif + +int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, const yaffs_Spare * spare) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#endif + size_t dummy; + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + __u8 spareAsBytes[8]; /* OOB */ + + if (data && !spare) + retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data); + else if (spare) { + if (dev->useNANDECC) { + translate_spare2oob(spare, spareAsBytes); + ops.mode = MTD_OOB_AUTO; + ops.ooblen = 8; /* temp hack */ + } else { + ops.mode = MTD_OOB_RAW; + ops.ooblen = YAFFS_BYTES_PER_SPARE; + } + ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; + ops.datbuf = (u8 *)data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; + retval = mtd->write_oob(mtd, addr, &ops); + } +#else + __u8 *spareAsBytes = (__u8 *) spare; + + if (data && spare) { + if (dev->useNANDECC) + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_oobinfo); + else + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_noeccinfo); + } else { + if (data) + retval = + mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (spare) + retval = + mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, + &dummy, spareAsBytes); + } +#endif + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, + yaffs_Spare * spare) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#endif + size_t dummy; + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + __u8 spareAsBytes[8]; /* OOB */ + + if (data && !spare) + retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data); + else if (spare) { + if (dev->useNANDECC) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = 8; /* temp hack */ + } else { + ops.mode = MTD_OOB_RAW; + ops.ooblen = YAFFS_BYTES_PER_SPARE; + } + ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; + ops.datbuf = data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; + retval = mtd->read_oob(mtd, addr, &ops); + if (dev->useNANDECC) + translate_oob2spare(spare, spareAsBytes); + } +#else + __u8 *spareAsBytes = (__u8 *) spare; + + if (data && spare) { + if (dev->useNANDECC) { + /* Careful, this call adds 2 ints */ + /* to the end of the spare data. Calling function */ + /* should allocate enough memory for spare, */ + /* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */ + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_oobinfo); + } else { + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_noeccinfo); + } + } else { + if (data) + retval = + mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (spare) + retval = + mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, + &dummy, spareAsBytes); + } +#endif + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + __u32 addr = + ((loff_t) blockNumber) * dev->nDataBytesPerChunk + * dev->nChunksPerBlock; + struct erase_info ei; + int retval = 0; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + /* Todo finish off the ei if required */ + + sema_init(&dev->sem, 0); + + retval = mtd->erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd_InitialiseNAND(yaffs_Device * dev) +{ + return YAFFS_OK; +} + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_mtdif.h kernel.android/fs/yaffs2/yaffs_mtdif.h --- linux-2.6.23/fs/yaffs2/yaffs_mtdif.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_mtdif.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,27 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF_H__ +#define __YAFFS_MTDIF_H__ + +#include "yaffs_guts.h" + +int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, const yaffs_Spare * spare); +int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, + yaffs_Spare * spare); +int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber); +int nandmtd_InitialiseNAND(yaffs_Device * dev); +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_mtdif1.c kernel.android/fs/yaffs2/yaffs_mtdif1.c --- linux-2.6.23/fs/yaffs2/yaffs_mtdif1.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_mtdif1.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,363 @@ +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. + * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. + * + * Copyright (C) 2002 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * 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 interface between yaffs_nand.c and the + * MTD API. This version is used when the MTD interface supports the + * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, + * and we have small-page NAND device. + * + * These functions are invoked via function pointers in yaffs_nand.c. + * This replaces functionality provided by functions in yaffs_mtdif.c + * and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are + * called in yaffs_mtdif.c when the function pointers are NULL. + * We assume the MTD layer is performing ECC (useNANDECC is true). + */ + +#include "yportenv.h" +#include "yaffs_guts.h" +#include "yaffs_packedtags1.h" +#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC + +#include "linux/kernel.h" +#include "linux/version.h" +#include "linux/types.h" +#include "linux/mtd/mtd.h" + +/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + +const char *yaffs_mtdif1_c_version = "$Id$"; + +#ifndef CONFIG_YAFFS_9BYTE_TAGS +# define YTAG1_SIZE 8 +#else +# define YTAG1_SIZE 9 +#endif + +#if 0 +/* Use the following nand_ecclayout with MTD when using + * CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. + * If you have existing Yaffs images and the byte order differs from this, + * adjust 'oobfree' to match your existing Yaffs data. + * + * This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the + * pageStatus byte (at NAND spare offset 4) scattered/gathered from/to + * the 9th byte. + * + * Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 + * We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P + * where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus + * byte and B is the small-page bad-block indicator byte. + */ +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +}; +#endif + +/* Write a chunk (page) of data to NAND. + * + * Caller always provides ExtendedTags data which are converted to a more + * compact (packed) form for storage in NAND. A mini-ECC runs over the + * contents of the tags meta-data; used to valid the tags when read. + * + * - Pack ExtendedTags to PackedTags1 form + * - Compute mini-ECC for PackedTags1 + * - Write data and packed tags to NAND. + * + * Note: Due to the use of the PackedTags1 meta-data which does not include + * a full sequence number (as found in the larger PackedTags2 form) it is + * necessary for Yaffs to re-write a chunk/page (just once) to mark it as + * discarded and dirty. This is not ideal: newer NAND parts are supposed + * to be written just once. When Yaffs performs this operation, this + * function is called with a NULL data pointer -- calling MTD write_oob + * without data is valid usage (2.6.17). + * + * Any underlying MTD error results in YAFFS_FAIL. + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, + int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + + /* we assume that PackedTags1 and yaffs_Tags are compatible */ + compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); + compile_time_assertion(sizeof(yaffs_Tags) == 8); + + dev->nPageWrites++; + + yaffs_PackTags1(&pt1, etags); + yaffs_CalcTagsECC((yaffs_Tags *)&pt1); + + /* When deleting a chunk, the upper layer provides only skeletal + * etags, one with chunkDeleted set. However, we need to update the + * tags, not erase them completely. So we use the NAND write property + * that only zeroed-bits stick and set tag bytes to all-ones and + * zero just the (not) deleted bit. + */ +#ifndef CONFIG_YAFFS_9BYTE_TAGS + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* clear delete status bit to indicate deleted */ + pt1.deleted = 0; + } +#else + ((__u8 *)&pt1)[8] = 0xff; + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* zero pageStatus byte to indicate deleted */ + ((__u8 *)&pt1)[8] = 0; + } +#endif + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (__u8 *)&pt1; + + retval = mtd->write_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "write_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + return retval ? YAFFS_FAIL : YAFFS_OK; +} + +/* Return with empty ExtendedTags but add eccResult. + */ +static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) +{ + if (etags) { + memset(etags, 0, sizeof(*etags)); + etags->eccResult = eccResult; + } + return retval; +} + +/* Read a chunk (page) from NAND. + * + * Caller expects ExtendedTags data to be usable even on error; that is, + * all members except eccResult and blockBad are zeroed. + * + * - Check ECC results for data (if applicable) + * - Check for blank/erased block (return empty ExtendedTags if blank) + * - Check the PackedTags1 mini-ECC (correct if necessary/possible) + * - Convert PackedTags1 to ExtendedTags + * - Update eccResult and blockBad members to refect state. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, + int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + int eccres = YAFFS_ECC_RESULT_NO_ERROR; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + int deleted; + + dev->nPageReads++; + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = data; + ops.oobbuf = (__u8 *)&pt1; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; + * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. + */ + ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +#endif + /* Read page and oob using MTD. + * Check status and determine ECC result. + */ + retval = mtd->read_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "read_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + + switch (retval) { + case 0: + /* no error */ + break; + + case -EUCLEAN: + /* MTD's ECC fixed the data */ + eccres = YAFFS_ECC_RESULT_FIXED; + dev->eccFixed++; + break; + + case -EBADMSG: + /* MTD's ECC could not fix the data */ + dev->eccUnfixed++; + /* fall into... */ + default: + rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); + etags->blockBad = (mtd->block_isbad)(mtd, addr); + return YAFFS_FAIL; + } + + /* Check for a blank/erased chunk. + */ + if (yaffs_CheckFF((__u8 *)&pt1, 8)) { + /* when blank, upper layers want eccResult to be <= NO_ERROR */ + return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); + } + +#ifndef CONFIG_YAFFS_9BYTE_TAGS + /* Read deleted status (bit) then return it to it's non-deleted + * state before performing tags mini-ECC check. pt1.deleted is + * inverted. + */ + deleted = !pt1.deleted; + pt1.deleted = 1; +#else + deleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); +#endif + + /* Check the packed tags mini-ECC and correct if necessary/possible. + */ + retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); + switch (retval) { + case 0: + /* no tags error, use MTD result */ + break; + case 1: + /* recovered tags-ECC error */ + dev->tagsEccFixed++; + if (eccres == YAFFS_ECC_RESULT_NO_ERROR) + eccres = YAFFS_ECC_RESULT_FIXED; + break; + default: + /* unrecovered tags-ECC error */ + dev->tagsEccUnfixed++; + return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); + } + + /* Unpack the tags to extended form and set ECC result. + * [set shouldBeFF just to keep yaffs_UnpackTags1 happy] + */ + pt1.shouldBeFF = 0xFFFFFFFF; + yaffs_UnpackTags1(etags, &pt1); + etags->eccResult = eccres; + + /* Set deleted state */ + etags->chunkDeleted = deleted; + return YAFFS_OK; +} + +/* Mark a block bad. + * + * This is a persistant state. + * Use of this function should be rare. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +{ + struct mtd_info * mtd = dev->genericDevice; + int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); + + retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +/* Check any MTD prerequists. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +static int nandmtd1_TestPrerequists(struct mtd_info * mtd) +{ + /* 2.6.18 has mtd->ecclayout->oobavail */ + /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ + int oobavail = mtd->ecclayout->oobavail; + + if (oobavail < YTAG1_SIZE) { + yaffs_trace(YAFFS_TRACE_ERROR, + "mtd device has only %d bytes for tags, need %d\n", + oobavail, YTAG1_SIZE); + return YAFFS_FAIL; + } + return YAFFS_OK; +} + +/* Query for the current state of a specific block. + * + * Examine the tags of the first chunk of the block and return the state: + * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad + * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use + * - YAFFS_BLOCK_STATE_EMPTY, the block is clean + * + * Always returns YAFFS_OK. + */ +int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * pState, int *pSequenceNumber) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkNo = blockNo * dev->nChunksPerBlock; + yaffs_ExtendedTags etags; + int state = YAFFS_BLOCK_STATE_DEAD; + int seqnum = 0; + int retval; + + /* We don't yet have a good place to test for MTD config prerequists. + * Do it here as we are called during the initial scan. + */ + if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { + return YAFFS_FAIL; + } + + retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); + if (etags.blockBad) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is marked bad", blockNo); + state = YAFFS_BLOCK_STATE_DEAD; + } + else if (etags.chunkUsed) { + state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + seqnum = etags.sequenceNumber; + } + else { + state = YAFFS_BLOCK_STATE_EMPTY; + } + + *pState = state; + *pSequenceNumber = seqnum; + + /* query always succeeds */ + return YAFFS_OK; +} + +#endif /*KERNEL_VERSION*/ diff -Nru linux-2.6.23/fs/yaffs2/yaffs_mtdif1.h kernel.android/fs/yaffs2/yaffs_mtdif1.h --- linux-2.6.23/fs/yaffs2/yaffs_mtdif1.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_mtdif1.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF1_H__ +#define __YAFFS_MTDIF1_H__ + +int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, const yaffs_ExtendedTags * tags); + +int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * data, yaffs_ExtendedTags * tags); + +int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); + +int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_mtdif2.c kernel.android/fs/yaffs2/yaffs_mtdif2.c --- linux-2.6.23/fs/yaffs2/yaffs_mtdif2.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_mtdif2.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,232 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + */ + +/* mtd interface for YAFFS2 */ + +const char *yaffs_mtdif2_c_version = + "$Id$"; + +#include "yportenv.h" + + +#include "yaffs_mtdif2.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" + +#include "yaffs_packedtags2.h" + +int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#else + size_t dummy; +#endif + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; + + yaffs_PackedTags2 pt; + + T(YAFFS_TRACE_MTD, + (TSTR + ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p" + TENDSTR), chunkInNAND, data, tags)); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + if (tags) + yaffs_PackTags2(&pt, tags); + else + BUG(); /* both tags and data should always be present */ + + if (data) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = dev->nDataBytesPerChunk; + ops.ooboffs = 0; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (void *)&pt; + retval = mtd->write_oob(mtd, addr, &ops); + } else + BUG(); /* both tags and data should always be present */ +#else + if (tags) { + yaffs_PackTags2(&pt, tags); + } + + if (data && tags) { + if (dev->useNANDECC) + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, (__u8 *) & pt, NULL); + else + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, (__u8 *) & pt, NULL); + } else { + if (data) + retval = + mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (tags) + retval = + mtd->write_oob(mtd, addr, mtd->oobsize, &dummy, + (__u8 *) & pt); + + } +#endif + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * data, yaffs_ExtendedTags * tags) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#endif + size_t dummy; + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; + + yaffs_PackedTags2 pt; + + T(YAFFS_TRACE_MTD, + (TSTR + ("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p" + TENDSTR), chunkInNAND, data, tags)); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + if (data && !tags) + retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data); + else if (tags) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt); + ops.ooboffs = 0; + ops.datbuf = data; + ops.oobbuf = dev->spareBuffer; + retval = mtd->read_oob(mtd, addr, &ops); + } +#else + if (data && tags) { + if (dev->useNANDECC) { + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, dev->spareBuffer, + NULL); + } else { + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, dev->spareBuffer, + NULL); + } + } else { + if (data) + retval = + mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (tags) + retval = + mtd->read_oob(mtd, addr, mtd->oobsize, &dummy, + dev->spareBuffer); + } +#endif + + memcpy(&pt, dev->spareBuffer, sizeof(pt)); + + if (tags) + yaffs_UnpackTags2(tags, &pt); + + if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR) + tags->eccResult = YAFFS_ECC_RESULT_UNFIXED; + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + int retval; + T(YAFFS_TRACE_MTD, + (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo)); + + retval = + mtd->block_markbad(mtd, + blockNo * dev->nChunksPerBlock * + dev->nDataBytesPerChunk); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; + +} + +int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + int retval; + + T(YAFFS_TRACE_MTD, + (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo)); + retval = + mtd->block_isbad(mtd, + blockNo * dev->nChunksPerBlock * + dev->nDataBytesPerChunk); + + if (retval) { + T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR))); + + *state = YAFFS_BLOCK_STATE_DEAD; + *sequenceNumber = 0; + } else { + yaffs_ExtendedTags t; + nandmtd2_ReadChunkWithTagsFromNAND(dev, + blockNo * + dev->nChunksPerBlock, NULL, + &t); + + if (t.chunkUsed) { + *sequenceNumber = t.sequenceNumber; + *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + } else { + *sequenceNumber = 0; + *state = YAFFS_BLOCK_STATE_EMPTY; + } + } + T(YAFFS_TRACE_MTD, + (TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber, + *state)); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_mtdif2.h kernel.android/fs/yaffs2/yaffs_mtdif2.h --- linux-2.6.23/fs/yaffs2/yaffs_mtdif2.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_mtdif2.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,29 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF2_H__ +#define __YAFFS_MTDIF2_H__ + +#include "yaffs_guts.h" +int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags); +int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * data, yaffs_ExtendedTags * tags); +int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); +int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_nand.c kernel.android/fs/yaffs2/yaffs_nand.c --- linux-2.6.23/fs/yaffs2/yaffs_nand.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_nand.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,134 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + */ + +const char *yaffs_nand_c_version = + "$Id$"; + +#include "yaffs_nand.h" +#include "yaffs_tagscompat.h" +#include "yaffs_tagsvalidity.h" + + +int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * buffer, + yaffs_ExtendedTags * tags) +{ + int result; + yaffs_ExtendedTags localTags; + + int realignedChunkInNAND = chunkInNAND - dev->chunkOffset; + + /* If there are no tags provided, use local tags to get prioritised gc working */ + if(!tags) + tags = &localTags; + + if (dev->readChunkWithTagsFromNAND) + result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer, + tags); + else + result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev, + realignedChunkInNAND, + buffer, + tags); + if(tags && + tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){ + + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, chunkInNAND/dev->nChunksPerBlock); + yaffs_HandleChunkError(dev,bi); + } + + return result; +} + +int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * buffer, + yaffs_ExtendedTags * tags) +{ + chunkInNAND -= dev->chunkOffset; + + + if (tags) { + tags->sequenceNumber = dev->sequenceNumber; + tags->chunkUsed = 1; + if (!yaffs_ValidateTags(tags)) { + T(YAFFS_TRACE_ERROR, + (TSTR("Writing uninitialised tags" TENDSTR))); + YBUG(); + } + T(YAFFS_TRACE_WRITE, + (TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND, + tags->objectId, tags->chunkId)); + } else { + T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR))); + YBUG(); + } + + if (dev->writeChunkWithTagsToNAND) + return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer, + tags); + else + return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev, + chunkInNAND, + buffer, + tags); +} + +int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo) +{ + blockNo -= dev->blockOffset; + +; + if (dev->markNANDBlockBad) + return dev->markNANDBlockBad(dev, blockNo); + else + return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo); +} + +int yaffs_QueryInitialBlockState(yaffs_Device * dev, + int blockNo, + yaffs_BlockState * state, + unsigned *sequenceNumber) +{ + blockNo -= dev->blockOffset; + + if (dev->queryNANDBlock) + return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber); + else + return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo, + state, + sequenceNumber); +} + + +int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, + int blockInNAND) +{ + int result; + + blockInNAND -= dev->blockOffset; + + + dev->nBlockErasures++; + result = dev->eraseBlockInNAND(dev, blockInNAND); + + return result; +} + +int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev) +{ + return dev->initialiseNAND(dev); +} + + + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_nand.h kernel.android/fs/yaffs2/yaffs_nand.h --- linux-2.6.23/fs/yaffs2/yaffs_nand.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_nand.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_NAND_H__ +#define __YAFFS_NAND_H__ +#include "yaffs_guts.h" + + + +int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * buffer, + yaffs_ExtendedTags * tags); + +int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * buffer, + yaffs_ExtendedTags * tags); + +int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo); + +int yaffs_QueryInitialBlockState(yaffs_Device * dev, + int blockNo, + yaffs_BlockState * state, + unsigned *sequenceNumber); + +int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, + int blockInNAND); + +int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev); + +#endif + diff -Nru linux-2.6.23/fs/yaffs2/yaffs_nandemul2k.h kernel.android/fs/yaffs2/yaffs_nandemul2k.h --- linux-2.6.23/fs/yaffs2/yaffs_nandemul2k.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_nandemul2k.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* Interface to emulated NAND functions (2k page size) */ + +#ifndef __YAFFS_NANDEMUL2K_H__ +#define __YAFFS_NANDEMUL2K_H__ + +#include "yaffs_guts.h" + +int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, const __u8 * data, + yaffs_ExtendedTags * tags); +int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, __u8 * data, + yaffs_ExtendedTags * tags); +int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); +int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); +int nandemul2k_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, + int blockInNAND); +int nandemul2k_InitialiseNAND(struct yaffs_DeviceStruct *dev); +int nandemul2k_GetBytesPerChunk(void); +int nandemul2k_GetChunksPerBlock(void); +int nandemul2k_GetNumberOfBlocks(void); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_packedtags1.c kernel.android/fs/yaffs2/yaffs_packedtags1.c --- linux-2.6.23/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_packedtags1.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,52 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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 "yaffs_packedtags1.h" +#include "yportenv.h" + +void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t) +{ + pt->chunkId = t->chunkId; + pt->serialNumber = t->serialNumber; + pt->byteCount = t->byteCount; + pt->objectId = t->objectId; + pt->ecc = 0; + pt->deleted = (t->chunkDeleted) ? 0 : 1; + pt->unusedStuff = 0; + pt->shouldBeFF = 0xFFFFFFFF; + +} + +void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt) +{ + static const __u8 allFF[] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff }; + + if (memcmp(allFF, pt, sizeof(yaffs_PackedTags1))) { + t->blockBad = 0; + if (pt->shouldBeFF != 0xFFFFFFFF) { + t->blockBad = 1; + } + t->chunkUsed = 1; + t->objectId = pt->objectId; + t->chunkId = pt->chunkId; + t->byteCount = pt->byteCount; + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + t->chunkDeleted = (pt->deleted) ? 0 : 1; + t->serialNumber = pt->serialNumber; + } else { + memset(t, 0, sizeof(yaffs_ExtendedTags)); + + } +} diff -Nru linux-2.6.23/fs/yaffs2/yaffs_packedtags1.h kernel.android/fs/yaffs2/yaffs_packedtags1.h --- linux-2.6.23/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_packedtags1.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,37 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ + +#ifndef __YAFFS_PACKEDTAGS1_H__ +#define __YAFFS_PACKEDTAGS1_H__ + +#include "yaffs_guts.h" + +typedef struct { + unsigned chunkId:20; + unsigned serialNumber:2; + unsigned byteCount:10; + unsigned objectId:18; + unsigned ecc:12; + unsigned deleted:1; + unsigned unusedStuff:1; + unsigned shouldBeFF; + +} yaffs_PackedTags1; + +void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t); +void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt); +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_packedtags2.c kernel.android/fs/yaffs2/yaffs_packedtags2.c --- linux-2.6.23/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_packedtags2.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,186 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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 "yaffs_packedtags2.h" +#include "yportenv.h" +#include "yaffs_tagsvalidity.h" + +/* This code packs a set of extended tags into a binary structure for + * NAND storage + */ + +/* Some of the information is "extra" struff which can be packed in to + * speed scanning + * This is defined by having the EXTRA_HEADER_INFO_FLAG set. + */ + +/* Extra flags applied to chunkId */ + +#define EXTRA_HEADER_INFO_FLAG 0x80000000 +#define EXTRA_SHRINK_FLAG 0x40000000 +#define EXTRA_SHADOWS_FLAG 0x20000000 +#define EXTRA_SPARE_FLAGS 0x10000000 + +#define ALL_EXTRA_FLAGS 0xF0000000 + +/* Also, the top 4 bits of the object Id are set to the object type. */ +#define EXTRA_OBJECT_TYPE_SHIFT (28) +#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT) + +#ifndef CONFIG_YAFFS_DOES_ECC +#define YAFFS_IGNORE_TAGS_ECC 1 +#endif + +static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 * pt) +{ + T(YAFFS_TRACE_MTD, + (TSTR("packed tags obj %d chunk %d byte %d seq %d" TENDSTR), + pt->t.objectId, pt->t.chunkId, pt->t.byteCount, + pt->t.sequenceNumber)); +} + +static void yaffs_DumpTags2(const yaffs_ExtendedTags * t) +{ + T(YAFFS_TRACE_MTD, + (TSTR + ("ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte " + "%d del %d ser %d seq %d" + TENDSTR), t->eccResult, t->blockBad, t->chunkUsed, t->objectId, + t->chunkId, t->byteCount, t->chunkDeleted, t->serialNumber, + t->sequenceNumber)); + +} + +void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t) +{ + pt->t.chunkId = t->chunkId; + pt->t.sequenceNumber = t->sequenceNumber; + pt->t.byteCount = t->byteCount; + pt->t.objectId = t->objectId; + + if (t->chunkId == 0 && t->extraHeaderInfoAvailable) { + /* Store the extra header info instead */ + /* We save the parent object in the chunkId */ + pt->t.chunkId = EXTRA_HEADER_INFO_FLAG + | t->extraParentObjectId; + if (t->extraIsShrinkHeader) { + pt->t.chunkId |= EXTRA_SHRINK_FLAG; + } + if (t->extraShadows) { + pt->t.chunkId |= EXTRA_SHADOWS_FLAG; + } + + pt->t.objectId &= ~EXTRA_OBJECT_TYPE_MASK; + pt->t.objectId |= + (t->extraObjectType << EXTRA_OBJECT_TYPE_SHIFT); + + if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) { + pt->t.byteCount = t->extraEquivalentObjectId; + } else if (t->extraObjectType == YAFFS_OBJECT_TYPE_FILE) { + pt->t.byteCount = t->extraFileLength; + } else { + pt->t.byteCount = 0; + } + } + + yaffs_DumpPackedTags2(pt); + yaffs_DumpTags2(t); + +#ifndef YAFFS_IGNORE_TAGS_ECC + { + yaffs_ECCCalculateOther((unsigned char *)&pt->t, + sizeof(yaffs_PackedTags2TagsPart), + &pt->ecc); + } +#endif +} + +void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt) +{ + + memset(t, 0, sizeof(yaffs_ExtendedTags)); + + yaffs_InitialiseTags(t); + + if (pt->t.sequenceNumber != 0xFFFFFFFF) { + /* Page is in use */ +#ifdef YAFFS_IGNORE_TAGS_ECC + { + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + } +#else + { + yaffs_ECCOther ecc; + int result; + yaffs_ECCCalculateOther((unsigned char *)&pt->t, + sizeof + (yaffs_PackedTags2TagsPart), + &ecc); + result = + yaffs_ECCCorrectOther((unsigned char *)&pt->t, + sizeof + (yaffs_PackedTags2TagsPart), + &pt->ecc, &ecc); + switch(result){ + case 0: + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + break; + case 1: + t->eccResult = YAFFS_ECC_RESULT_FIXED; + break; + case -1: + t->eccResult = YAFFS_ECC_RESULT_UNFIXED; + break; + default: + t->eccResult = YAFFS_ECC_RESULT_UNKNOWN; + } + } +#endif + t->blockBad = 0; + t->chunkUsed = 1; + t->objectId = pt->t.objectId; + t->chunkId = pt->t.chunkId; + t->byteCount = pt->t.byteCount; + t->chunkDeleted = 0; + t->serialNumber = 0; + t->sequenceNumber = pt->t.sequenceNumber; + + /* Do extra header info stuff */ + + if (pt->t.chunkId & EXTRA_HEADER_INFO_FLAG) { + t->chunkId = 0; + t->byteCount = 0; + + t->extraHeaderInfoAvailable = 1; + t->extraParentObjectId = + pt->t.chunkId & (~(ALL_EXTRA_FLAGS)); + t->extraIsShrinkHeader = + (pt->t.chunkId & EXTRA_SHRINK_FLAG) ? 1 : 0; + t->extraShadows = + (pt->t.chunkId & EXTRA_SHADOWS_FLAG) ? 1 : 0; + t->extraObjectType = + pt->t.objectId >> EXTRA_OBJECT_TYPE_SHIFT; + t->objectId &= ~EXTRA_OBJECT_TYPE_MASK; + + if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) { + t->extraEquivalentObjectId = pt->t.byteCount; + } else { + t->extraFileLength = pt->t.byteCount; + } + } + } + + yaffs_DumpPackedTags2(pt); + yaffs_DumpTags2(t); + +} diff -Nru linux-2.6.23/fs/yaffs2/yaffs_packedtags2.h kernel.android/fs/yaffs2/yaffs_packedtags2.h --- linux-2.6.23/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_packedtags2.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,38 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ + +#ifndef __YAFFS_PACKEDTAGS2_H__ +#define __YAFFS_PACKEDTAGS2_H__ + +#include "yaffs_guts.h" +#include "yaffs_ecc.h" + +typedef struct { + unsigned sequenceNumber; + unsigned objectId; + unsigned chunkId; + unsigned byteCount; +} yaffs_PackedTags2TagsPart; + +typedef struct { + yaffs_PackedTags2TagsPart t; + yaffs_ECCOther ecc; +} yaffs_PackedTags2; + +void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t); +void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt); +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_qsort.c kernel.android/fs/yaffs2/yaffs_qsort.c --- linux-2.6.23/fs/yaffs2/yaffs_qsort.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_qsort.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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. + */ + +#include "yportenv.h" +//#include + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + register TYPE *pi = (TYPE *) (parmi); \ + register TYPE *pj = (TYPE *) (parmj); \ + do { \ + register TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static __inline void +swapfunc(char *a, char *b, int n, int swaptype) +{ + if (swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +static __inline char * +med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) +{ + return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) + :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); +} + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +void +yaffs_qsort(void *aa, size_t n, size_t es, + int (*cmp)(const void *, const void *)) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + register char *a = aa; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp); + pm = med3(pm - d, pm, pm + d, cmp); + pn = med3(pn - 2 * d, pn - d, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); + } + swap(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = cmp(pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min((long)(pd - pc), (long)(pn - pd - es)); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + yaffs_qsort(a, r / es, es, cmp); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* yaffs_qsort(pn - r, r / es, es, cmp);*/ +} diff -Nru linux-2.6.23/fs/yaffs2/yaffs_qsort.h kernel.android/fs/yaffs2/yaffs_qsort.h --- linux-2.6.23/fs/yaffs2/yaffs_qsort.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_qsort.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,23 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YAFFS_QSORT_H__ +#define __YAFFS_QSORT_H__ + +extern void yaffs_qsort (void *const base, size_t total_elems, size_t size, + int (*cmp)(const void *, const void *)); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_tagscompat.c kernel.android/fs/yaffs2/yaffs_tagscompat.c --- linux-2.6.23/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_tagscompat.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,530 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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 "yaffs_guts.h" +#include "yaffs_tagscompat.h" +#include "yaffs_ecc.h" + +static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND); +#ifdef NOTYET +static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND); +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_Spare * spare); +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_Spare * spare); +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND); +#endif + +static const char yaffs_countBitsTable[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +int yaffs_CountBits(__u8 x) +{ + int retVal; + retVal = yaffs_countBitsTable[x]; + return retVal; +} + +/********** Tags ECC calculations *********/ + +void yaffs_CalcECC(const __u8 * data, yaffs_Spare * spare) +{ + yaffs_ECCCalculate(data, spare->ecc1); + yaffs_ECCCalculate(&data[256], spare->ecc2); +} + +void yaffs_CalcTagsECC(yaffs_Tags * tags) +{ + /* Calculate an ecc */ + + unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes; + unsigned i, j; + unsigned ecc = 0; + unsigned bit = 0; + + tags->ecc = 0; + + for (i = 0; i < 8; i++) { + for (j = 1; j & 0xff; j <<= 1) { + bit++; + if (b[i] & j) { + ecc ^= bit; + } + } + } + + tags->ecc = ecc; + +} + +int yaffs_CheckECCOnTags(yaffs_Tags * tags) +{ + unsigned ecc = tags->ecc; + + yaffs_CalcTagsECC(tags); + + ecc ^= tags->ecc; + + if (ecc && ecc <= 64) { + /* TODO: Handle the failure better. Retire? */ + unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes; + + ecc--; + + b[ecc / 8] ^= (1 << (ecc & 7)); + + /* Now recvalc the ecc */ + yaffs_CalcTagsECC(tags); + + return 1; /* recovered error */ + } else if (ecc) { + /* Wierd ecc failure value */ + /* TODO Need to do somethiong here */ + return -1; /* unrecovered error */ + } + + return 0; +} + +/********** Tags **********/ + +static void yaffs_LoadTagsIntoSpare(yaffs_Spare * sparePtr, + yaffs_Tags * tagsPtr) +{ + yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr; + + yaffs_CalcTagsECC(tagsPtr); + + sparePtr->tagByte0 = tu->asBytes[0]; + sparePtr->tagByte1 = tu->asBytes[1]; + sparePtr->tagByte2 = tu->asBytes[2]; + sparePtr->tagByte3 = tu->asBytes[3]; + sparePtr->tagByte4 = tu->asBytes[4]; + sparePtr->tagByte5 = tu->asBytes[5]; + sparePtr->tagByte6 = tu->asBytes[6]; + sparePtr->tagByte7 = tu->asBytes[7]; +} + +static void yaffs_GetTagsFromSpare(yaffs_Device * dev, yaffs_Spare * sparePtr, + yaffs_Tags * tagsPtr) +{ + yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr; + int result; + + tu->asBytes[0] = sparePtr->tagByte0; + tu->asBytes[1] = sparePtr->tagByte1; + tu->asBytes[2] = sparePtr->tagByte2; + tu->asBytes[3] = sparePtr->tagByte3; + tu->asBytes[4] = sparePtr->tagByte4; + tu->asBytes[5] = sparePtr->tagByte5; + tu->asBytes[6] = sparePtr->tagByte6; + tu->asBytes[7] = sparePtr->tagByte7; + + result = yaffs_CheckECCOnTags(tagsPtr); + if (result > 0) { + dev->tagsEccFixed++; + } else if (result < 0) { + dev->tagsEccUnfixed++; + } +} + +static void yaffs_SpareInitialise(yaffs_Spare * spare) +{ + memset(spare, 0xFF, sizeof(yaffs_Spare)); +} + +static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, const __u8 * data, + yaffs_Spare * spare) +{ + if (chunkInNAND < dev->startBlock * dev->nChunksPerBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs chunk %d is not valid" TENDSTR), + chunkInNAND)); + return YAFFS_FAIL; + } + + dev->nPageWrites++; + return dev->writeChunkToNAND(dev, chunkInNAND, data, spare); +} + +static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, + __u8 * data, + yaffs_Spare * spare, + yaffs_ECCResult * eccResult, + int doErrorCorrection) +{ + int retVal; + yaffs_Spare localSpare; + + dev->nPageReads++; + + if (!spare && data) { + /* If we don't have a real spare, then we use a local one. */ + /* Need this for the calculation of the ecc */ + spare = &localSpare; + } + + if (!dev->useNANDECC) { + retVal = dev->readChunkFromNAND(dev, chunkInNAND, data, spare); + if (data && doErrorCorrection) { + /* Do ECC correction */ + /* Todo handle any errors */ + int eccResult1, eccResult2; + __u8 calcEcc[3]; + + yaffs_ECCCalculate(data, calcEcc); + eccResult1 = + yaffs_ECCCorrect(data, spare->ecc1, calcEcc); + yaffs_ECCCalculate(&data[256], calcEcc); + eccResult2 = + yaffs_ECCCorrect(&data[256], spare->ecc2, calcEcc); + + if (eccResult1 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error fix performed on chunk %d:0" + TENDSTR), chunkInNAND)); + dev->eccFixed++; + } else if (eccResult1 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error unfixed on chunk %d:0" + TENDSTR), chunkInNAND)); + dev->eccUnfixed++; + } + + if (eccResult2 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error fix performed on chunk %d:1" + TENDSTR), chunkInNAND)); + dev->eccFixed++; + } else if (eccResult2 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error unfixed on chunk %d:1" + TENDSTR), chunkInNAND)); + dev->eccUnfixed++; + } + + if (eccResult1 || eccResult2) { + /* We had a data problem on this page */ + yaffs_HandleReadDataError(dev, chunkInNAND); + } + + if (eccResult1 < 0 || eccResult2 < 0) + *eccResult = YAFFS_ECC_RESULT_UNFIXED; + else if (eccResult1 > 0 || eccResult2 > 0) + *eccResult = YAFFS_ECC_RESULT_FIXED; + else + *eccResult = YAFFS_ECC_RESULT_NO_ERROR; + } + } else { + /* Must allocate enough memory for spare+2*sizeof(int) */ + /* for ecc results from device. */ + struct yaffs_NANDSpare nspare; + retVal = + dev->readChunkFromNAND(dev, chunkInNAND, data, + (yaffs_Spare *) & nspare); + memcpy(spare, &nspare, sizeof(yaffs_Spare)); + if (data && doErrorCorrection) { + if (nspare.eccres1 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error fix performed on chunk %d:0" + TENDSTR), chunkInNAND)); + } else if (nspare.eccres1 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error unfixed on chunk %d:0" + TENDSTR), chunkInNAND)); + } + + if (nspare.eccres2 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error fix performed on chunk %d:1" + TENDSTR), chunkInNAND)); + } else if (nspare.eccres2 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error unfixed on chunk %d:1" + TENDSTR), chunkInNAND)); + } + + if (nspare.eccres1 || nspare.eccres2) { + /* We had a data problem on this page */ + yaffs_HandleReadDataError(dev, chunkInNAND); + } + + if (nspare.eccres1 < 0 || nspare.eccres2 < 0) + *eccResult = YAFFS_ECC_RESULT_UNFIXED; + else if (nspare.eccres1 > 0 || nspare.eccres2 > 0) + *eccResult = YAFFS_ECC_RESULT_FIXED; + else + *eccResult = YAFFS_ECC_RESULT_NO_ERROR; + + } + } + return retVal; +} + +#ifdef NOTYET +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, + int chunkInNAND) +{ + + static int init = 0; + static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK]; + static __u8 data[YAFFS_BYTES_PER_CHUNK]; + /* Might as well always allocate the larger size for */ + /* dev->useNANDECC == true; */ + static __u8 spare[sizeof(struct yaffs_NANDSpare)]; + + dev->readChunkFromNAND(dev, chunkInNAND, data, (yaffs_Spare *) spare); + + if (!init) { + memset(cmpbuf, 0xff, YAFFS_BYTES_PER_CHUNK); + init = 1; + } + + if (memcmp(cmpbuf, data, YAFFS_BYTES_PER_CHUNK)) + return YAFFS_FAIL; + if (memcmp(cmpbuf, spare, 16)) + return YAFFS_FAIL; + + return YAFFS_OK; + +} +#endif + +/* + * Functions for robustisizing + */ + +static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND) +{ + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + + /* Mark the block for retirement */ + yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>>Block %d marked for retirement" TENDSTR), blockInNAND)); + + /* TODO: + * Just do a garbage collection on the affected block + * then retire the block + * NB recursion + */ +} + +#ifdef NOTYET +static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND) +{ +} + +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_Spare * spare) +{ +} + +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_Spare * spare) +{ +} + +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND) +{ + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + + /* Mark the block for retirement */ + yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1; + /* Delete the chunk */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); +} + +static int yaffs_VerifyCompare(const __u8 * d0, const __u8 * d1, + const yaffs_Spare * s0, const yaffs_Spare * s1) +{ + + if (memcmp(d0, d1, YAFFS_BYTES_PER_CHUNK) != 0 || + s0->tagByte0 != s1->tagByte0 || + s0->tagByte1 != s1->tagByte1 || + s0->tagByte2 != s1->tagByte2 || + s0->tagByte3 != s1->tagByte3 || + s0->tagByte4 != s1->tagByte4 || + s0->tagByte5 != s1->tagByte5 || + s0->tagByte6 != s1->tagByte6 || + s0->tagByte7 != s1->tagByte7 || + s0->ecc1[0] != s1->ecc1[0] || + s0->ecc1[1] != s1->ecc1[1] || + s0->ecc1[2] != s1->ecc1[2] || + s0->ecc2[0] != s1->ecc2[0] || + s0->ecc2[1] != s1->ecc2[1] || s0->ecc2[2] != s1->ecc2[2]) { + return 0; + } + + return 1; +} +#endif /* NOTYET */ + +int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * + eTags) +{ + yaffs_Spare spare; + yaffs_Tags tags; + + yaffs_SpareInitialise(&spare); + + if (eTags->chunkDeleted) { + spare.pageStatus = 0; + } else { + tags.objectId = eTags->objectId; + tags.chunkId = eTags->chunkId; + tags.byteCount = eTags->byteCount; + tags.serialNumber = eTags->serialNumber; + + if (!dev->useNANDECC && data) { + yaffs_CalcECC(data, &spare); + } + yaffs_LoadTagsIntoSpare(&spare, &tags); + + } + + return yaffs_WriteChunkToNAND(dev, chunkInNAND, data, &spare); +} + +int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev, + int chunkInNAND, + __u8 * data, + yaffs_ExtendedTags * eTags) +{ + + yaffs_Spare spare; + yaffs_Tags tags; + yaffs_ECCResult eccResult; + + static yaffs_Spare spareFF; + static int init; + + if (!init) { + memset(&spareFF, 0xFF, sizeof(spareFF)); + init = 1; + } + + if (yaffs_ReadChunkFromNAND + (dev, chunkInNAND, data, &spare, &eccResult, 1)) { + /* eTags may be NULL */ + if (eTags) { + + int deleted = + (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0; + + eTags->chunkDeleted = deleted; + eTags->eccResult = eccResult; + eTags->blockBad = 0; /* We're reading it */ + /* therefore it is not a bad block */ + eTags->chunkUsed = + (memcmp(&spareFF, &spare, sizeof(spareFF)) != + 0) ? 1 : 0; + + if (eTags->chunkUsed) { + yaffs_GetTagsFromSpare(dev, &spare, &tags); + + eTags->objectId = tags.objectId; + eTags->chunkId = tags.chunkId; + eTags->byteCount = tags.byteCount; + eTags->serialNumber = tags.serialNumber; + } + } + + return YAFFS_OK; + } else { + return YAFFS_FAIL; + } +} + +int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, + int blockInNAND) +{ + + yaffs_Spare spare; + + memset(&spare, 0xff, sizeof(yaffs_Spare)); + + spare.blockStatus = 'Y'; + + yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL, + &spare); + yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, + NULL, &spare); + + return YAFFS_OK; + +} + +int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, + int blockNo, yaffs_BlockState * + state, + int *sequenceNumber) +{ + + yaffs_Spare spare0, spare1; + static yaffs_Spare spareFF; + static int init; + yaffs_ECCResult dummy; + + if (!init) { + memset(&spareFF, 0xFF, sizeof(spareFF)); + init = 1; + } + + *sequenceNumber = 0; + + yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock, NULL, + &spare0, &dummy, 1); + yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock + 1, NULL, + &spare1, &dummy, 1); + + if (yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7) + *state = YAFFS_BLOCK_STATE_DEAD; + else if (memcmp(&spareFF, &spare0, sizeof(spareFF)) == 0) + *state = YAFFS_BLOCK_STATE_EMPTY; + else + *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + + return YAFFS_OK; +} diff -Nru linux-2.6.23/fs/yaffs2/yaffs_tagscompat.h kernel.android/fs/yaffs2/yaffs_tagscompat.h --- linux-2.6.23/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_tagscompat.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,40 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_TAGSCOMPAT_H__ +#define __YAFFS_TAGSCOMPAT_H__ + +#include "yaffs_guts.h" +int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * + tags); +int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev, + int chunkInNAND, + __u8 * data, + yaffs_ExtendedTags * + tags); +int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, + int blockNo); +int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, + int blockNo, yaffs_BlockState * + state, int *sequenceNumber); + +void yaffs_CalcTagsECC(yaffs_Tags * tags); +int yaffs_CheckECCOnTags(yaffs_Tags * tags); +int yaffs_CountBits(__u8 byte); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffs_tagsvalidity.c kernel.android/fs/yaffs2/yaffs_tagsvalidity.c --- linux-2.6.23/fs/yaffs2/yaffs_tagsvalidity.c 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_tagsvalidity.c 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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 "yaffs_tagsvalidity.h" + +void yaffs_InitialiseTags(yaffs_ExtendedTags * tags) +{ + memset(tags, 0, sizeof(yaffs_ExtendedTags)); + tags->validMarker0 = 0xAAAAAAAA; + tags->validMarker1 = 0x55555555; +} + +int yaffs_ValidateTags(yaffs_ExtendedTags * tags) +{ + return (tags->validMarker0 == 0xAAAAAAAA && + tags->validMarker1 == 0x55555555); + +} diff -Nru linux-2.6.23/fs/yaffs2/yaffs_tagsvalidity.h kernel.android/fs/yaffs2/yaffs_tagsvalidity.h --- linux-2.6.23/fs/yaffs2/yaffs_tagsvalidity.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffs_tagsvalidity.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,24 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YAFFS_TAGS_VALIDITY_H__ +#define __YAFFS_TAGS_VALIDITY_H__ + +#include "yaffs_guts.h" + +void yaffs_InitialiseTags(yaffs_ExtendedTags * tags); +int yaffs_ValidateTags(yaffs_ExtendedTags * tags); +#endif diff -Nru linux-2.6.23/fs/yaffs2/yaffsinterface.h kernel.android/fs/yaffs2/yaffsinterface.h --- linux-2.6.23/fs/yaffs2/yaffsinterface.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yaffsinterface.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,21 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFSINTERFACE_H__ +#define __YAFFSINTERFACE_H__ + +int yaffs_Initialise(unsigned nBlocks); + +#endif diff -Nru linux-2.6.23/fs/yaffs2/yportenv.h kernel.android/fs/yaffs2/yportenv.h --- linux-2.6.23/fs/yaffs2/yportenv.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/fs/yaffs2/yportenv.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,188 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * 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. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YPORTENV_H__ +#define __YPORTENV_H__ + +#if defined CONFIG_YAFFS_WINCE + +#include "ywinceenv.h" + +#elif defined __KERNEL__ + +#include "moduleconfig.h" + +/* Linux kernel */ +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strncmp(a,b,c) strncmp(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) + +#define Y_INLINE inline + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" + +/* #define YPRINTF(x) printk x */ +#define YMALLOC(x) kmalloc(x,GFP_KERNEL) +#define YFREE(x) kfree(x) +#define YMALLOC_ALT(x) vmalloc(x) +#define YFREE_ALT(x) vfree(x) +#define YMALLOC_DMA(x) YMALLOC(x) + +// KR - added for use in scan so processes aren't blocked indefinitely. +#define YYIELD() schedule() + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#define Y_CURRENT_TIME CURRENT_TIME.tv_sec +#define Y_TIME_CONVERT(x) (x).tv_sec +#else +#define Y_CURRENT_TIME CURRENT_TIME +#define Y_TIME_CONVERT(x) (x) +#endif + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#define TENDSTR "\n" +#define TSTR(x) KERN_WARNING x +#define TOUT(p) printk p + +#define yaffs_trace(mask, fmt, args...) \ + do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \ + printk(KERN_WARNING "yaffs: " fmt, ## args); \ + } while (0) + +#define compile_time_assertion(assertion) \ + ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) + +#elif defined CONFIG_YAFFS_DIRECT + +/* Direct interface */ +#include "ydirectenv.h" + +#elif defined CONFIG_YAFFS_UTIL + +/* Stuff for YAFFS utilities */ + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +#include "devextras.h" + +#define YMALLOC(x) malloc(x) +#define YFREE(x) free(x) +#define YMALLOC_ALT(x) malloc(x) +#define YFREE_ALT(x) free(x) + +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) + +#define Y_INLINE inline + +/* #define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) */ +/* #define YALERT(s) YINFO(s) */ + +#define TENDSTR "\n" +#define TSTR(x) x +#define TOUT(p) printf p + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" +/* #define YPRINTF(x) printf x */ + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#else +/* Should have specified a configuration type */ +#error Unknown configuration + +#endif + +/* see yaffs_fs.c */ +extern unsigned int yaffs_traceMask; +extern unsigned int yaffs_wr_attempts; + +/* + * Tracing flags. + * The flags masked in YAFFS_TRACE_ALWAYS are always traced. + */ + +#define YAFFS_TRACE_OS 0x00000002 +#define YAFFS_TRACE_ALLOCATE 0x00000004 +#define YAFFS_TRACE_SCAN 0x00000008 +#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 +#define YAFFS_TRACE_ERASE 0x00000020 +#define YAFFS_TRACE_GC 0x00000040 +#define YAFFS_TRACE_WRITE 0x00000080 +#define YAFFS_TRACE_TRACING 0x00000100 +#define YAFFS_TRACE_DELETION 0x00000200 +#define YAFFS_TRACE_BUFFERS 0x00000400 +#define YAFFS_TRACE_NANDACCESS 0x00000800 +#define YAFFS_TRACE_GC_DETAIL 0x00001000 +#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 +#define YAFFS_TRACE_MTD 0x00004000 +#define YAFFS_TRACE_CHECKPOINT 0x00008000 + +#define YAFFS_TRACE_VERIFY 0x00010000 +#define YAFFS_TRACE_VERIFY_NAND 0x00020000 +#define YAFFS_TRACE_VERIFY_FULL 0x00040000 +#define YAFFS_TRACE_VERIFY_ALL 0x000F0000 + + +#define YAFFS_TRACE_ERROR 0x40000000 +#define YAFFS_TRACE_BUG 0x80000000 +#define YAFFS_TRACE_ALWAYS 0xF0000000 + + +#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ALWAYS)) TOUT(p);} while(0) + +#ifndef CONFIG_YAFFS_WINCE +#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__)) +#endif + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/dma.h kernel.android/include/asm-arm/arch-goldfish/dma.h --- linux-2.6.23/include/asm-arm/arch-goldfish/dma.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/dma.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1 @@ +/* include/asm-arm/arch-goldfish/dma.h */ diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/entry-macro.S kernel.android/include/asm-arm/arch-goldfish/entry-macro.S --- linux-2.6.23/include/asm-arm/arch-goldfish/entry-macro.S 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/entry-macro.S 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,34 @@ +/* include/asm-arm/arch-goldfish/entry-macro.S +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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 + + .macro disable_fiq + .endm + + .macro get_irqnr_preamble, base, tmp + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + ldr \base, =IO_ADDRESS(GOLDFISH_INTERRUPT_BASE) + ldr \irqnr, [\base, #GOLDFISH_INTERRUPT_NUMBER] + ldr \irqstat, [\base, #GOLDFISH_INTERRUPT_STATUS] + teq \irqstat, #0 + /* EQ will be set if no irqs pending */ + .endm diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/hardware.h kernel.android/include/asm-arm/arch-goldfish/hardware.h --- linux-2.6.23/include/asm-arm/arch-goldfish/hardware.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/hardware.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,44 @@ +/* include/asm-arm/arch-goldfish/hardware.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#include + +/* + * Where in virtual memory the IO devices (timers, system controllers + * and so on) + */ +#define IO_BASE 0xfe000000 // VA of IO +#define IO_SIZE 0x00800000 // How much? +#define IO_START 0xff000000 // PA of IO + +#define GOLDFISH_INTERRUPT_BASE (0x0) +#define GOLDFISH_INTERRUPT_STATUS (0x00) // number of pending interrupts +#define GOLDFISH_INTERRUPT_NUMBER (0x04) +#define GOLDFISH_INTERRUPT_DISABLE_ALL (0x08) +#define GOLDFISH_INTERRUPT_DISABLE (0x0c) +#define GOLDFISH_INTERRUPT_ENABLE (0x10) + +#define GOLDFISH_PDEV_BUS_BASE (0x1000) +#define GOLDFISH_PDEV_BUS_END (0x100) + +#define GOLDFISH_TIMER_BASE (0x3000) + +/* macro to get at IO space when running virtually */ +#define IO_ADDRESS(x) ((x) + IO_BASE) + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/io.h kernel.android/include/asm-arm/arch-goldfish/io.h --- linux-2.6.23/include/asm-arm/arch-goldfish/io.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/io.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,24 @@ +/* include/asm-arm/arch-goldfish/io.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +#define __io(a) ((void __iomem *)(a)) +#define __mem_pci(a) (a) + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/irqs.h kernel.android/include/asm-arm/arch-goldfish/irqs.h --- linux-2.6.23/include/asm-arm/arch-goldfish/irqs.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/irqs.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,24 @@ +/* include/asm-arm/arch-goldfish/irqs.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_IRQS_H +#define __ASM_ARCH_IRQS_H + +#define IRQ_PDEV_BUS (1) +#define IRQ_TIMER (3) + +#define NR_IRQS (256) + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/memory.h kernel.android/include/asm-arm/arch-goldfish/memory.h --- linux-2.6.23/include/asm-arm/arch-goldfish/memory.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/memory.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,35 @@ +/* include/asm-arm/arch-goldfish/memory.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +/* + * Physical DRAM offset. + */ +#define PHYS_OFFSET UL(0x00000000) +#define BUS_OFFSET UL(0x00000000) + +/* + * Virtual view <-> DMA view memory address translations + * virt_to_bus: Used to translate the virtual address to an + * address suitable to be passed to set_dma_addr + * bus_to_virt: Used to convert an address for DMA operations + * to an address that the kernel can use. + */ +#define __virt_to_bus(x) (x - PAGE_OFFSET + BUS_OFFSET) +#define __bus_to_virt(x) (x - BUS_OFFSET + PAGE_OFFSET) + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/system.h kernel.android/include/asm-arm/arch-goldfish/system.h --- linux-2.6.23/include/asm-arm/arch-goldfish/system.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/system.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,28 @@ +/* include/asm-arm/arch-goldfish/system.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +static inline void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode) +{ +} + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/timer.h kernel.android/include/asm-arm/arch-goldfish/timer.h --- linux-2.6.23/include/asm-arm/arch-goldfish/timer.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/timer.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,27 @@ +/* include/asm-arm/arch-goldfish/timer.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_TIMER_H +#define __ASM_ARCH_TIMER_H + +enum { + TIMER_TIME_LOW = 0x00, // get low bits of current time and update TIMER_TIME_HIGH + TIMER_TIME_HIGH = 0x04, // get high bits of time at last TIMER_TIME_LOW read + TIMER_ALARM_LOW = 0x08, // set low bits of alarm and activate it + TIMER_ALARM_HIGH = 0x0c, // set high bits of next alarm + TIMER_CLEAR_INTERRUPT = 0x10 +}; + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/timex.h kernel.android/include/asm-arm/arch-goldfish/timex.h --- linux-2.6.23/include/asm-arm/arch-goldfish/timex.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/timex.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,24 @@ +/* include/asm-arm/arch-goldfish/timex.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_TIMEX_H +#define __ASM_ARCH_TIMEX_H + +/* + * ?? + */ +#define CLOCK_TICK_RATE (50000000 / 16) + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/uncompress.h kernel.android/include/asm-arm/arch-goldfish/uncompress.h --- linux-2.6.23/include/asm-arm/arch-goldfish/uncompress.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/uncompress.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,40 @@ +/* include/asm-arm/arch-goldfish/uncompress.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_UNCOMPRESS_H +#define __ASM_ARCH_UNCOMPRESS_H + +#define GOLDFISH_TTY_PUT_CHAR (*(volatile unsigned int *)0xff002000) + +/* + * This does not append a newline + */ +static void putc(int c) +{ + GOLDFISH_TTY_PUT_CHAR = c; +} + +static inline void flush(void) +{ +} + +/* + * nothing to do + */ +#define arch_decomp_setup() + +#define arch_decomp_wdog() + +#endif diff -Nru linux-2.6.23/include/asm-arm/arch-goldfish/vmalloc.h kernel.android/include/asm-arm/arch-goldfish/vmalloc.h --- linux-2.6.23/include/asm-arm/arch-goldfish/vmalloc.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/asm-arm/arch-goldfish/vmalloc.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,21 @@ +/* include/asm-arm/arch-goldfish/vmalloc.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef __ASM_ARCH_VMALLOC_H +#define __ASM_ARCH_VMALLOC_H + +#define VMALLOC_END (PAGE_OFFSET + 0x3c000000) + +#endif diff -Nru linux-2.6.23/include/asm-arm/elf.h kernel.android/include/asm-arm/elf.h --- linux-2.6.23/include/asm-arm/elf.h 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/include/asm-arm/elf.h 2007-11-12 07:49:03.000000000 +1100 @@ -60,6 +60,11 @@ #define ELF_PLATFORM (elf_platform) extern char elf_platform[]; + +struct task_struct; + +extern int dump_task_regs (struct task_struct *, elf_gregset_t *); + #endif /* @@ -114,6 +119,9 @@ } \ } while (0) +#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs) + + #endif #endif diff -Nru linux-2.6.23/include/linux/android_alarm.h kernel.android/include/linux/android_alarm.h --- linux-2.6.23/include/linux/android_alarm.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/linux/android_alarm.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,55 @@ +/* include/linux/android_alarm.h +** +** Copyright (C) 2006-2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef _LINUX_ANDROID_ALARM_H +#define _LINUX_ANDROID_ALARM_H + +#include +#include + +typedef enum { + // return code bit numbers or set alarm arg + ANDROID_ALARM_RTC_WAKEUP, + ANDROID_ALARM_RTC, + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, + ANDROID_ALARM_ELAPSED_REALTIME, + ANDROID_ALARM_SYSTEMTIME, + // + ANDROID_ALARM_TYPE_COUNT, + + // return code bit numbers +// ANDROID_ALARM_TIME_CHANGE = 16 +} android_alarm_type_t; + +typedef enum { + ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP, + ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC, + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK = 1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, + ANDROID_ALARM_ELAPSED_REALTIME_MASK = 1U << ANDROID_ALARM_ELAPSED_REALTIME, + ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME, + ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16 +} android_alarm_return_flags_t; + +#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4)) // diasable alarm +#define ANDROID_ALARM_WAIT _IO('a', 1) // ack last alarm and wait for next +#define ANDROID_ALARM_SET(type) _IOW('a', 2 | ((type) << 4), struct timespec) // set alarm +#define ANDROID_ALARM_SET_AND_WAIT(type) _IOW('a', 3 | ((type) << 4), struct timespec) +#define ANDROID_ALARM_GET_TIME(type) _IOW('a', 4 | ((type) << 4), struct timespec) +#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec) + +#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0))) +#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4) + +#endif diff -Nru linux-2.6.23/include/linux/android_gadget.h kernel.android/include/linux/android_gadget.h --- linux-2.6.23/include/linux/android_gadget.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/linux/android_gadget.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,88 @@ +/* include/linux/android_gadget.h +** +** Copyright (C) 2006-2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef _LINUX_ANDROID_GADGET_H +#define _LINUX_ANDROID_GADGET_H + + +/* used for ANDROID_GADGET_OPEN_FILE */ +struct android_gadget_file_open { + const char* path; + uint32_t path_length; // strlen(path) + uint32_t flags; + uint32_t mode; +}; + + +/* + * Used for optimized file copy ioctls ANDROID_GADGET_READ_TO_FILE and ANDROID_GADGET_WRITE_FROM_FILE. + * File's seek position must be set to the proper location before calling ioctl. +*/ +struct android_gadget_file_copy { + uint32_t fd; + uint32_t length; + uint64_t offset; +}; + + +/* Set number of UMS devices, for USB_BULK_GET_MAX_LUN_REQUEST SETUP request */ +#define ANDROID_GADGET_SET_UMS_DEVICE_COUNT _IO('g',1) + +/* Sets USB configuration settings */ +#define ANDROID_GADGET_SET_MANUFACTURER_NAME _IO('g',2) +#define ANDROID_GADGET_SET_PRODUCT_NAME _IO('g',3) +#define ANDROID_GADGET_SET_SERIAL _IO('g',4) +#define ANDROID_GADGET_SET_VENDOR_ID _IO('g',5) +#define ANDROID_GADGET_SET_PRODUCT_ID _IO('g',6) +#define ANDROID_GADGET_SET_COMPOSITE_PRODUCT_ID _IO('g',19) + +/* Enables ADB interface */ +#define ANDROID_GADGET_ENABLE_ADB _IO('g',7) + +/* Enables mass storage interface */ +#define ANDROID_GADGET_ENABLE_UMS _IO('g',8) + +/* Enables kernel debug interface */ +#define ANDROID_GADGET_ENABLE_KDBG _IO('g',18) + +/* Enables MTP support */ +#define ANDROID_GADGET_ENABLE_MTP _IO('g',21) + +/* + * Enable or disable USB, depending on boolean argument. + * Do this after all other configuation is complete + */ +#define ANDROID_GADGET_ENABLE_USB _IO('g',9) + +/* used to open a file within the kernel. ioctl returns fd or error */ +#define ANDROID_GADGET_OPEN_FILE _IO('g',10) + +/* used to close a file opened with ANDROID_GADGET_OPEN_FILE */ +#define ANDROID_GADGET_CLOSE_FILE _IO('g',11) + +/* used to copy data from USB to a local file */ +#define ANDROID_GADGET_READ_TO_FILE _IO('g',12) + +/* used to copy data from a local file to USB */ +#define ANDROID_GADGET_WRITE_FROM_FILE _IO('g',13) + +/* Used to query current state of android_gadget */ +#define ANDROID_GADGET_IS_CONNECTED _IO('g',14) +#define ANDROID_GADGET_IS_ADB_ENABLED _IO('g',15) +#define ANDROID_GADGET_IS_UMS_ENABLED _IO('g',16) +#define ANDROID_GADGET_IS_CONFIGURED _IO('g',17) +#define ANDROID_GADGET_IS_KDBG_ENABLED _IO('g',20) + +#endif /* _LINUX_ANDROID_GADGET_H */ diff -Nru linux-2.6.23/include/linux/android_power.h kernel.android/include/linux/android_power.h --- linux-2.6.23/include/linux/android_power.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/linux/android_power.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,89 @@ +/* include/linux/android_power.h +** +** Copyright (C) 2007 Google, Inc. +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef _LINUX_ANDROID_POWER_H +#define _LINUX_ANDROID_POWER_H + +#include +#include + +typedef struct +{ + struct list_head link; + int flags; + const char *name; + int expires; +#ifdef CONFIG_ANDROID_POWER_STAT + struct { + int count; + int expire_count; + ktime_t total_time; + ktime_t max_time; + ktime_t last_time; + } stat; +#endif +} android_suspend_lock_t; + +#if 0 // none of these flags are implemented +#define ANDROID_SUSPEND_LOCK_FLAG_COUNTED (1U << 0) +#define ANDROID_SUSPEND_LOCK_FLAG_USER_READABLE (1U << 1) +#define ANDROID_SUSPEND_LOCK_FLAG_USER_SET (1U << 2) +#define ANDROID_SUSPEND_LOCK_FLAG_USER_CLEAR (1U << 3) +#define ANDROID_SUSPEND_LOCK_FLAG_USER_INC (1U << 4) +#define ANDROID_SUSPEND_LOCK_FLAG_USER_DEC (1U << 5) +#define ANDROID_SUSPEND_LOCK_FLAG_USER_VISIBLE_MASK (0x1fU << 1) +#endif +#define ANDROID_SUSPEND_LOCK_AUTO_EXPIRE (1U << 6) +#define ANDROID_SUSPEND_LOCK_ACTIVE (1U << 7) + + +typedef struct android_early_suspend android_early_suspend_t; +struct android_early_suspend +{ + struct list_head link; + int level; + void (*suspend)(android_early_suspend_t *h); + void (*resume)(android_early_suspend_t *h); +}; + +typedef enum { + ANDROID_CHARGING_STATE_UNKNOWN, + ANDROID_CHARGING_STATE_DISCHARGE, + ANDROID_CHARGING_STATE_MAINTAIN, // or trickle + ANDROID_CHARGING_STATE_SLOW, + ANDROID_CHARGING_STATE_NORMAL, + ANDROID_CHARGING_STATE_FAST, + ANDROID_CHARGING_STATE_OVERHEAT +} android_charging_state_t; + +//android_suspend_lock_t *android_allocate_suspend_lock(const char *debug_name); +//void android_free_suspend_lock(android_suspend_lock_t *lock); +int android_init_suspend_lock(android_suspend_lock_t *lock); +void android_uninit_suspend_lock(android_suspend_lock_t *lock); +void android_lock_suspend(android_suspend_lock_t *lock); +void android_lock_suspend_auto_expire(android_suspend_lock_t *lock, int timeout); +void android_unlock_suspend(android_suspend_lock_t *lock); +void android_power_wakeup(int notification); /* notification = 0: normal wakeup, notification = 1: temporary wakeup */ + +int android_power_is_driver_suspended(void); + +void android_register_early_suspend(android_early_suspend_t *handler); +void android_unregister_early_suspend(android_early_suspend_t *handler); + +void android_power_set_battery_level(int level); // level 0-100 +void android_power_set_charging_state(android_charging_state_t state); + +#endif + diff -Nru linux-2.6.23/include/linux/binder_module.h kernel.android/include/linux/binder_module.h --- linux-2.6.23/include/linux/binder_module.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/linux/binder_module.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2005 Palmsource, Inc. + * + * This software is licensed as described in the file LICENSE, which + * you should have received as part of this distribution. The terms + * are also available at http://www.openbinder.org/license.html. + * + * This software consists of voluntary contributions made by many + * individuals. For the exact contribution history, see the revision + * history and logs, available at http://www.openbinder.org + */ + +#ifndef _BINDER_MODULE_H_ +#define _BINDER_MODULE_H_ + +#include + +#if TARGET_HOST == TARGET_HOST_PALMOS +#include +#include +#endif + +#ifdef __cplusplus +#if _SUPPORTS_NAMESPACE +namespace openbinder { +namespace support { +#endif +#endif + +// These are pre-packed type constants for the object type codes. +enum { + kPackedLargeBinderType = B_PACK_LARGE_TYPE(B_BINDER_TYPE), + kPackedLargeBinderWeakType = B_PACK_LARGE_TYPE(B_BINDER_WEAK_TYPE), + kPackedLargeBinderHandleType = B_PACK_LARGE_TYPE(B_BINDER_HANDLE_TYPE), + kPackedLargeBinderWeakHandleType = B_PACK_LARGE_TYPE(B_BINDER_WEAK_HANDLE_TYPE), + kPackedLargeBinderNodeType = B_PACK_LARGE_TYPE(B_BINDER_NODE_TYPE), + kPackedLargeBinderWeakNodeType = B_PACK_LARGE_TYPE(B_BINDER_WEAK_NODE_TYPE), +}; + +// Internal data structure used by driver. +struct binder_node; + +// This is the flattened representation of a Binder object for transfer +// between processes. The 'offsets' supplied as part of a binder transaction +// contains offsets into the data where these structures occur. The Binder +// driver takes care of re-writing the structure type and data as it moves +// between processes. +// +// Note that this is very intentionally designed to be the same as a user-space +// large_flat_data structure holding 8 bytes. The IPC mechanism requires that +// this structure be at least 8 bytes large. +typedef struct flat_binder_object +{ + // 8 bytes for large_flat_header. + unsigned long type; + unsigned long length; + + // 8 bytes of data. + union { + void* binder; // local object + signed long handle; // remote object + struct binder_node* node; // driver node + }; + void* cookie; // extra data associated with local object +} flat_binder_object_t; + +/* + * On 64-bit platforms where user code may run in 32-bits the driver must + * translate the buffer (and local binder) addresses apropriately. + */ + +typedef struct binder_write_read { + signed long write_size; // bytes to write + signed long write_consumed; // bytes consumed by driver (for ERESTARTSYS) + unsigned long write_buffer; + signed long read_size; // bytes to read + signed long read_consumed; // bytes consumed by driver (for ERESTARTSYS) + unsigned long read_buffer; +} binder_write_read_t; + +// Use with BINDER_VERSION, driver fills in fields. +typedef struct binder_version { + signed long protocol_version; // driver protocol version -- increment with incompatible change +} binder_version_t; + +// This is the current protocol version. +#define BINDER_CURRENT_PROTOCOL_VERSION 5 + +#define BINDER_IOC_MAGIC 'b' +#define BINDER_WRITE_READ _IOWR(BINDER_IOC_MAGIC, 1, binder_write_read_t) +#define BINDER_SET_WAKEUP_TIME _IOW(BINDER_IOC_MAGIC, 2, binder_wakeup_time_t) +#define BINDER_SET_IDLE_TIMEOUT _IOW(BINDER_IOC_MAGIC, 3, bigtime_t) +#define BINDER_SET_REPLY_TIMEOUT _IOW(BINDER_IOC_MAGIC, 4, bigtime_t) +#define BINDER_SET_MAX_THREADS _IOW(BINDER_IOC_MAGIC, 5, size_t) +#define BINDER_SET_IDLE_PRIORITY _IOW(BINDER_IOC_MAGIC, 6, int) +#define BINDER_SET_CONTEXT_MGR _IOW(BINDER_IOC_MAGIC, 7, int) +#define BINDER_THREAD_EXIT _IOW(BINDER_IOC_MAGIC, 8, int) +#define BINDER_VERSION _IOWR(BINDER_IOC_MAGIC, 9, binder_version_t) +#define BINDER_IOC_MAXNR 9 + +// NOTE: Two special error codes you should check for when calling +// in to the driver are: +// +// EINTR -- The operation has been interupted. This should be +// handled by retrying the ioctl() until a different error code +// is returned. +// +// ECONNREFUSED -- The driver is no longer accepting operations +// from your process. That is, the process is being destroyed. +// You should handle this by exiting from your process. Note +// that once this error code is returned, all further calls to +// the driver from any thread will return this same code. + +typedef int64_t bigtime_t; + +enum transaction_flags { + tfInline = 0x01, // not yet implemented + tfSynchronous = 0x02, // obsolete + tfRootObject = 0x04, // contents are the component's root object + tfStatusCode = 0x08 // contents are a 32-bit status code +}; + +typedef struct binder_transaction_data +{ + // The first two are only used for bcTRANSACTION and brTRANSACTION, + // identifying the target and contents of the transaction. + union { + unsigned long handle; // target descriptor of command transaction + void *ptr; // target descriptor of return transaction + } target; + void* cookie; // target object cookie + unsigned int code; // transaction command + + // General information about the transaction. + unsigned int flags; + int priority; // requested/current thread priority + size_t data_size; // number of bytes of data + size_t offsets_size; // number of bytes of flat_binder_object offsets + + // If this transaction is inline, the data immediately + // follows here; otherwise, it ends with a pointer to + // the data buffer. + union { + struct { + const void *buffer; // transaction data + const void *offsets; // offsets to flat_binder_object structs + } ptr; + uint8_t buf[8]; + } data; +} binder_transaction_data_t; + +typedef struct binder_wakeup_time +{ + bigtime_t time; + int priority; +} binder_wakeup_time_t; + +enum BinderDriverReturnProtocol { + brERROR = -1, + /* + int: error code + */ + + brOK = 0, + brTIMEOUT, + brWAKEUP, + /* No parameters! */ + + brTRANSACTION, + brREPLY, + /* + binder_transaction_data: the received command. + */ + + brACQUIRE_RESULT, + /* + int: 0 if the last bcATTEMPT_ACQUIRE was not successful. + Else the remote object has acquired a primary reference. + */ + + brDEAD_REPLY, + /* + The target of the last transaction (either a bcTRANSACTION or + a bcATTEMPT_ACQUIRE) is no longer with us. No parameters. + */ + + brTRANSACTION_COMPLETE, + /* + No parameters... always refers to the last transaction requested + (including replies). Note that this will be sent even for asynchronous + transactions. + */ + + brINCREFS, + brACQUIRE, + brRELEASE, + brDECREFS, + /* + void *: ptr to binder + void *: cookie for binder + */ + + brATTEMPT_ACQUIRE, + /* + int: priority + void *: ptr to binder + void *: cookie for binder + */ + + brEVENT_OCCURRED, + /* + This is returned when the bcSET_NEXT_EVENT_TIME has elapsed. + At this point the next event time is set to B_INFINITE_TIMEOUT, + so you must send another bcSET_NEXT_EVENT_TIME command if you + have another event pending. + */ + + brNOOP, + /* + * No parameters. Do nothing and examine the next command. It exists + * primarily so that we can replace it with a brSPAWN_LOOPER command. + */ + + brSPAWN_LOOPER, + /* + * No parameters. The driver has determined that a process has no threads + * waiting to service incomming transactions. When a process receives this + * command, it must spawn a new service thread and register it via + * bcENTER_LOOPER. + */ + + brFINISHED, + + brDEAD_BINDER, + /* + void *: cookie + */ + brCLEAR_DEATH_NOTIFICATION_DONE, + /* + void *: cookie + */ + + brFAILED_REPLY + /* + The the last transaction (either a bcTRANSACTION or + a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters. + */ +}; + +enum BinderDriverCommandProtocol { + bcNOOP = 0, + /* No parameters! */ + + bcTRANSACTION, + bcREPLY, + /* + binder_transaction_data: the sent command. + */ + + bcACQUIRE_RESULT, + /* + int: 0 if the last brATTEMPT_ACQUIRE was not successful. + Else you have acquired a primary reference on the object. + */ + + bcFREE_BUFFER, + /* + void *: ptr to transaction data received on a read + */ + + bcTRANSACTION_COMPLETE, + /* + No parameters... send when finishing an asynchronous transaction. + */ + + bcINCREFS, + bcACQUIRE, + bcRELEASE, + bcDECREFS, + /* + int: descriptor + */ + + bcINCREFS_DONE, + bcACQUIRE_DONE, + /* + void *: ptr to binder + void *: cookie for binder + */ + + bcATTEMPT_ACQUIRE, + /* + int: priority + int: descriptor + */ + + bcRETRIEVE_ROOT_OBJECT, + /* + int: process ID + */ + + bcSET_THREAD_ENTRY, + /* + void *: thread entry function for new threads created to handle tasks + void *: argument passed to those threads + */ + + bcREGISTER_LOOPER, + /* + No parameters. + Register a spawned looper thread with the device. This must be + called by the function that is supplied in bcSET_THREAD_ENTRY as + part of its initialization with the binder. + */ + + bcENTER_LOOPER, + bcEXIT_LOOPER, + /* + No parameters. + These two commands are sent as an application-level thread + enters and exits the binder loop, respectively. They are + used so the binder can have an accurate count of the number + of looping threads it has available. + */ + + bcSYNC, + /* + No parameters. + Upon receiving this command, the driver waits until all + pending asynchronous transactions have completed. + */ + +#if 0 + bcCATCH_ROOT_OBJECTS, + /* + No parameters. + Call this to have your team start catching root objects + published by other teams that are spawned outside of the binder. + When this happens, you will receive a brTRANSACTION with the + tfRootObject flag set. (Note that this is distinct from receiving + normal root objects, which are a brREPLY.) + */ +#endif + + bcSTOP_PROCESS, + /* + int: descriptor of process's root object + int: 1 to stop immediately, 0 when root object is released + */ + + bcSTOP_SELF, + /* + int: 1 to stop immediately, 0 when root object is released + */ + + bcREQUEST_DEATH_NOTIFICATION, + /* + void *: ptr to binder + void *: cookie + */ + + bcCLEAR_DEATH_NOTIFICATION, + /* + void *: ptr to binder + void *: cookie + */ + bcDEAD_BINDER_DONE + /* + void *: cookie + */ +}; + +#if 0 +/* Parameters for BINDER_READ_WRITE ioctl. */ +#if BINDER_DEBUG_LIB + +struct binder_write_read +{ + ssize_t write_size; + const void* write_buffer; + ssize_t read_size; + void* read_buffer; +}; + + +/* Below are calls to access the binder when debugging the driver from + user space by compiling it as libbinderdbg and linking libbe2 with it. */ + +extern int open_binder(int teamID=0); +extern status_t close_binder(int desc); +extern status_t ioctl_binder(int desc, int cmd, void *data, int len); +extern ssize_t read_binder(int desc, void *data, size_t numBytes); +extern ssize_t write_binder(int desc, void *data, size_t numBytes); + +#else + +#include +inline int open_binder(int ) { return open("/dev/misc/binder2",O_RDWR|O_CLOEXEC); }; +inline status_t close_binder(int desc) { return close(desc); }; +inline status_t ioctl_binder(int desc, int cmd, void *data, int len) { return ioctl(desc,cmd,data,len); }; +inline ssize_t read_binder(int desc, void *data, size_t numBytes) { return read(desc,data,numBytes); }; +inline ssize_t write_binder(int desc, void *data, size_t numBytes) { return write(desc,data,numBytes); }; + +#endif +#endif + +#ifdef __cplusplus +#if _SUPPORTS_NAMESPACE +} } // namespace openbinder::support +#endif +#endif + +#endif // _BINDER_MODULE_H_ diff -Nru linux-2.6.23/include/linux/binder_type_constants.h kernel.android/include/linux/binder_type_constants.h --- linux-2.6.23/include/linux/binder_type_constants.h 1970-01-01 10:00:00.000000000 +1000 +++ kernel.android/include/linux/binder_type_constants.h 2007-11-12 07:49:03.000000000 +1100 @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2005 Palmsource, Inc. + * + * This software is licensed as described in the file LICENSE, which + * you should have received as part of this distribution. The terms + * are also available at http://www.openbinder.org/license.html. + * + * This software consists of voluntary contributions made by many + * individuals. For the exact contribution history, see the revision + * history and logs, available at http://www.openbinder.org + */ + +#ifndef _SUPPORT_TYPECONSTANTS_H +#define _SUPPORT_TYPECONSTANTS_H + +/*! @file support/TypeConstants.h + @ingroup CoreSupportUtilities + @brief Format and standard definitions of SValue type codes. +*/ + +#ifdef __cplusplus +#if _SUPPORTS_NAMESPACE +namespace openbinder { +namespace support { +#endif +#endif + +/*! @addtogroup CoreSupportUtilities + @{ +*/ + +/*-------------------------------------------------------------*/ +/*----- Data Types --------------------------------------------*/ + +/*! @name Type Code Definitions + Type codes are 32-bit integers. The upper 24 bits are the + the code, and the lower 8 bits are metadata. The code is + constructed as 3 characters. Codes containing only the + characters a-z and _, and codes whose last letter is not + alphabetic (a-zA-Z), are reserved for use by the system. + Type codes that end with the character '*' contain + pointers to external objects. + Type codes that end with the character '#' are in a special + namespace reserved for SDimth units. */ +//@{ + +//! Type code manipulation. +enum { + B_TYPE_CODE_MASK = 0x7f7f7f00, // Usable bits for the type code + B_TYPE_CODE_SHIFT = 8, // Where code appears. + + B_TYPE_LENGTH_MASK = 0x00000007, // Usable bits for the data length + B_TYPE_LENGTH_MAX = 0x00000004, // Largest length that can be encoded in type + B_TYPE_LENGTH_LARGE = 0x00000005, // Value when length is > 4 bytes + B_TYPE_LENGTH_MAP = 0x00000007, // For use by SValue + + B_TYPE_BYTEORDER_MASK = 0x80000080, // Bits used to check byte order + B_TYPE_BYTEORDER_NORMAL = 0x00000080, // This bit is set if the byte order is okay + B_TYPE_BYTEORDER_SWAPPED = 0x80000000 // This bit is set if the byte order is swapped +}; + +//! Pack a small (size <= B_TYPE_LENGTH_MAX) type code from its constituent parts. +#define B_PACK_SMALL_TYPE(code, length) (((code)&B_TYPE_CODE_MASK) | (length) | B_TYPE_BYTEORDER_NORMAL) +//! Pack a large (size > B_TYPE_LENGTH_MAX) type code from its constituent parts. +#define B_PACK_LARGE_TYPE(code) (((code)&B_TYPE_CODE_MASK) | B_TYPE_LENGTH_LARGE | B_TYPE_BYTEORDER_NORMAL) +//! Retrieve type information from a packed type code. +#define B_UNPACK_TYPE_CODE(type) ((type)&B_TYPE_CODE_MASK) +//! Retrieve size information from a packaed type code. +#define B_UNPACK_TYPE_LENGTH(type) ((type)&B_TYPE_LENGTH_MASK) + +//! Build a valid code for a type code. +/*! Ensures only correct bits are used, and shifts value into correct location. */ +#define B_TYPE_CODE(code) (((code)< +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +** +*/ + +#ifndef _LINUX_LOGGER_H +#define _LINUX_LOGGER_H + +#include +#include + +struct logger_entry { + __u16 len; /* length of the payload */ + __u16 __pad; /* no matter what, we get 2 bytes of padding */ + __s32 pid; /* generating process's pid */ + __s32 tid; /* generating process's tid */ + __s32 sec; /* seconds since Epoch */ + __s32 nsec; /* nanoseconds */ + char msg[0]; /* the entry's payload */ +}; + +#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ +#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ +#define LOGGER_LOG_MAIN "log_main" /* everything else */ + +#define LOGGER_ENTRY_MAX_LEN (4*1024) +#define LOGGER_ENTRY_MAX_PAYLOAD \ + (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ + +#endif /* _LINUX_LOGGER_H */ diff -Nru linux-2.6.23/init/Kconfig kernel.android/init/Kconfig --- linux-2.6.23/init/Kconfig 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/init/Kconfig 2007-11-12 07:49:03.000000000 +1100 @@ -350,6 +350,12 @@ config SYSCTL bool +config PANIC_TIMEOUT + int "Default panic timeout" + default 0 + help + Set default panic timeout. + menuconfig EMBEDDED bool "Configure standard kernel features (for small systems)" help diff -Nru linux-2.6.23/kernel/exit.c kernel.android/kernel/exit.c --- linux-2.6.23/kernel/exit.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/exit.c 2007-11-12 07:49:03.000000000 +1100 @@ -854,6 +854,11 @@ release_task(tsk); } +#ifdef CONFIG_QEMU_TRACE +// -- wen +extern void qemu_trace_exit(int code); +#endif + #ifdef CONFIG_DEBUG_STACK_USAGE static void check_stack_usage(void) { @@ -1024,6 +1029,14 @@ /* causes final put_task_struct in finish_task_switch(). */ tsk->state = TASK_DEAD; +#ifdef CONFIG_QEMU_TRACE + /* + * signal QEMU of exit event + * -- wen + */ + qemu_trace_exit(code); +#endif + schedule(); BUG(); /* Avoid "noreturn function does return". */ diff -Nru linux-2.6.23/kernel/fork.c kernel.android/kernel/fork.c --- linux-2.6.23/kernel/fork.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/fork.c 2007-11-12 07:49:03.000000000 +1100 @@ -1354,6 +1354,11 @@ return 0; } +// -- wen +#ifdef CONFIG_QEMU_TRACE +extern void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags); +#endif + /* * Ok, this is the main fork-routine. * @@ -1412,6 +1417,14 @@ ptrace_notify ((trace << 8) | SIGTRAP); } +#ifdef CONFIG_QEMU_TRACE + /* + * signal QEMU of fork event + * -- wen + */ + qemu_trace_fork(p, clone_flags); +#endif + if (clone_flags & CLONE_VFORK) { freezer_do_not_count(); wait_for_completion(&vfork); diff -Nru linux-2.6.23/kernel/futex.c kernel.android/kernel/futex.c --- linux-2.6.23/kernel/futex.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/futex.c 2007-11-12 07:49:03.000000000 +1100 @@ -1292,8 +1292,8 @@ restart->fn = futex_wait_restart; restart->arg0 = (unsigned long)uaddr; restart->arg1 = (unsigned long)val; - restart->arg2 = (unsigned long)abs_time; - restart->arg3 = 0; + restart->arg2 = abs_time->tv64 & 0xFFFFFFFF; + restart->arg3 = abs_time->tv64 >> 32; if (fshared) restart->arg3 |= ARG3_SHARED; return -ERESTART_RESTARTBLOCK; @@ -1312,13 +1312,14 @@ { u32 __user *uaddr = (u32 __user *)restart->arg0; u32 val = (u32)restart->arg1; - ktime_t *abs_time = (ktime_t *)restart->arg2; + ktime_t abs_time; struct rw_semaphore *fshared = NULL; + abs_time.tv64 = ((u64)restart->arg3 << 32) | (u64) restart->arg2; restart->fn = do_no_restart_syscall; if (restart->arg3 & ARG3_SHARED) fshared = ¤t->mm->mmap_sem; - return (long)futex_wait(uaddr, fshared, val, abs_time); + return (long)futex_wait(uaddr, fshared, val, &abs_time); } diff -Nru linux-2.6.23/kernel/hrtimer.c kernel.android/kernel/hrtimer.c --- linux-2.6.23/kernel/hrtimer.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/hrtimer.c 2007-11-12 07:49:03.000000000 +1100 @@ -1125,8 +1125,14 @@ * If the timer was rearmed on another CPU, reprogram * the event device. */ - if (timer->base->first == &timer->node) - hrtimer_reprogram(timer, timer->base); + if (timer->base->first == &timer->node) { + if(hrtimer_reprogram(timer, timer->base)) { + __remove_hrtimer(timer, timer->base, + HRTIMER_STATE_PENDING, 0); + list_add_tail(&timer->cb_entry, + &cpu_base->cb_pending); + } + } } } spin_unlock_irq(&cpu_base->lock); diff -Nru linux-2.6.23/kernel/panic.c kernel.android/kernel/panic.c --- linux-2.6.23/kernel/panic.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/panic.c 2007-11-12 07:49:03.000000000 +1100 @@ -26,7 +26,10 @@ static int pause_on_oops_flag; static DEFINE_SPINLOCK(pause_on_oops_lock); -int panic_timeout; +#ifndef CONFIG_PANIC_TIMEOUT +#define CONFIG_PANIC_TIMEOUT 0 +#endif +int panic_timeout = CONFIG_PANIC_TIMEOUT; ATOMIC_NOTIFIER_HEAD(panic_notifier_list); diff -Nru linux-2.6.23/kernel/power/console.c kernel.android/kernel/power/console.c --- linux-2.6.23/kernel/power/console.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/power/console.c 2007-11-12 07:49:03.000000000 +1100 @@ -52,6 +52,12 @@ acquire_console_sem(); set_console(orig_fgconsole); release_console_sem(); + + if (vt_waitactive(orig_fgconsole)) { + pr_debug("Resume: Can't switch VCs."); + return; + } + kmsg_redirect = orig_kmsg; return; } diff -Nru linux-2.6.23/kernel/power/process.c kernel.android/kernel/power/process.c --- linux-2.6.23/kernel/power/process.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/power/process.c 2007-11-12 07:49:03.000000000 +1100 @@ -13,6 +13,9 @@ #include #include #include +#ifdef CONFIG_ANDROID_POWER +#include +#endif /* * Timeout for stopping processes @@ -149,6 +152,10 @@ } while_each_thread(g, p); read_unlock(&tasklist_lock); yield(); /* Yield is okay here */ +#ifdef CONFIG_ANDROID_POWER + if (todo && !android_power_is_driver_suspended()) + break; +#endif if (time_after(jiffies, end_time)) break; } while (todo); diff -Nru linux-2.6.23/kernel/sched.c kernel.android/kernel/sched.c --- linux-2.6.23/kernel/sched.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/kernel/sched.c 2007-11-12 07:49:03.000000000 +1100 @@ -1852,6 +1852,11 @@ put_user(current->pid, current->set_child_tid); } +// -- wen +#ifdef CONFIG_QEMU_TRACE +extern void qemu_trace_cs(struct task_struct *prev, struct task_struct *next); +#endif + /* * context_switch - switch to the new MM and the new * thread's register state. @@ -1893,6 +1898,16 @@ spin_release(&rq->lock.dep_map, 1, _THIS_IP_); #endif +#ifdef CONFIG_QEMU_TRACE + /* + * signal QEMU of context switch event + * set up the pid here. real event happens + * in __switch_to() + * -- wen + */ + qemu_trace_cs(prev, next); +#endif + /* Here we just switch the register state and the stack. */ switch_to(prev, next, prev); diff -Nru linux-2.6.23/mm/mmap.c kernel.android/mm/mmap.c --- linux-2.6.23/mm/mmap.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/mm/mmap.c 2007-11-12 07:49:03.000000000 +1100 @@ -885,6 +885,10 @@ } #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_QEMU_TRACE +/* -- wen */ +extern void qemu_trace_mmap(struct vm_area_struct * vma); +#endif /* * The caller must hold down_write(current->mm->mmap_sem). */ @@ -1173,6 +1177,14 @@ pgoff = vma->vm_pgoff; vm_flags = vma->vm_flags; +#ifdef CONFIG_QEMU_TRACE + /* + * trace the mmap + * -- wen + */ + qemu_trace_mmap(vma); +#endif + if (vma_wants_writenotify(vma)) vma->vm_page_prot = protection_map[vm_flags & (VM_READ|VM_WRITE|VM_EXEC)]; @@ -1201,6 +1213,7 @@ } if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) make_pages_present(addr, addr + len); + return addr; unmap_and_free_vma: diff -Nru linux-2.6.23/scripts/kallsyms.c kernel.android/scripts/kallsyms.c --- linux-2.6.23/scripts/kallsyms.c 2007-10-10 06:31:38.000000000 +1000 +++ kernel.android/scripts/kallsyms.c 2007-11-12 07:49:03.000000000 +1100 @@ -31,6 +31,47 @@ #define KSYM_NAME_LEN 128 +#if defined(__APPLE__) /* memmem() doesn't exist on Darwin */ +static void *memmem(const void *haystack, size_t n, const void *needle, size_t m) +{ + const unsigned char *y = (const unsigned char *)haystack; + const unsigned char *x = (const unsigned char *)needle; + + size_t j, k, l; + + if (m > n || !m || !n) + return NULL; + + if (1 != m) { + if (x[0] == x[1]) { + k = 2; + l = 1; + } else { + k = 1; + l = 2; + } + + j = 0; + while (j <= n - m) { + if (x[1] != y[j + 1]) { + j += k; + } else { + if (!memcmp(x + 2, y + j + 2, m - 2) + && x[0] == y[j]) + return (void *)&y[j]; + j += l; + } + } + } else + do { + if (*y == *x) + return (void *)y; + y++; + } while (--n); + + return NULL; +} +#endif struct sym_entry { unsigned long long addr;