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,3 +21,4 @@ obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_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,6 +100,13 @@ 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. config MMC_ASIC3 tristate "HTC ASIC3 SD/MMC support" depends on MMC && HTC_ASIC3 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,3 +15,4 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_ASIC3) += asic3_mmc.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