1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-24 00:07:11 +02:00

add the 'goldfish' target, useful for experimenting with virtual phone hardware (includes the emulator)

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@16459 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
nbd 2009-06-14 20:42:33 +00:00
parent 8378beaa85
commit 2d581e6047
57 changed files with 16670 additions and 0 deletions

View File

@ -0,0 +1,68 @@
#
# Copyright (C) 2006 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=goldfish-qemu
PKG_VERSION:=20090429
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=git://android.git.kernel.org/platform/external/qemu
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=2b8ea29e2bd12f876a4d06647e6077bf72de567e
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_TARGETS:=bin
include $(INCLUDE_DIR)/package.mk
define Package/goldfish-qemu
SECTION:=emulator
CATEGORY:=Emulators
DEPENDS:=@TARGET_goldfish
TITLE:=A modified version of the Google Android Emulator
URL:=http://www.android.com/
endef
LIBSDL_PATCHED:=sdl-1.2.12-android-20080919
define Download/libsdl-patched
FILE:=$(LIBSDL_PATCHED).tar.gz
URL:=http://android.git.kernel.org/pub
MD5SUM:=22df8cbb2ecb811938eba8410e861650
endef
$(eval $(call Download,libsdl-patched))
Build/Exports=
define Build/Prepare
$(call Build/Prepare/Default)
zcat $(DL_DIR)/$(LIBSDL_PATCHED).tar.gz | tar x -C $(PKG_BUILD_DIR)
endef
define Build/Configure
[ -x $(PKG_BUILD_DIR)/libsdl/bin/sdl-config ] || ( \
cd $(PKG_BUILD_DIR)/$(LIBSDL_PATCHED); \
./android-configure --prefix=$(PKG_BUILD_DIR)/libsdl; \
make all install; \
)
endef
define Build/Compile
(cd $(PKG_BUILD_DIR); \
[ -f $(PKG_BUILD_DIR)/objs/config.make ] || \
./android-configure.sh --sdl-config=$(PKG_BUILD_DIR)/libsdl/bin/sdl-config \
)
$(MAKE) -C $(PKG_BUILD_DIR)
endef
define Package/goldfish-qemu/install
$(INSTALL_DIR) $(1)
$(CP) $(PKG_BUILD_DIR)/objs/emulator $(1)/
endef
$(eval $(call BuildPackage,goldfish-qemu))

View File

@ -0,0 +1,30 @@
--- a/android-configure.sh
+++ b/android-configure.sh
@@ -656,6 +656,9 @@ case "$CPU" in
*) HOST_CPU=$CPU
;;
esac
+case "$OS" in
+ darwin*) echo "#define _BSD 1" >> $config_h;;
+esac
echo "#define HOST_$HOST_CPU 1" >> $config_h
log "Generate : $config_h"
--- a/android/utils/display-quartz.m
+++ b/android/utils/display-quartz.m
@@ -34,6 +34,7 @@ get_monitor_resolution( int *px_dpi, in
int
get_nearest_monitor_rect( int *x, int *y, int *width, int *height )
{
+#if 0
SDL_SysWMinfo info;
NSWindow* window;
@@ -108,4 +109,7 @@ get_nearest_monitor_rect( int *x, int
}
return 0;
}
+#else
+ return -1;
+#endif
};

View File

@ -0,0 +1,233 @@
--- a/android/cmdline-option.c
+++ b/android/cmdline-option.c
@@ -50,16 +50,6 @@ android_parse_options( int *pargc, char
char arg2_tab[64], *arg2 = arg2_tab;
int nn;
- /* process @<name> as a special exception meaning
- * '-avd <name>'
- */
- if (aread[0][0] == '@') {
- opt->avd = aread[0]+1;
- nargs--;
- aread++;
- continue;
- }
-
/* anything that isn't an option past this points
* exits the loop
*/
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -60,21 +60,16 @@
*/
CFG_PARAM( sysdir, "<dir>", "search for system disk images in <dir>" )
-CFG_PARAM( system, "<file>", "read initial system image from <file>" )
-CFG_PARAM( datadir, "<dir>", "write user data into <dir>" )
-CFG_PARAM( kernel, "<file>", "use specific emulated kernel" )
-CFG_PARAM( ramdisk, "<file>", "ramdisk image (default <system>/ramdisk.img" )
-CFG_PARAM( image, "<file>", "obsolete, use -system <file> instead" )
-CFG_PARAM( init_data, "<file>", "initial data image (default <system>/userdata.img" )
-CFG_PARAM( initdata, "<file>", "same as '-init-data <file>'" )
-CFG_PARAM( data, "<file>", "data image (default <datadir>/userdata-qemu.img" )
+CFG_PARAM( system, "<file>", "read system image from <file>, default: <system>/system.img" )
+CFG_PARAM( data, "<file>", "data image, default: <system>/data.img" )
+CFG_PARAM( kernel, "<file>", "use specific emulated kernel, default: kernel.bin" )
+CFG_PARAM( ramdisk, "<file>", "ramdisk image (default <system>/ramdisk.bin" )
CFG_PARAM( partition_size, "<size>", "system/data partition size in MBs" )
CFG_PARAM( cache, "<file>", "cache partition image (default is temporary file)" )
CFG_FLAG ( no_cache, "disable the cache partition" )
CFG_FLAG ( nocache, "same as -no-cache" )
OPT_PARAM( sdcard, "<file>", "SD card image (default <system>/sdcard.img")
OPT_FLAG ( wipe_data, "reset the use data image (copy it from initdata)" )
-CFG_PARAM( avd, "<name>", "use a specific android virtual device" )
CFG_PARAM( skindir, "<dir>", "search skins in <dir> (default <system>/skins)" )
CFG_PARAM( skin, "<name>", "select a given skin" )
CFG_FLAG ( no_skin, "don't use any emulator skin" )
--- a/android/main.c
+++ b/android/main.c
@@ -1606,6 +1606,7 @@ report_console( const char* proto_port,
* containing 'fileName'. this is *not* the full
* path to 'fileName'.
*/
+
static char*
_getSdkImagePath( const char* fileName )
{
@@ -1617,8 +1618,6 @@ _getSdkImagePath( const char* fileName
static const char* const searchPaths[] = {
"", /* program's directory */
- "/lib/images", /* this is for SDK 1.0 */
- "/../platforms/android-1.1/images", /* this is for SDK 1.1 */
NULL
};
@@ -1841,25 +1840,7 @@ int main(int argc, char **argv)
}
}
- /* legacy support: we used to use -system <dir> and -image <file>
- * instead of -sysdir <dir> and -system <file>, so handle this by checking
- * whether the options point to directories or files.
- */
- if (opts->image != NULL) {
- if (opts->system != NULL) {
- if (opts->sysdir != NULL) {
- derror( "You can't use -sysdir, -system and -image at the same time.\n"
- "You should probably use '-sysdir <path> -system <file>'.\n" );
- exit(2);
- }
- }
- dwarning( "Please note that -image is obsolete and that -system is now used to point\n"
- "to the system image. Next time, try using '-sysdir <path> -system <file>' instead.\n" );
- opts->sysdir = opts->system;
- opts->system = opts->image;
- opts->image = NULL;
- }
- else if (opts->system != NULL && path_is_dir(opts->system)) {
+ if (opts->system != NULL && path_is_dir(opts->system)) {
if (opts->sysdir != NULL) {
derror( "Option -system should now be followed by a file path, not a directory one.\n"
"Please use '-sysdir <path>' to point to the system directory.\n" );
@@ -1885,49 +1866,11 @@ int main(int argc, char **argv)
if (opts->noskin)
opts->no_skin = opts->noskin;
- if (opts->initdata) {
- opts->init_data = opts->initdata;
- opts->initdata = NULL;
- }
-
- /* If no AVD name was given, try to find the top of the
- * Android build tree
- */
- if (opts->avd == NULL) {
- do {
- char* out = getenv("ANDROID_PRODUCT_OUT");
-
- if (out == NULL || out[0] == 0)
- break;
-
- if (!path_exists(out)) {
- derror("Can't access ANDROID_PRODUCT_OUT as '%s'\n"
- "You need to build the Android system before launching the emulator",
- out);
- exit(2);
- }
-
- android_build_root = path_parent( out, 4 );
- if (android_build_root == NULL || !path_exists(android_build_root)) {
- derror("Can't find the Android build root from '%s'\n"
- "Please check the definition of the ANDROID_PRODUCT_OUT variable.\n"
- "It should point to your product-specific build output directory.\n",
- out );
- exit(2);
- }
- android_build_out = out;
- D( "found Android build root: %s", android_build_root );
- D( "found Android build out: %s", android_build_out );
- } while (0);
- }
/* if no virtual device name is given, and we're not in the
* Android build system, we'll need to perform some auto-detection
* magic :-)
*/
- if (opts->avd == NULL && !android_build_out)
{
- char dataDirIsSystem = 0;
-
if (!opts->sysdir) {
opts->sysdir = _getSdkImagePath("system.img");
if (!opts->sysdir) {
@@ -1945,47 +1888,30 @@ int main(int argc, char **argv)
}
if (!opts->system) {
- opts->system = _getSdkSystemImage(opts->sysdir, "-image", "system.img");
- D("autoconfig: -image %s", opts->image);
+ opts->system = _getSdkSystemImage(opts->sysdir, "-system", "system.img");
+ D("autoconfig: -system %s", opts->system);
}
if (!opts->kernel) {
- opts->kernel = _getSdkSystemImage(opts->sysdir, "-kernel", "kernel-qemu");
+ opts->kernel = _getSdkSystemImage(opts->sysdir, "-kernel", "kernel.bin");
D("autoconfig: -kernel %s", opts->kernel);
}
if (!opts->ramdisk) {
- opts->ramdisk = _getSdkSystemImage(opts->sysdir, "-ramdisk", "ramdisk.img");
+ opts->ramdisk = _getSdkSystemImage(opts->sysdir, "-ramdisk", "ramdisk.bin");
D("autoconfig: -ramdisk %s", opts->ramdisk);
}
- /* if no data directory is specified, use the system directory */
- if (!opts->datadir) {
- opts->datadir = qemu_strdup(opts->sysdir);
- dataDirIsSystem = 1;
- D("autoconfig: -datadir %s", opts->sysdir);
- }
-
if (!opts->data) {
/* check for userdata-qemu.img in the data directory */
- bufprint(tmp, tmpend, "%s/userdata-qemu.img", opts->datadir);
- if (!path_exists(tmp)) {
- derror(
- "You did not provide the name of an Android Virtual Device\n"
- "with the '-avd <name>' option. Read -help-avd for more information.\n\n"
-
- "If you *really* want to *NOT* run an AVD, consider using '-data <file>'\n"
- "to specify a data partition image file (I hope you know what you're doing).\n"
- );
- exit(2);
- }
+ bufprint(tmp, tmpend, "%s/data.img", opts->sysdir);
opts->data = qemu_strdup(tmp);
D("autoconfig: -data %s", opts->data);
}
- if (!opts->sdcard && opts->datadir) {
- bufprint(tmp, tmpend, "%s/sdcard.img", opts->datadir);
+ if (!opts->sdcard && opts->sysdir) {
+ bufprint(tmp, tmpend, "%s/sdcard.img", opts->sysdir);
if (path_exists(tmp)) {
opts->sdcard = qemu_strdup(tmp);
D("autoconfig: -sdcard %s", opts->sdcard);
@@ -2029,19 +1955,6 @@ int main(int argc, char **argv)
android_avdParams->skinName = opts->skin;
android_avdParams->skinRootPath = opts->skindir;
- /* setup the virtual device differently depending on whether
- * we are in the Android build system or not
- */
- if (opts->avd != NULL)
- {
- android_avdInfo = avdInfo_new( opts->avd, android_avdParams );
- if (android_avdInfo == NULL) {
- /* an error message has already been printed */
- dprint("could not find virtual device named '%s'", opts->avd);
- exit(1);
- }
- }
- else
{
if (!android_build_out) {
android_build_out = android_build_root = opts->sysdir;
--- a/android/avd/info.c
+++ b/android/avd/info.c
@@ -1233,10 +1233,8 @@ _getBuildImagePaths( AvdInfo* i, AvdInf
** take care of checking the state
**/
imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
- imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK );
-
- /* force the system image to read-only status */
- l->pState[0] = IMAGE_STATE_READONLY;
+ l->pState[0] = IMAGE_STATE_MUSTLOCK;
+ imageLoader_load( l, IMAGE_REQUIRED );
/** cache partition handling
**/

View File

@ -0,0 +1,25 @@
#
# Copyright (C) 2006-2009 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
ARCH:=arm
BOARD:=goldfish
BOARDNAME:=Goldfish (Android Emulator)
FEATURES:=
CFLAGS:=-O2 -pipe -march=armv5te -mtune=xscale -funit-at-a-time
LINUX_VERSION:=2.6.30
DEVICE_TYPE=phone
define Target/Description
Android emulator virtual platform
endef
include $(INCLUDE_DIR)/target.mk
$(eval $(call BuildTarget))

View File

@ -0,0 +1,218 @@
CONFIG_AEABI=y
CONFIG_ALIGNMENT_TRAP=y
CONFIG_ARCH_GOLDFISH=y
# CONFIG_ARCH_HAS_HOLES_MEMORYMODEL is not set
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SUPPORTS_MSI is not set
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARM=y
CONFIG_ARM_THUMB=y
# CONFIG_BACKTRACE_SELF_TEST is not set
CONFIG_BASE_SMALL=0
# CONFIG_BATTERY_DS2760 is not set
CONFIG_BATTERY_GOLDFISH=y
# CONFIG_BINARY_PRINTF is not set
CONFIG_BITREVERSE=y
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
# CONFIG_BOOT_PRINTK_DELAY is not set
CONFIG_CMDLINE="console=/dev/ttyS0 root=mtdblock0 rootdelay=1 rootfstype=yaffs2"
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_CPU_32=y
CONFIG_CPU_32v5=y
CONFIG_CPU_ABRT_EV5TJ=y
CONFIG_CPU_ARM926T=y
# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
CONFIG_CPU_CACHE_VIVT=y
CONFIG_CPU_COPY_V4WB=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y
# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
# CONFIG_CPU_ICACHE_DISABLE is not set
CONFIG_CPU_PABRT_NOIFAR=y
CONFIG_CPU_TLB_V4WBI=y
CONFIG_CRYPTO_AEAD2=y
CONFIG_CRYPTO_BLKCIPHER2=y
CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_MANAGER2=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_WORKQUEUE=y
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_ERRORS is not set
# CONFIG_DEBUG_INFO is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_KOBJECT is not set
# CONFIG_DEBUG_LIST is not set
# CONFIG_DEBUG_LL is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_SHIRQ is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_USER is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_WRITECOUNT is not set
# CONFIG_DETECT_HUNG_TASK is not set
CONFIG_DETECT_SOFTLOCKUP=y
CONFIG_DEVKMEM=y
# CONFIG_DM9000 is not set
CONFIG_DUMMY_CONSOLE=y
# CONFIG_FAULT_INJECTION is not set
CONFIG_FB=y
# CONFIG_FB_BACKLIGHT is not set
# CONFIG_FB_BOOT_VESA_SUPPORT is not set
# CONFIG_FB_BROADSHEET is not set
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
# CONFIG_FB_DDC is not set
# CONFIG_FB_FOREIGN_ENDIAN is not set
CONFIG_FB_GOLDFISH=y
# CONFIG_FB_MACMODES is not set
# CONFIG_FB_MB862XX is not set
# CONFIG_FB_METRONOME is not set
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_SYS_COPYAREA is not set
# CONFIG_FB_SYS_FILLRECT is not set
# CONFIG_FB_SYS_FOPS is not set
# CONFIG_FB_SYS_IMAGEBLIT is not set
CONFIG_FB_TILEBLITTING=y
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FONTS is not set
CONFIG_FONT_8x16=y
CONFIG_FONT_8x8=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_FRAME_POINTER=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_FIND_LAST_BIT=y
# CONFIG_GENERIC_GPIO is not set
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_GOLDFISH_TTY=y
# CONFIG_HAMRADIO is not set
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAVE_AOUT=y
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_HAVE_IDE=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HID=y
CONFIG_HID_SUPPORT=y
CONFIG_HW_CONSOLE=y
# CONFIG_HW_RANDOM is not set
# CONFIG_I2C is not set
CONFIG_INITRAMFS_SOURCE=""
CONFIG_INPUT=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_GPIO=y
CONFIG_INPUT_KEYBOARD=y
CONFIG_INPUT_KEYRESET=y
CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_ISDN is not set
# CONFIG_JFFS2_CMODE_PRIORITY is not set
CONFIG_JFFS2_CMODE_SIZE=y
# CONFIG_KEYBOARD_ATKBD is not set
CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KGDB is not set
# CONFIG_LOCK_STAT is not set
# CONFIG_LOGO is not set
CONFIG_MACH_GOLDFISH=y
# CONFIG_MTD_CFI is not set
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_CONCAT=y
CONFIG_MTD_GOLDFISH_NAND=y
CONFIG_MTD_NAND=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_RESERVE=1
# CONFIG_MTD_UBI_DEBUG is not set
CONFIG_MTD_UBI_GLUEBI=y
CONFIG_MTD_UBI_WL_THRESHOLD=4096
# CONFIG_NETDEV_1000 is not set
# CONFIG_NO_IOPORT is not set
# CONFIG_OABI_COMPAT is not set
# CONFIG_OUTER_CACHE is not set
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_PAGE_OFFSET=0xC0000000
# CONFIG_PAGE_POISONING is not set
# CONFIG_PCI_SYSCALL is not set
# CONFIG_PDA_POWER is not set
CONFIG_POWER_SUPPLY=y
# CONFIG_POWER_SUPPLY_DEBUG is not set
# CONFIG_PROVE_LOCKING is not set
CONFIG_QEMU_TRACE=y
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_RD_LZMA is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_SCHEDSTATS is not set
CONFIG_SCHED_DEBUG=y
# CONFIG_SCSI_DMA is not set
# CONFIG_SERIAL_8250 is not set
# CONFIG_SLOW_WORK is not set
CONFIG_SMC91X=y
CONFIG_SPLIT_PTLOCK_CPUS=4096
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_TICK_ONESHOT=y
# CONFIG_TIMER_STATS is not set
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_ELO is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
CONFIG_TRACING_SUPPORT=y
# CONFIG_UBIFS_FS is not set
CONFIG_UEVENT_HELPER_PATH=""
CONFIG_UID16=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_VFP=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_WATCHDOG is not set
CONFIG_ZBOOT_ROM_BSS=0
CONFIG_ZBOOT_ROM_TEXT=0
CONFIG_ZONE_DMA_FLAG=0

View File

@ -0,0 +1,43 @@
#
# Copyright (C) 2006 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/image.mk
JFFS2_BLOCKSIZE=124k
JFFS2OPTS += -n -s 2048
_PREFIX=openwrt-goldfish-
define Image/BuildKernel
$(TARGET_CROSS)objcopy -O binary -R .note -R .comment -S \
$(LINUX_DIR)/arch/arm/boot/compressed/vmlinux $(BIN_DIR)/$(_PREFIX)kernel.bin
$(if $(CONFIG_TARGET_ROOTFS_INITRAMFS), \
$(CP) $(LINUX_DIR)/usr/initramfs_data.cpio.gz, \
gzip -c < $(LINUX_DIR)/usr/initramfs_data.cpio > \
) $(BIN_DIR)/$(_PREFIX)ramdisk.bin
$(CP) ./run-emulator.sh $(BIN_DIR)/
endef
define Image/Build/jffs2-124k
$(CP) ./ubinize.cfg $(KDIR)/
(cd $(KDIR); \
ubinize \
-o $(BIN_DIR)/$(_PREFIX)$(1).img \
-p 128KiB -m 2KiB -s 2KiB ubinize.cfg; \
)
nand_ecc \
$(BIN_DIR)/$(_PREFIX)$(1).img \
$(BIN_DIR)/$(_PREFIX)system.bin
endef
define Image/Build
$(call Image/Build/$(1),$(1))
endef
$(eval $(call BuildImage))

View File

@ -0,0 +1,3 @@
PREFIX=openwrt-goldfish-
touch ${PREFIX}data.bin
./goldfish-qemu/emulator -sysdir . -ramdisk ${PREFIX}ramdisk.bin -kernel ${PREFIX}kernel.bin -system ${PREFIX}system.bin -data ${PREFIX}data.bin -show-kernel -partition-size 100 -qemu -append "console=ttyS0 ubi.mtd=0 root=/dev/mtdblock3 rootfstype=jffs2 bootdelay=1 init=/etc/preinit" -net nic,vlan=0,model=smc91c111 -net user,vlan=0,hostname=OpenWrt

View File

@ -0,0 +1,14 @@
[rootfs]
# Volume mode (other option is static)
mode=ubi
# Source image
image=root.jffs2-124k
# Volume ID in UBI image
vol_id=0
# Allow for dynamic resize
vol_type=dynamic
# Volume name
vol_name=rootfs
# Autoresize volume at first mount
vol_flags=autoresize

View File

@ -0,0 +1,36 @@
From 0c61b75f9da1a0889959a0f9bd0b8b63f936ddf3 Mon Sep 17 00:00:00 2001
From: Tony Lindgren <tony@atomide.com>
Date: Mon, 9 May 2005 14:10:26 -0700
Subject: [PATCH 042/134] ARM: Make low-level printk work
Makes low-level printk work.
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
kernel/printk.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -45,6 +45,10 @@ void asmlinkage __attribute__((weak)) ea
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+#ifdef CONFIG_DEBUG_LL
+extern void printascii(char *);
+#endif
+
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
@@ -687,6 +691,10 @@ asmlinkage int vprintk(const char *fmt,
sizeof(printk_buf) - printed_len, fmt, args);
+#ifdef CONFIG_DEBUG_LL
+ printascii(printk_buf);
+#endif
+
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here

View File

@ -0,0 +1,71 @@
From 3742e6638bdb7325c6432e2a145ad985ee47d052 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Mon, 26 Jan 2009 19:13:47 -0800
Subject: [PATCH 052/134] lowmemorykiller: Only iterate over process list when needed.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Use NR_ACTIVE plus NR_INACTIVE as a size estimate for our fake cache
instead the sum of rss. Neither method is accurate.
Also skip the process scan, if the amount of memory available is above
the largest threshold set.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/staging/android/lowmemorykiller.c | 35 +++++++++++++++++-----------
1 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 3715d56..b9a2e84 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -71,23 +71,30 @@ static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
}
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);
+ rem = global_page_state(NR_ACTIVE) + global_page_state(NR_INACTIVE);
+ if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
+ lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
+ return rem;
+ }
+
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 (p->oomkilladj < min_adj || !p->mm)
+ continue;
+ tasksize = get_mm_rss(p->mm);
+ if (tasksize <= 0)
+ continue;
+ if (selected) {
+ if (p->oomkilladj < selected->oomkilladj)
+ continue;
+ if (p->oomkilladj == selected->oomkilladj &&
+ tasksize <= selected_tasksize)
+ continue;
}
+ 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);
}
if(selected != NULL) {
lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
--
1.6.2

View File

@ -0,0 +1,53 @@
From f82da10dcae73652a6f0355e4398b4be1af17e6b Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Mon, 26 Jan 2009 19:22:19 -0800
Subject: [PATCH 053/134] lowmemorykiller: Don't count free space unless it meets the specified limit by itself
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
This allows processes to be killed when the kernel evict cache pages in
an attempt to get more contiguous free memory.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/staging/android/lowmemorykiller.c | 13 +++++++++----
1 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index b9a2e84..b2ab7fa 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -58,20 +58,25 @@ static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
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);
+ int other_free = global_page_state(NR_FREE_PAGES);
+ int other_file = 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]) {
+ if (other_free < lowmem_minfree[i] &&
+ other_file < 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);
- rem = global_page_state(NR_ACTIVE) + global_page_state(NR_INACTIVE);
+ lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", nr_to_scan, gfp_mask, other_free, other_file, min_adj);
+ rem = global_page_state(NR_ACTIVE_ANON) +
+ global_page_state(NR_ACTIVE_FILE) +
+ global_page_state(NR_INACTIVE_ANON) +
+ global_page_state(NR_INACTIVE_FILE);
if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
return rem;
--
1.6.2

View File

@ -0,0 +1,432 @@
From e4c9c5d7d6d5deb124083678fe5d839d3133f60a Mon Sep 17 00:00:00 2001
From: Mike Lockwood <lockwood@android.com>
Date: Mon, 12 Jan 2009 13:25:05 -0500
Subject: [PATCH 054/134] timed_gpio: Separate timed_output class into a separate driver.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Mike Lockwood <lockwood@android.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/staging/android/Kconfig | 6 ++-
drivers/staging/android/Makefile | 1 +
drivers/staging/android/timed_gpio.c | 98 +++++++++++--------------
drivers/staging/android/timed_gpio.h | 4 +-
drivers/staging/android/timed_output.c | 121 ++++++++++++++++++++++++++++++++
drivers/staging/android/timed_output.h | 37 ++++++++++
6 files changed, 210 insertions(+), 57 deletions(-)
create mode 100644 drivers/staging/android/timed_output.c
create mode 100644 drivers/staging/android/timed_output.h
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 604bd1e..1784508 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -73,9 +73,13 @@ config ANDROID_RAM_CONSOLE_EARLY_SIZE
default 0
depends on ANDROID_RAM_CONSOLE_EARLY_INIT
+config ANDROID_TIMED_OUTPUT
+ bool "Timed output class driver"
+ default y
+
config ANDROID_TIMED_GPIO
tristate "Android timed gpio driver"
- depends on GENERIC_GPIO
+ depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT
default n
config ANDROID_LOW_MEMORY_KILLER
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index 95209d6..8e057e6 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
+obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c
index 33daff0..be7cdaa 100644
--- a/drivers/staging/android/timed_gpio.c
+++ b/drivers/staging/android/timed_gpio.c
@@ -20,13 +20,12 @@
#include <linux/err.h>
#include <linux/gpio.h>
+#include "timed_output.h"
#include "timed_gpio.h"
-static struct class *timed_gpio_class;
-
struct timed_gpio_data {
- struct device *dev;
+ struct timed_output_dev dev;
struct hrtimer timer;
spinlock_t lock;
unsigned gpio;
@@ -36,70 +35,62 @@ struct timed_gpio_data {
static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
{
- struct timed_gpio_data *gpio_data = container_of(timer, struct timed_gpio_data, timer);
+ struct timed_gpio_data *data =
+ container_of(timer, struct timed_gpio_data, timer);
- gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? 1 : 0);
+ gpio_direction_output(data->gpio, data->active_low ? 1 : 0);
return HRTIMER_NORESTART;
}
-static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+static int gpio_get_time(struct timed_output_dev *dev)
{
- struct timed_gpio_data *gpio_data = dev_get_drvdata(dev);
- int remaining;
+ struct timed_gpio_data *data =
+ container_of(dev, struct timed_gpio_data, dev);
- if (hrtimer_active(&gpio_data->timer)) {
- ktime_t r = hrtimer_get_remaining(&gpio_data->timer);
+ if (hrtimer_active(&data->timer)) {
+ ktime_t r = hrtimer_get_remaining(&data->timer);
struct timeval t = ktime_to_timeval(r);
- remaining = t.tv_sec * 1000 + t.tv_usec / 1000;
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
} else
- remaining = 0;
-
- return sprintf(buf, "%d\n", remaining);
+ return 0;
}
-static ssize_t gpio_enable_store(
- struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
+static void gpio_enable(struct timed_output_dev *dev, int value)
{
- struct timed_gpio_data *gpio_data = dev_get_drvdata(dev);
- int value;
+ struct timed_gpio_data *data =
+ container_of(dev, struct timed_gpio_data, dev);
unsigned long flags;
- sscanf(buf, "%d", &value);
-
- spin_lock_irqsave(&gpio_data->lock, flags);
+ spin_lock_irqsave(&data->lock, flags);
/* cancel previous timer and set GPIO according to value */
- hrtimer_cancel(&gpio_data->timer);
- gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? !value : !!value);
+ hrtimer_cancel(&data->timer);
+ gpio_direction_output(data->gpio, data->active_low ? !value : !!value);
if (value > 0) {
- if (value > gpio_data->max_timeout)
- value = gpio_data->max_timeout;
+ if (value > data->max_timeout)
+ value = data->max_timeout;
- hrtimer_start(&gpio_data->timer,
- ktime_set(value / 1000, (value % 1000) * 1000000),
- HRTIMER_MODE_REL);
+ hrtimer_start(&data->timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
}
- spin_unlock_irqrestore(&gpio_data->lock, flags);
-
- return size;
+ spin_unlock_irqrestore(&data->lock, flags);
}
-static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, gpio_enable_show, gpio_enable_store);
-
static int timed_gpio_probe(struct platform_device *pdev)
{
struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
struct timed_gpio *cur_gpio;
struct timed_gpio_data *gpio_data, *gpio_dat;
- int i, ret = 0;
+ int i, j, ret = 0;
if (!pdata)
return -EBUSY;
- gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, GFP_KERNEL);
+ gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios,
+ GFP_KERNEL);
if (!gpio_data)
return -ENOMEM;
@@ -107,23 +98,26 @@ static int timed_gpio_probe(struct platform_device *pdev)
cur_gpio = &pdata->gpios[i];
gpio_dat = &gpio_data[i];
- hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
gpio_dat->timer.function = gpio_timer_func;
spin_lock_init(&gpio_dat->lock);
+ gpio_dat->dev.name = cur_gpio->name;
+ gpio_dat->dev.get_time = gpio_get_time;
+ gpio_dat->dev.enable = gpio_enable;
+ ret = timed_output_dev_register(&gpio_dat->dev);
+ if (ret < 0) {
+ for (j = 0; j < i; j++)
+ timed_output_dev_unregister(&gpio_data[i].dev);
+ kfree(gpio_data);
+ return ret;
+ }
+
gpio_dat->gpio = cur_gpio->gpio;
gpio_dat->max_timeout = cur_gpio->max_timeout;
gpio_dat->active_low = cur_gpio->active_low;
gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low);
-
- gpio_dat->dev = device_create(timed_gpio_class, &pdev->dev, 0, "%s", cur_gpio->name);
- if (unlikely(IS_ERR(gpio_dat->dev)))
- return PTR_ERR(gpio_dat->dev);
-
- dev_set_drvdata(gpio_dat->dev, gpio_dat);
- ret = device_create_file(gpio_dat->dev, &dev_attr_enable);
- if (ret)
- return ret;
}
platform_set_drvdata(pdev, gpio_data);
@@ -137,10 +131,8 @@ static int timed_gpio_remove(struct platform_device *pdev)
struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < pdata->num_gpios; i++) {
- device_remove_file(gpio_data[i].dev, &dev_attr_enable);
- device_unregister(gpio_data[i].dev);
- }
+ for (i = 0; i < pdata->num_gpios; i++)
+ timed_output_dev_unregister(&gpio_data[i].dev);
kfree(gpio_data);
@@ -151,22 +143,18 @@ static struct platform_driver timed_gpio_driver = {
.probe = timed_gpio_probe,
.remove = timed_gpio_remove,
.driver = {
- .name = "timed-gpio",
+ .name = TIMED_GPIO_NAME,
.owner = THIS_MODULE,
},
};
static int __init timed_gpio_init(void)
{
- timed_gpio_class = class_create(THIS_MODULE, "timed_output");
- if (IS_ERR(timed_gpio_class))
- return PTR_ERR(timed_gpio_class);
return platform_driver_register(&timed_gpio_driver);
}
static void __exit timed_gpio_exit(void)
{
- class_destroy(timed_gpio_class);
platform_driver_unregister(&timed_gpio_driver);
}
diff --git a/drivers/staging/android/timed_gpio.h b/drivers/staging/android/timed_gpio.h
index 78449b2..a0e15f8 100644
--- a/drivers/staging/android/timed_gpio.h
+++ b/drivers/staging/android/timed_gpio.h
@@ -16,10 +16,12 @@
#ifndef _LINUX_TIMED_GPIO_H
#define _LINUX_TIMED_GPIO_H
+#define TIMED_GPIO_NAME "timed-gpio"
+
struct timed_gpio {
const char *name;
unsigned gpio;
- int max_timeout;
+ int max_timeout;
u8 active_low;
};
diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c
new file mode 100644
index 0000000..62e7918
--- /dev/null
+++ b/drivers/staging/android/timed_output.c
@@ -0,0 +1,121 @@
+/* drivers/misc/timed_output.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+
+#include "timed_output.h"
+
+static struct class *timed_output_class;
+static atomic_t device_count;
+
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
+ int remaining = tdev->get_time(tdev);
+
+ return sprintf(buf, "%d\n", remaining);
+}
+
+static ssize_t enable_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
+ int value;
+
+ sscanf(buf, "%d", &value);
+ tdev->enable(tdev, value);
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+
+static int create_timed_output_class(void)
+{
+ if (!timed_output_class) {
+ timed_output_class = class_create(THIS_MODULE, "timed_output");
+ if (IS_ERR(timed_output_class))
+ return PTR_ERR(timed_output_class);
+ atomic_set(&device_count, 0);
+ }
+
+ return 0;
+}
+
+int timed_output_dev_register(struct timed_output_dev *tdev)
+{
+ int ret;
+
+ if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
+ return -EINVAL;
+
+ ret = create_timed_output_class();
+ if (ret < 0)
+ return ret;
+
+ tdev->index = atomic_inc_return(&device_count);
+ tdev->dev = device_create(timed_output_class, NULL,
+ MKDEV(0, tdev->index), NULL, tdev->name);
+ if (IS_ERR(tdev->dev))
+ return PTR_ERR(tdev->dev);
+
+ ret = device_create_file(tdev->dev, &dev_attr_enable);
+ if (ret < 0)
+ goto err_create_file;
+
+ dev_set_drvdata(tdev->dev, tdev);
+ tdev->state = 0;
+ return 0;
+
+err_create_file:
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
+ printk(KERN_ERR "timed_output: Failed to register driver %s\n",
+ tdev->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(timed_output_dev_register);
+
+void timed_output_dev_unregister(struct timed_output_dev *tdev)
+{
+ device_remove_file(tdev->dev, &dev_attr_enable);
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
+ dev_set_drvdata(tdev->dev, NULL);
+}
+EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
+
+static int __init timed_output_init(void)
+{
+ return create_timed_output_class();
+}
+
+static void __exit timed_output_exit(void)
+{
+ class_destroy(timed_output_class);
+}
+
+module_init(timed_output_init);
+module_exit(timed_output_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("timed output class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/timed_output.h b/drivers/staging/android/timed_output.h
new file mode 100644
index 0000000..ec907ab
--- /dev/null
+++ b/drivers/staging/android/timed_output.h
@@ -0,0 +1,37 @@
+/* include/linux/timed_output.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#ifndef _LINUX_TIMED_OUTPUT_H
+#define _LINUX_TIMED_OUTPUT_H
+
+struct timed_output_dev {
+ const char *name;
+
+ /* enable the output and set the timer */
+ void (*enable)(struct timed_output_dev *sdev, int timeout);
+
+ /* returns the current number of milliseconds remaining on the timer */
+ int (*get_time)(struct timed_output_dev *sdev);
+
+ /* private data */
+ struct device *dev;
+ int index;
+ int state;
+};
+
+extern int timed_output_dev_register(struct timed_output_dev *dev);
+extern void timed_output_dev_unregister(struct timed_output_dev *dev);
+
+#endif
--
1.6.2

View File

@ -0,0 +1,62 @@
From d620f695290e4ffb1586420ba1dbbb5b2c8c075d Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Tue, 17 Feb 2009 14:51:02 -0800
Subject: [PATCH 055/134] mm: Add min_free_order_shift tunable.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
By default the kernel tries to keep half as much memory free at each
order as it does for one order below. This can be too agressive when
running without swap.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
kernel/sysctl.c | 9 +++++++++
mm/page_alloc.c | 3 ++-
2 files changed, 11 insertions(+), 1 deletions(-)
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -77,6 +77,7 @@ extern int suid_dumpable;
extern char core_pattern[];
extern int pid_max;
extern int min_free_kbytes;
+extern int min_free_order_shift;
extern int pid_max_min, pid_max_max;
extern int sysctl_drop_caches;
extern int percpu_pagelist_fraction;
@@ -1109,6 +1110,14 @@ static struct ctl_table vm_table[] = {
.extra1 = &zero,
},
{
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "min_free_order_shift",
+ .data = &min_free_order_shift,
+ .maxlen = sizeof(min_free_order_shift),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
.ctl_name = VM_PERCPU_PAGELIST_FRACTION,
.procname = "percpu_pagelist_fraction",
.data = &percpu_pagelist_fraction,
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -119,6 +119,7 @@ static char * const zone_names[MAX_NR_ZO
};
int min_free_kbytes = 1024;
+int min_free_order_shift = 1;
unsigned long __meminitdata nr_kernel_pages;
unsigned long __meminitdata nr_all_pages;
@@ -1253,7 +1254,7 @@ int zone_watermark_ok(struct zone *z, in
free_pages -= z->free_area[o].nr_free << o;
/* Require fewer higher order pages to be free */
- min >>= 1;
+ min >>= min_free_order_shift;
if (free_pages <= min)
return 0;

View File

@ -0,0 +1,45 @@
From a4eb204a8029320c2dd748daf4f51fd48d337c3d Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Wed, 18 Mar 2009 17:27:31 -0700
Subject: [PATCH 056/134] mm: Check if any page in a pageblock is reserved before marking it MIGRATE_RESERVE
This fixes a problem where the first pageblock got marked MIGRATE_RESERVE even
though it only had a few free pages. This in turn caused no contiguous memory
to be reserved and frequent kswapd wakeups that emptied the caches to get more
contiguous memory.
---
mm/page_alloc.c | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2545,6 +2545,20 @@ static inline unsigned long wait_table_b
#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
/*
+ * Check if a pageblock contains reserved pages
+ */
+static int pageblock_is_reserved(unsigned long start_pfn)
+{
+ unsigned long end_pfn = start_pfn + pageblock_nr_pages;
+ unsigned long pfn;
+
+ for (pfn = start_pfn; pfn < end_pfn; pfn++)
+ if (PageReserved(pfn_to_page(pfn)))
+ return 1;
+ return 0;
+}
+
+/*
* Mark a number of pageblocks as MIGRATE_RESERVE. The number
* of blocks reserved is based on zone->pages_min. The memory within the
* reserve will tend to store contiguous free pages. Setting min_free_kbytes
@@ -2573,7 +2587,7 @@ static void setup_zone_migrate_reserve(s
continue;
/* Blocks with reserved pages will never free, skip them. */
- if (PageReserved(page))
+ if (pageblock_is_reserved(pfn))
continue;
block_migratetype = get_pageblock_migratetype(page);

View File

@ -0,0 +1,43 @@
From 10276fd993c6e4c92d8086a6ccd0c9e0ff53b053 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Wed, 10 Dec 2008 20:06:28 -0800
Subject: [PATCH 058/134] sched: Enable might_sleep before initializing drivers.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
This allows detection of init bugs in built-in drivers.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
kernel/sched.c | 14 ++++++++++++--
1 files changed, 12 insertions(+), 2 deletions(-)
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -9064,13 +9064,23 @@ void __init sched_init(void)
}
#ifdef CONFIG_DEBUG_SPINLOCK_SLEEP
+static int __might_sleep_init_called;
+int __init __might_sleep_init(void)
+{
+ __might_sleep_init_called = 1;
+ return 0;
+}
+early_initcall(__might_sleep_init);
+
void __might_sleep(char *file, int line)
{
#ifdef in_atomic
static unsigned long prev_jiffy; /* ratelimiting */
- if ((!in_atomic() && !irqs_disabled()) ||
- system_state != SYSTEM_RUNNING || oops_in_progress)
+ if ((!in_atomic() && !irqs_disabled()) || oops_in_progress)
+ return;
+ if (system_state != SYSTEM_RUNNING &&
+ (!__might_sleep_init_called || system_state != SYSTEM_BOOTING))
return;
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
return;

View File

@ -0,0 +1,35 @@
From d52bcb0a807dde87057f330dbe95aa6d221a4b85 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Mon, 9 Apr 2007 17:13:02 +0700
Subject: [PATCH 060/134] [ARM] Add code to prevent system calls from being restarted muliple times before returning from the kernel.
Fixes crashes with thumb syscalls.
---
arch/arm/kernel/signal.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -534,6 +534,14 @@ setup_rt_frame(int usig, struct k_sigact
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 @@ static int do_signal(sigset_t *oldset, s
*/
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;

View File

@ -0,0 +1,50 @@
From 784b170a69906c48a688a9ffa7512fc858f8836c Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Sun, 30 Mar 2008 21:36:29 -0700
Subject: [PATCH 061/134] [ARM] Save thread registers in coredumps
Signed-off-by: Brian Swetland <swetland@google.com>
---
arch/arm/include/asm/elf.h | 6 ++++++
arch/arm/kernel/process.c | 10 ++++++++++
2 files changed, 16 insertions(+), 0 deletions(-)
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -86,6 +86,10 @@ extern char elf_platform[];
struct elf32_hdr;
+struct task_struct;
+
+extern int dump_task_regs (struct task_struct *, elf_gregset_t *);
+
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
@@ -113,4 +117,6 @@ extern int arm_elf_read_implies_exec(con
extern void elf_set_personality(const struct elf32_hdr *);
#define SET_PERSONALITY(ex) elf_set_personality(&(ex))
+#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs)
+
#endif
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -337,6 +337,16 @@ int dump_fpu (struct pt_regs *regs, stru
EXPORT_SYMBOL(dump_fpu);
/*
+ * 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
* the thread function, and r3 points to the exit function.

View File

@ -0,0 +1,104 @@
From e93bd714c424d9fe907cb1d8ec5ce784767a3b16 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Fri, 26 Sep 2008 22:10:56 -0700
Subject: [PATCH 064/134] PM: Add wake lock api.
---
include/linux/wakelock.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 91 insertions(+), 0 deletions(-)
create mode 100755 include/linux/wakelock.h
--- /dev/null
+++ b/include/linux/wakelock.h
@@ -0,0 +1,91 @@
+/* include/linux/wakelock.h
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_WAKELOCK_H
+#define _LINUX_WAKELOCK_H
+
+#include <linux/list.h>
+#include <linux/ktime.h>
+
+/* A wake_lock prevents the system from entering suspend or other low power
+ * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock
+ * prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low power
+ * states that cause large interrupt latencies or that disable a set of
+ * interrupts will not entered from idle until the wake_locks are released.
+ */
+
+enum {
+ WAKE_LOCK_SUSPEND, /* Prevent suspend */
+ WAKE_LOCK_IDLE, /* Prevent low power idle */
+ WAKE_LOCK_TYPE_COUNT
+};
+
+struct wake_lock {
+#ifdef CONFIG_HAS_WAKELOCK
+ struct list_head link;
+ int flags;
+ const char *name;
+ unsigned long expires;
+#ifdef CONFIG_WAKELOCK_STAT
+ struct {
+ int count;
+ int expire_count;
+ int wakeup_count;
+ ktime_t total_time;
+ ktime_t prevent_suspend_time;
+ ktime_t max_time;
+ ktime_t last_time;
+ } stat;
+#endif
+#endif
+};
+
+#ifdef CONFIG_HAS_WAKELOCK
+
+void wake_lock_init(struct wake_lock *lock, int type, const char *name);
+void wake_lock_destroy(struct wake_lock *lock);
+void wake_lock(struct wake_lock *lock);
+void wake_lock_timeout(struct wake_lock *lock, long timeout);
+void wake_unlock(struct wake_lock *lock);
+
+/* wake_lock_active returns a non-zero value if the wake_lock is currently
+ * locked. If the wake_lock has a timeout, it does not check the timeout
+ * but if the timeout had aready been checked it will return 0.
+ */
+int wake_lock_active(struct wake_lock *lock);
+
+/* has_wake_lock returns 0 if no wake locks of the specified type are active,
+ * and non-zero if one or more wake locks are held. Specifically it returns
+ * -1 if one or more wake locks with no timeout are active or the
+ * number of jiffies until all active wake locks time out.
+ */
+long has_wake_lock(int type);
+
+#else
+
+static inline void wake_lock_init(struct wake_lock *lock, int type,
+ const char *name) {}
+static inline void wake_lock_destroy(struct wake_lock *lock) {}
+static inline void wake_lock(struct wake_lock *lock) {}
+static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) {}
+static inline void wake_unlock(struct wake_lock *lock) {}
+
+static inline int wake_lock_active(struct wake_lock *lock) { return 0; }
+static inline long has_wake_lock(int type) { return 0; }
+
+#endif
+
+#endif
+

View File

@ -0,0 +1,69 @@
From 4f76252afe98fd017894e61c296bc61836e67a4a Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Fri, 26 Sep 2008 22:10:56 -0700
Subject: [PATCH 065/134] PM: Add early suspend api.
---
include/linux/earlysuspend.h | 56 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 56 insertions(+), 0 deletions(-)
create mode 100755 include/linux/earlysuspend.h
--- /dev/null
+++ b/include/linux/earlysuspend.h
@@ -0,0 +1,56 @@
+/* include/linux/earlysuspend.h
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_EARLYSUSPEND_H
+#define _LINUX_EARLYSUSPEND_H
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/list.h>
+#endif
+
+/* The early_suspend structure defines suspend and resume hooks to be called
+ * when the user visible sleep state of the system changes, and a level to
+ * control the order. They can be used to turn off the screen and input
+ * devices that are not used for wakeup.
+ * Suspend handlers are called in low to high level order, resume handlers are
+ * called in the opposite order. If, when calling register_early_suspend,
+ * the suspend handlers have already been called without a matching call to the
+ * resume handlers, the suspend handler will be called directly from
+ * register_early_suspend. This direct call can violate the normal level order.
+ */
+enum {
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
+ EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
+ EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
+};
+struct early_suspend {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct list_head link;
+ int level;
+ void (*suspend)(struct early_suspend *h);
+ void (*resume)(struct early_suspend *h);
+#endif
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void register_early_suspend(struct early_suspend *handler);
+void unregister_early_suspend(struct early_suspend *handler);
+#else
+#define register_early_suspend(handler) do { } while (0)
+#define unregister_early_suspend(handler) do { } while (0)
+#endif
+
+#endif
+

View File

@ -0,0 +1,673 @@
From eef7f012d480142a3eccff91e0ae73ecb13220b3 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Tue, 9 Sep 2008 22:14:34 -0700
Subject: [PATCH 066/134] PM: Implement wakelock api.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
PM: wakelock: Replace expire work with a timer
The expire work function did not work in the normal case.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
kernel/power/Kconfig | 19 ++
kernel/power/Makefile | 1 +
kernel/power/power.h | 7 +
kernel/power/wakelock.c | 598 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 625 insertions(+), 0 deletions(-)
create mode 100644 kernel/power/wakelock.c
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -116,6 +116,25 @@ config SUSPEND_FREEZER
Turning OFF this setting is NOT recommended! If in doubt, say Y.
+config HAS_WAKELOCK
+ bool
+
+config WAKELOCK
+ bool "Wake lock"
+ depends on PM && RTC_CLASS
+ default n
+ select HAS_WAKELOCK
+ ---help---
+ Enable wakelocks. When user space request a sleep state the
+ sleep request will be delayed until no wake locks are held.
+
+config WAKELOCK_STAT
+ bool "Wake lock stats"
+ depends on WAKELOCK
+ default y
+ ---help---
+ Report wake lock stats in /proc/wakelocks
+
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -6,6 +6,7 @@ endif
obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
+obj-$(CONFIG_WAKELOCK) += wakelock.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -223,3 +223,10 @@ static inline void suspend_thaw_processe
{
}
#endif
+
+#ifdef CONFIG_WAKELOCK
+/* kernel/power/wakelock.c */
+extern struct workqueue_struct *suspend_work_queue;
+extern struct wake_lock main_wake_lock;
+extern suspend_state_t requested_suspend_state;
+#endif
--- /dev/null
+++ b/kernel/power/wakelock.c
@@ -0,0 +1,598 @@
+/* kernel/power/wakelock.c
+ *
+ * Copyright (C) 2005-2008 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/syscalls.h> /* sys_sync */
+#include <linux/wakelock.h>
+#ifdef CONFIG_WAKELOCK_STAT
+#include <linux/proc_fs.h>
+#endif
+#include "power.h"
+
+enum {
+ DEBUG_EXIT_SUSPEND = 1U << 0,
+ DEBUG_WAKEUP = 1U << 1,
+ DEBUG_SUSPEND = 1U << 2,
+ DEBUG_EXPIRE = 1U << 3,
+ DEBUG_WAKE_LOCK = 1U << 4,
+};
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define WAKE_LOCK_TYPE_MASK (0x0f)
+#define WAKE_LOCK_INITIALIZED (1U << 8)
+#define WAKE_LOCK_ACTIVE (1U << 9)
+#define WAKE_LOCK_AUTO_EXPIRE (1U << 10)
+#define WAKE_LOCK_PREVENTING_SUSPEND (1U << 11)
+
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(inactive_locks);
+static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
+static int current_event_num;
+struct workqueue_struct *suspend_work_queue;
+struct wake_lock main_wake_lock;
+suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
+static struct wake_lock unknown_wakeup;
+
+#ifdef CONFIG_WAKELOCK_STAT
+static struct wake_lock deleted_wake_locks;
+static ktime_t last_sleep_time_update;
+static int wait_for_wakeup;
+
+int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
+{
+ struct timespec ts;
+ struct timespec kt;
+ struct timespec tomono;
+ struct timespec delta;
+ unsigned long seq;
+ long timeout;
+
+ if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
+ return 0;
+ do {
+ seq = read_seqbegin(&xtime_lock);
+ timeout = lock->expires - jiffies;
+ if (timeout > 0)
+ return 0;
+ kt = current_kernel_time();
+ tomono = wall_to_monotonic;
+ } while (read_seqretry(&xtime_lock, seq));
+ jiffies_to_timespec(-timeout, &delta);
+ set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,
+ kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);
+ *expire_time = timespec_to_ktime(ts);
+ return 1;
+}
+
+
+static int print_lock_stat(char *buf, struct wake_lock *lock)
+{
+ int lock_count = lock->stat.count;
+ int expire_count = lock->stat.expire_count;
+ ktime_t active_time = ktime_set(0, 0);
+ ktime_t total_time = lock->stat.total_time;
+ ktime_t max_time = lock->stat.max_time;
+ ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
+ if (lock->flags & WAKE_LOCK_ACTIVE) {
+ ktime_t now, add_time;
+ int expired = get_expired_time(lock, &now);
+ if (!expired)
+ now = ktime_get();
+ add_time = ktime_sub(now, lock->stat.last_time);
+ lock_count++;
+ if (!expired)
+ active_time = add_time;
+ else
+ expire_count++;
+ total_time = ktime_add(total_time, add_time);
+ if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND)
+ prevent_suspend_time = ktime_add(prevent_suspend_time,
+ ktime_sub(now, last_sleep_time_update));
+ if (add_time.tv64 > max_time.tv64)
+ max_time = add_time;
+ }
+
+ return sprintf(buf, "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t"
+ "%lld\n", lock->name, lock_count, expire_count,
+ lock->stat.wakeup_count, ktime_to_ns(active_time),
+ ktime_to_ns(total_time),
+ ktime_to_ns(prevent_suspend_time), ktime_to_ns(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;
+ struct wake_lock *lock;
+ int len = 0;
+ char *p = page;
+ int type;
+
+ spin_lock_irqsave(&list_lock, irqflags);
+
+ p += sprintf(p, "name\tcount\texpire_count\twake_count\tactive_since"
+ "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
+ list_for_each_entry(lock, &inactive_locks, link) {
+ p += print_lock_stat(p, lock);
+ }
+ for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
+ list_for_each_entry(lock, &active_wake_locks[type], link)
+ p += print_lock_stat(p, lock);
+ }
+ spin_unlock_irqrestore(&list_lock, irqflags);
+
+ *start = page + off;
+
+ len = p - page;
+ if (len > off)
+ len -= off;
+ else
+ len = 0;
+
+ return len < count ? len : count;
+}
+
+static void wake_unlock_stat_locked(struct wake_lock *lock, int expired)
+{
+ ktime_t duration;
+ ktime_t now;
+ if (!(lock->flags & WAKE_LOCK_ACTIVE))
+ return;
+ if (get_expired_time(lock, &now))
+ expired = 1;
+ else
+ now = ktime_get();
+ lock->stat.count++;
+ if (expired)
+ lock->stat.expire_count++;
+ duration = ktime_sub(now, 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();
+ if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
+ duration = ktime_sub(now, last_sleep_time_update);
+ lock->stat.prevent_suspend_time = ktime_add(
+ lock->stat.prevent_suspend_time, duration);
+ lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
+ }
+}
+
+static void update_sleep_wait_stats_locked(int done)
+{
+ struct wake_lock *lock;
+ ktime_t now, etime, elapsed, add;
+ int expired;
+
+ now = ktime_get();
+ elapsed = ktime_sub(now, last_sleep_time_update);
+ list_for_each_entry(lock, &active_wake_locks[WAKE_LOCK_SUSPEND], link) {
+ expired = get_expired_time(lock, &etime);
+ if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
+ if (expired)
+ add = ktime_sub(etime, last_sleep_time_update);
+ else
+ add = elapsed;
+ lock->stat.prevent_suspend_time = ktime_add(
+ lock->stat.prevent_suspend_time, add);
+ }
+ if (done || expired)
+ lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
+ else
+ lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
+ }
+ last_sleep_time_update = now;
+}
+#endif
+
+
+static void expire_wake_lock(struct wake_lock *lock)
+{
+#ifdef CONFIG_WAKELOCK_STAT
+ wake_unlock_stat_locked(lock, 1);
+#endif
+ lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
+ list_del(&lock->link);
+ list_add(&lock->link, &inactive_locks);
+ if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
+ pr_info("expired wake lock %s\n", lock->name);
+}
+
+static void print_active_locks(int type)
+{
+ unsigned long irqflags;
+ struct wake_lock *lock;
+
+ BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
+ spin_lock_irqsave(&list_lock, irqflags);
+ list_for_each_entry(lock, &active_wake_locks[type], link) {
+ if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
+ long timeout = lock->expires - jiffies;
+ if (timeout <= 0)
+ pr_info("wake lock %s, expired\n", lock->name);
+ else
+ pr_info("active wake lock %s, time left %ld\n",
+ lock->name, timeout);
+ } else
+ pr_info("active wake lock %s\n", lock->name);
+ }
+ spin_unlock_irqrestore(&list_lock, irqflags);
+}
+
+static long has_wake_lock_locked(int type)
+{
+ struct wake_lock *lock, *n;
+ long max_timeout = 0;
+
+ BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
+ list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
+ if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
+ long timeout = lock->expires - jiffies;
+ if (timeout <= 0)
+ expire_wake_lock(lock);
+ else if (timeout > max_timeout)
+ max_timeout = timeout;
+ } else
+ return -1;
+ }
+ return max_timeout;
+}
+
+long has_wake_lock(int type)
+{
+ long ret;
+ unsigned long irqflags;
+ spin_lock_irqsave(&list_lock, irqflags);
+ ret = has_wake_lock_locked(type);
+ spin_unlock_irqrestore(&list_lock, irqflags);
+ return ret;
+}
+
+static void suspend(struct work_struct *work)
+{
+ int ret;
+ int entry_event_num;
+
+ if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("suspend: abort suspend\n");
+ return;
+ }
+
+ entry_event_num = current_event_num;
+ sys_sync();
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("suspend: enter suspend\n");
+ ret = pm_suspend(requested_suspend_state);
+ if (debug_mask & DEBUG_EXIT_SUSPEND) {
+ struct timespec ts;
+ struct rtc_time tm;
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ pr_info("suspend: exit suspend, ret = %d "
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+ }
+ if (current_event_num == entry_event_num) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("suspend: pm_suspend returned with no event\n");
+ wake_lock_timeout(&unknown_wakeup, HZ / 2);
+ }
+}
+static DECLARE_WORK(suspend_work, suspend);
+
+static void expire_wake_locks(unsigned long data)
+{
+ long has_lock;
+ unsigned long irqflags;
+ if (debug_mask & DEBUG_EXPIRE)
+ pr_info("expire_wake_locks: start\n");
+ if (debug_mask & DEBUG_SUSPEND)
+ print_active_locks(WAKE_LOCK_SUSPEND);
+ spin_lock_irqsave(&list_lock, irqflags);
+ has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
+ if (debug_mask & DEBUG_EXPIRE)
+ pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
+ if (has_lock == 0)
+ queue_work(suspend_work_queue, &suspend_work);
+ spin_unlock_irqrestore(&list_lock, irqflags);
+}
+static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
+
+static int power_suspend_late(struct platform_device *pdev, pm_message_t state)
+{
+ int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
+#ifdef CONFIG_WAKELOCK_STAT
+ wait_for_wakeup = 1;
+#endif
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("power_suspend_late return %d\n", ret);
+ return ret;
+}
+
+static struct platform_driver power_driver = {
+ .driver.name = "power",
+ .suspend_late = power_suspend_late,
+};
+static struct platform_device power_device = {
+ .name = "power",
+};
+
+void wake_lock_init(struct wake_lock *lock, int type, const char *name)
+{
+ unsigned long irqflags = 0;
+
+ if (name)
+ lock->name = name;
+ BUG_ON(!lock->name);
+
+ if (debug_mask & DEBUG_WAKE_LOCK)
+ pr_info("wake_lock_init name=%s\n", lock->name);
+#ifdef CONFIG_WAKELOCK_STAT
+ lock->stat.count = 0;
+ lock->stat.expire_count = 0;
+ lock->stat.wakeup_count = 0;
+ lock->stat.total_time = ktime_set(0, 0);
+ lock->stat.prevent_suspend_time = ktime_set(0, 0);
+ lock->stat.max_time = ktime_set(0, 0);
+ lock->stat.last_time = ktime_set(0, 0);
+#endif
+ lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
+
+ INIT_LIST_HEAD(&lock->link);
+ spin_lock_irqsave(&list_lock, irqflags);
+ list_add(&lock->link, &inactive_locks);
+ spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(wake_lock_init);
+
+void wake_lock_destroy(struct wake_lock *lock)
+{
+ unsigned long irqflags;
+ if (debug_mask & DEBUG_WAKE_LOCK)
+ pr_info("wake_lock_destroy name=%s\n", lock->name);
+ spin_lock_irqsave(&list_lock, irqflags);
+ lock->flags &= ~WAKE_LOCK_INITIALIZED;
+#ifdef CONFIG_WAKELOCK_STAT
+ if (lock->stat.count) {
+ deleted_wake_locks.stat.count += lock->stat.count;
+ deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
+ deleted_wake_locks.stat.total_time =
+ ktime_add(deleted_wake_locks.stat.total_time,
+ lock->stat.total_time);
+ deleted_wake_locks.stat.prevent_suspend_time =
+ ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
+ lock->stat.prevent_suspend_time);
+ deleted_wake_locks.stat.max_time =
+ ktime_add(deleted_wake_locks.stat.max_time,
+ lock->stat.max_time);
+ }
+#endif
+ list_del(&lock->link);
+ spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(wake_lock_destroy);
+
+static void wake_lock_internal(
+ struct wake_lock *lock, long timeout, int has_timeout)
+{
+ int type;
+ unsigned long irqflags;
+ long expire_in;
+
+ spin_lock_irqsave(&list_lock, irqflags);
+ type = lock->flags & WAKE_LOCK_TYPE_MASK;
+ BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
+ BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
+#ifdef CONFIG_WAKELOCK_STAT
+ if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
+ if (debug_mask & DEBUG_WAKEUP)
+ pr_info("wakeup wake lock: %s\n", lock->name);
+ wait_for_wakeup = 0;
+ lock->stat.wakeup_count++;
+ }
+ if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
+ (long)(lock->expires - jiffies) <= 0) {
+ wake_unlock_stat_locked(lock, 0);
+ lock->stat.last_time = ktime_get();
+ }
+#endif
+ if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
+ lock->flags |= WAKE_LOCK_ACTIVE;
+#ifdef CONFIG_WAKELOCK_STAT
+ lock->stat.last_time = ktime_get();
+#endif
+ }
+ list_del(&lock->link);
+ if (has_timeout) {
+ if (debug_mask & DEBUG_WAKE_LOCK)
+ pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
+ lock->name, type, timeout / HZ,
+ (timeout % HZ) * MSEC_PER_SEC / HZ);
+ lock->expires = jiffies + timeout;
+ lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
+ list_add_tail(&lock->link, &active_wake_locks[type]);
+ } else {
+ if (debug_mask & DEBUG_WAKE_LOCK)
+ pr_info("wake_lock: %s, type %d\n", lock->name, type);
+ lock->expires = LONG_MAX;
+ lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
+ list_add(&lock->link, &active_wake_locks[type]);
+ }
+ if (type == WAKE_LOCK_SUSPEND) {
+ current_event_num++;
+#ifdef CONFIG_WAKELOCK_STAT
+ if (lock == &main_wake_lock)
+ update_sleep_wait_stats_locked(1);
+ else if (!wake_lock_active(&main_wake_lock))
+ update_sleep_wait_stats_locked(0);
+#endif
+ if (has_timeout)
+ expire_in = has_wake_lock_locked(type);
+ else
+ expire_in = -1;
+ if (expire_in > 0) {
+ if (debug_mask & DEBUG_EXPIRE)
+ pr_info("wake_lock: %s, start expire timer, "
+ "%ld\n", lock->name, expire_in);
+ mod_timer(&expire_timer, jiffies + expire_in);
+ } else {
+ if (del_timer(&expire_timer))
+ if (debug_mask & DEBUG_EXPIRE)
+ pr_info("wake_lock: %s, stop expire timer\n",
+ lock->name);
+ if (expire_in == 0)
+ queue_work(suspend_work_queue, &suspend_work);
+ }
+ }
+ spin_unlock_irqrestore(&list_lock, irqflags);
+}
+
+void wake_lock(struct wake_lock *lock)
+{
+ wake_lock_internal(lock, 0, 0);
+}
+EXPORT_SYMBOL(wake_lock);
+
+void wake_lock_timeout(struct wake_lock *lock, long timeout)
+{
+ wake_lock_internal(lock, timeout, 1);
+}
+EXPORT_SYMBOL(wake_lock_timeout);
+
+void wake_unlock(struct wake_lock *lock)
+{
+ int type;
+ unsigned long irqflags;
+ spin_lock_irqsave(&list_lock, irqflags);
+ type = lock->flags & WAKE_LOCK_TYPE_MASK;
+#ifdef CONFIG_WAKELOCK_STAT
+ wake_unlock_stat_locked(lock, 0);
+#endif
+ if (debug_mask & DEBUG_WAKE_LOCK)
+ pr_info("wake_unlock: %s\n", lock->name);
+ lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
+ list_del(&lock->link);
+ list_add(&lock->link, &inactive_locks);
+ if (type == WAKE_LOCK_SUSPEND) {
+ long has_lock = has_wake_lock_locked(type);
+ if (has_lock > 0) {
+ if (debug_mask & DEBUG_EXPIRE)
+ pr_info("wake_unlock: %s, start expire timer, "
+ "%ld\n", lock->name, has_lock);
+ mod_timer(&expire_timer, jiffies + has_lock);
+ } else {
+ if (del_timer(&expire_timer))
+ if (debug_mask & DEBUG_EXPIRE)
+ pr_info("wake_unlock: %s, stop expire "
+ "timer\n", lock->name);
+ if (has_lock == 0)
+ queue_work(suspend_work_queue, &suspend_work);
+ }
+ if (lock == &main_wake_lock) {
+ if (debug_mask & DEBUG_SUSPEND)
+ print_active_locks(WAKE_LOCK_SUSPEND);
+#ifdef CONFIG_WAKELOCK_STAT
+ update_sleep_wait_stats_locked(0);
+#endif
+ }
+ }
+ spin_unlock_irqrestore(&list_lock, irqflags);
+}
+EXPORT_SYMBOL(wake_unlock);
+
+int wake_lock_active(struct wake_lock *lock)
+{
+ return !!(lock->flags & WAKE_LOCK_ACTIVE);
+}
+EXPORT_SYMBOL(wake_lock_active);
+
+static int __init wakelocks_init(void)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
+ INIT_LIST_HEAD(&active_wake_locks[i]);
+
+#ifdef CONFIG_WAKELOCK_STAT
+ wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
+ "deleted_wake_locks");
+#endif
+ wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
+ wake_lock(&main_wake_lock);
+ wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
+
+ ret = platform_device_register(&power_device);
+ if (ret) {
+ pr_err("wakelocks_init: platform_device_register failed\n");
+ goto err_platform_device_register;
+ }
+ ret = platform_driver_register(&power_driver);
+ if (ret) {
+ pr_err("wakelocks_init: platform_driver_register failed\n");
+ goto err_platform_driver_register;
+ }
+
+ suspend_work_queue = create_singlethread_workqueue("suspend");
+ if (suspend_work_queue == NULL) {
+ ret = -ENOMEM;
+ goto err_suspend_work_queue;
+ }
+
+#ifdef CONFIG_WAKELOCK_STAT
+ create_proc_read_entry("wakelocks", S_IRUGO, NULL,
+ wakelocks_read_proc, NULL);
+#endif
+
+ return 0;
+
+err_suspend_work_queue:
+ platform_driver_unregister(&power_driver);
+err_platform_driver_register:
+ platform_device_unregister(&power_device);
+err_platform_device_register:
+ wake_lock_destroy(&unknown_wakeup);
+ wake_lock_destroy(&main_wake_lock);
+#ifdef CONFIG_WAKELOCK_STAT
+ wake_lock_destroy(&deleted_wake_locks);
+#endif
+ return ret;
+}
+
+static void __exit wakelocks_exit(void)
+{
+#ifdef CONFIG_WAKELOCK_STAT
+ remove_proc_entry("wakelocks", NULL);
+#endif
+ destroy_workqueue(suspend_work_queue);
+ platform_driver_unregister(&power_driver);
+ platform_device_unregister(&power_device);
+ wake_lock_destroy(&unknown_wakeup);
+ wake_lock_destroy(&main_wake_lock);
+#ifdef CONFIG_WAKELOCK_STAT
+ wake_lock_destroy(&deleted_wake_locks);
+#endif
+}
+
+core_initcall(wakelocks_init);
+module_exit(wakelocks_exit);

View File

@ -0,0 +1,244 @@
From 2d4a25785ad1aa51bad967543e1ab20e9ed4a046 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Tue, 7 Oct 2008 20:48:01 -0700
Subject: [PATCH 067/134] PM: Implement early suspend api
---
kernel/power/Kconfig | 12 +++
kernel/power/Makefile | 1 +
kernel/power/earlysuspend.c | 178 +++++++++++++++++++++++++++++++++++++++++++
kernel/power/power.h | 6 ++
4 files changed, 197 insertions(+), 0 deletions(-)
create mode 100644 kernel/power/earlysuspend.c
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -119,6 +119,9 @@ config SUSPEND_FREEZER
config HAS_WAKELOCK
bool
+config HAS_EARLYSUSPEND
+ bool
+
config WAKELOCK
bool "Wake lock"
depends on PM && RTC_CLASS
@@ -135,6 +138,15 @@ config WAKELOCK_STAT
---help---
Report wake lock stats in /proc/wakelocks
+config EARLYSUSPEND
+ bool "Early suspend"
+ depends on WAKELOCK
+ default y
+ select HAS_EARLYSUSPEND
+ ---help---
+ Call early suspend handlers when the user requested sleep state
+ changes.
+
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_WAKELOCK) += wakelock.o
+obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
--- /dev/null
+++ b/kernel/power/earlysuspend.c
@@ -0,0 +1,178 @@
+/* kernel/power/earlysuspend.c
+ *
+ * Copyright (C) 2005-2008 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 <linux/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h> /* sys_sync */
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+
+#include "power.h"
+
+enum {
+ DEBUG_USER_STATE = 1U << 0,
+ DEBUG_SUSPEND = 1U << 2,
+};
+static int debug_mask = DEBUG_USER_STATE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static DEFINE_MUTEX(early_suspend_lock);
+static LIST_HEAD(early_suspend_handlers);
+static void early_suspend(struct work_struct *work);
+static void late_resume(struct work_struct *work);
+static DECLARE_WORK(early_suspend_work, early_suspend);
+static DECLARE_WORK(late_resume_work, late_resume);
+static DEFINE_SPINLOCK(state_lock);
+enum {
+ SUSPEND_REQUESTED = 0x1,
+ SUSPENDED = 0x2,
+ SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
+};
+static int state;
+
+void register_early_suspend(struct early_suspend *handler)
+{
+ struct list_head *pos;
+
+ mutex_lock(&early_suspend_lock);
+ list_for_each(pos, &early_suspend_handlers) {
+ struct early_suspend *e;
+ e = list_entry(pos, struct early_suspend, link);
+ if (e->level > handler->level)
+ break;
+ }
+ list_add_tail(&handler->link, pos);
+ if ((state & SUSPENDED) && handler->suspend)
+ handler->suspend(handler);
+ mutex_unlock(&early_suspend_lock);
+}
+EXPORT_SYMBOL(register_early_suspend);
+
+void unregister_early_suspend(struct early_suspend *handler)
+{
+ mutex_lock(&early_suspend_lock);
+ list_del(&handler->link);
+ mutex_unlock(&early_suspend_lock);
+}
+EXPORT_SYMBOL(unregister_early_suspend);
+
+static void early_suspend(struct work_struct *work)
+{
+ struct early_suspend *pos;
+ unsigned long irqflags;
+ int abort = 0;
+
+ mutex_lock(&early_suspend_lock);
+ spin_lock_irqsave(&state_lock, irqflags);
+ if (state == SUSPEND_REQUESTED)
+ state |= SUSPENDED;
+ else
+ abort = 1;
+ spin_unlock_irqrestore(&state_lock, irqflags);
+
+ if (abort) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("early_suspend: abort, state %d\n", state);
+ mutex_unlock(&early_suspend_lock);
+ goto abort;
+ }
+
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("early_suspend: call handlers\n");
+ list_for_each_entry(pos, &early_suspend_handlers, link) {
+ if (pos->suspend != NULL)
+ pos->suspend(pos);
+ }
+ mutex_unlock(&early_suspend_lock);
+
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("early_suspend: sync\n");
+
+ sys_sync();
+abort:
+ spin_lock_irqsave(&state_lock, irqflags);
+ if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
+ wake_unlock(&main_wake_lock);
+ spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
+static void late_resume(struct work_struct *work)
+{
+ struct early_suspend *pos;
+ unsigned long irqflags;
+ int abort = 0;
+
+ mutex_lock(&early_suspend_lock);
+ spin_lock_irqsave(&state_lock, irqflags);
+ if (state == SUSPENDED)
+ state &= ~SUSPENDED;
+ else
+ abort = 1;
+ spin_unlock_irqrestore(&state_lock, irqflags);
+
+ if (abort) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("late_resume: abort, state %d\n", state);
+ goto abort;
+ }
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("late_resume: call handlers\n");
+ list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
+ if (pos->resume != NULL)
+ pos->resume(pos);
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("late_resume: done\n");
+abort:
+ mutex_unlock(&early_suspend_lock);
+}
+
+void request_suspend_state(suspend_state_t new_state)
+{
+ unsigned long irqflags;
+ int old_sleep;
+
+ spin_lock_irqsave(&state_lock, irqflags);
+ old_sleep = state & SUSPEND_REQUESTED;
+ if (debug_mask & DEBUG_USER_STATE) {
+ struct timespec ts;
+ struct rtc_time tm;
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ pr_info("request_suspend_state: %s (%d->%d) at %lld "
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
+ new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
+ requested_suspend_state, new_state,
+ ktime_to_ns(ktime_get()),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+ }
+ if (!old_sleep && new_state != PM_SUSPEND_ON) {
+ state |= SUSPEND_REQUESTED;
+ queue_work(suspend_work_queue, &early_suspend_work);
+ } else if (old_sleep && new_state == PM_SUSPEND_ON) {
+ state &= ~SUSPEND_REQUESTED;
+ wake_lock(&main_wake_lock);
+ queue_work(suspend_work_queue, &late_resume_work);
+ }
+ requested_suspend_state = new_state;
+ spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
+suspend_state_t get_suspend_state(void)
+{
+ return requested_suspend_state;
+}
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -230,3 +230,9 @@ extern struct workqueue_struct *suspend_
extern struct wake_lock main_wake_lock;
extern suspend_state_t requested_suspend_state;
#endif
+
+#ifdef CONFIG_EARLYSUSPEND
+/* kernel/power/earlysuspend.c */
+void request_suspend_state(suspend_state_t state);
+suspend_state_t get_suspend_state(void);
+#endif

View File

@ -0,0 +1,52 @@
From 7b236e69bb4403f20fbdef81907d46b4d32d4af8 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Thu, 9 Oct 2008 19:17:11 -0700
Subject: [PATCH 068/134] PM: Enable early suspend through /sys/power/state
If EARLYSUSPEND is enabled then writes to /sys/power/state no longer
blocks, and the kernel will try to enter the requested state every
time no wakelocks are held. Write "on" to resume normal operation.
---
kernel/power/main.c | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -408,6 +408,9 @@ static void suspend_finish(void)
static const char * const pm_states[PM_SUSPEND_MAX] = {
+#ifdef CONFIG_EARLYSUSPEND
+ [PM_SUSPEND_ON] = "on",
+#endif
[PM_SUSPEND_STANDBY] = "standby",
[PM_SUSPEND_MEM] = "mem",
};
@@ -525,7 +528,11 @@ static ssize_t state_store(struct kobjec
const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
+#ifdef CONFIG_EARLYSUSPEND
+ suspend_state_t state = PM_SUSPEND_ON;
+#else
suspend_state_t state = PM_SUSPEND_STANDBY;
+#endif
const char * const *s;
#endif
char *p;
@@ -547,8 +554,15 @@ static ssize_t state_store(struct kobjec
break;
}
if (state < PM_SUSPEND_MAX && *s)
+#ifdef CONFIG_EARLYSUSPEND
+ if (state == PM_SUSPEND_ON || valid_state(state)) {
+ error = 0;
+ request_suspend_state(state);
+ }
+#else
error = enter_state(state);
#endif
+#endif
Exit:
return error ? error : n;

View File

@ -0,0 +1,314 @@
From 48e1af2bdd11204f11b3770a6c8d3eee64aee2e8 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Thu, 9 Oct 2008 21:01:46 -0700
Subject: [PATCH 069/134] PM: Add user-space wake lock api.
This adds /sys/power/wake_lock and /sys/power/wake_unlock.
Writing a string to wake_lock creates a wake lock the
first time is sees a string and locks it. Optionally, the
string can be followed by a timeout.
To unlock the wake lock, write the same string to wake_unlock.
---
kernel/power/Kconfig | 10 ++
kernel/power/Makefile | 1 +
kernel/power/main.c | 9 ++
kernel/power/power.h | 11 ++
kernel/power/userwakelock.c | 218 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 249 insertions(+), 0 deletions(-)
create mode 100644 kernel/power/userwakelock.c
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -138,6 +138,16 @@ config WAKELOCK_STAT
---help---
Report wake lock stats in /proc/wakelocks
+config USER_WAKELOCK
+ bool "Userspace wake locks"
+ depends on WAKELOCK
+ default y
+ ---help---
+ User-space wake lock api. Write "lockname" or "lockname timeout"
+ to /sys/power/wake_lock lock and if needed create a wake lock.
+ Write "lockname" to /sys/power/wake_unlock to unlock a user wake
+ lock.
+
config EARLYSUSPEND
bool "Early suspend"
depends on WAKELOCK
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM) += main.o
obj-$(CONFIG_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_WAKELOCK) += wakelock.o
+obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -595,6 +595,11 @@ pm_trace_store(struct kobject *kobj, str
power_attr(pm_trace);
#endif /* CONFIG_PM_TRACE */
+#ifdef CONFIG_USER_WAKELOCK
+power_attr(wake_lock);
+power_attr(wake_unlock);
+#endif
+
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
@@ -603,6 +608,10 @@ static struct attribute * g[] = {
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
&pm_test_attr.attr,
#endif
+#ifdef CONFIG_USER_WAKELOCK
+ &wake_lock_attr.attr,
+ &wake_unlock_attr.attr,
+#endif
NULL,
};
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -231,6 +231,17 @@ extern struct wake_lock main_wake_lock;
extern suspend_state_t requested_suspend_state;
#endif
+#ifdef CONFIG_USER_WAKELOCK
+ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf);
+ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n);
+ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf);
+ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n);
+#endif
+
#ifdef CONFIG_EARLYSUSPEND
/* kernel/power/earlysuspend.c */
void request_suspend_state(suspend_state_t state);
--- /dev/null
+++ b/kernel/power/userwakelock.c
@@ -0,0 +1,218 @@
+/* kernel/power/userwakelock.c
+ *
+ * Copyright (C) 2005-2008 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 <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/wakelock.h>
+
+#include "power.h"
+
+enum {
+ DEBUG_FAILURE = BIT(0),
+ DEBUG_ERROR = BIT(1),
+ DEBUG_NEW = BIT(2),
+ DEBUG_ACCESS = BIT(3),
+ DEBUG_LOOKUP = BIT(4),
+};
+static int debug_mask = DEBUG_FAILURE;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static DEFINE_MUTEX(tree_lock);
+
+struct user_wake_lock {
+ struct rb_node node;
+ struct wake_lock wake_lock;
+ char name[0];
+};
+struct rb_root user_wake_locks;
+
+static struct user_wake_lock *lookup_wake_lock_name(
+ const char *buf, int allocate, long *timeoutptr)
+{
+ struct rb_node **p = &user_wake_locks.rb_node;
+ struct rb_node *parent = NULL;
+ struct user_wake_lock *l;
+ int diff;
+ u64 timeout;
+ int name_len;
+ const char *arg;
+
+ /* Find length of lock name and start of optional timeout string */
+ arg = buf;
+ while (*arg && !isspace(*arg))
+ arg++;
+ name_len = arg - buf;
+ if (!name_len)
+ goto bad_arg;
+ while (isspace(*arg))
+ arg++;
+
+ /* Process timeout string */
+ if (timeoutptr && *arg) {
+ timeout = simple_strtoull(arg, (char **)&arg, 0);
+ while (isspace(*arg))
+ arg++;
+ if (*arg)
+ goto bad_arg;
+ /* convert timeout from nanoseconds to jiffies > 0 */
+ timeout += (NSEC_PER_SEC / HZ) - 1;
+ do_div(timeout, (NSEC_PER_SEC / HZ));
+ if (timeout <= 0)
+ timeout = 1;
+ *timeoutptr = timeout;
+ } else if (*arg)
+ goto bad_arg;
+ else if (timeoutptr)
+ *timeoutptr = 0;
+
+ /* Lookup wake lock in rbtree */
+ while (*p) {
+ parent = *p;
+ l = rb_entry(parent, struct user_wake_lock, node);
+ diff = strncmp(buf, l->name, name_len);
+ if (!diff && l->name[name_len])
+ diff = -1;
+ if (debug_mask & DEBUG_ERROR)
+ pr_info("lookup_wake_lock_name: compare %.*s %s %d\n",
+ name_len, buf, l->name, diff);
+
+ if (diff < 0)
+ p = &(*p)->rb_left;
+ else if (diff > 0)
+ p = &(*p)->rb_right;
+ else
+ return l;
+ }
+
+ /* Allocate and add new wakelock to rbtree */
+ if (!allocate) {
+ if (debug_mask & DEBUG_ERROR)
+ pr_info("lookup_wake_lock_name: %.*s not found\n",
+ name_len, buf);
+ return ERR_PTR(-EINVAL);
+ }
+ l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
+ if (l == NULL) {
+ if (debug_mask & DEBUG_FAILURE)
+ pr_err("lookup_wake_lock_name: failed to allocate "
+ "memory for %.*s\n", name_len, buf);
+ return ERR_PTR(-ENOMEM);
+ }
+ memcpy(l->name, buf, name_len);
+ if (debug_mask & DEBUG_NEW)
+ pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name);
+ wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
+ rb_link_node(&l->node, parent, p);
+ rb_insert_color(&l->node, &user_wake_locks);
+ return l;
+
+bad_arg:
+ if (debug_mask & DEBUG_ERROR)
+ pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n",
+ name_len, buf, arg);
+ return ERR_PTR(-EINVAL);
+}
+
+ssize_t wake_lock_show(
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ char *s = buf;
+ char *end = buf + PAGE_SIZE;
+ struct rb_node *n;
+ struct user_wake_lock *l;
+
+ mutex_lock(&tree_lock);
+
+ for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
+ l = rb_entry(n, struct user_wake_lock, node);
+ if (wake_lock_active(&l->wake_lock))
+ s += scnprintf(s, end - s, "%s ", l->name);
+ }
+ s += scnprintf(s, end - s, "\n");
+
+ mutex_unlock(&tree_lock);
+ return (s - buf);
+}
+
+ssize_t wake_lock_store(
+ struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ long timeout;
+ struct user_wake_lock *l;
+
+ mutex_lock(&tree_lock);
+ l = lookup_wake_lock_name(buf, 1, &timeout);
+ if (IS_ERR(l)) {
+ n = PTR_ERR(l);
+ goto bad_name;
+ }
+
+ if (debug_mask & DEBUG_ACCESS)
+ pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
+
+ if (timeout)
+ wake_lock_timeout(&l->wake_lock, timeout);
+ else
+ wake_lock(&l->wake_lock);
+bad_name:
+ mutex_unlock(&tree_lock);
+ return n;
+}
+
+
+ssize_t wake_unlock_show(
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ char *s = buf;
+ char *end = buf + PAGE_SIZE;
+ struct rb_node *n;
+ struct user_wake_lock *l;
+
+ mutex_lock(&tree_lock);
+
+ for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
+ l = rb_entry(n, struct user_wake_lock, node);
+ if (!wake_lock_active(&l->wake_lock))
+ s += scnprintf(s, end - s, "%s ", l->name);
+ }
+ s += scnprintf(s, end - s, "\n");
+
+ mutex_unlock(&tree_lock);
+ return (s - buf);
+}
+
+ssize_t wake_unlock_store(
+ struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct user_wake_lock *l;
+
+ mutex_lock(&tree_lock);
+ l = lookup_wake_lock_name(buf, 0, NULL);
+ if (IS_ERR(l)) {
+ n = PTR_ERR(l);
+ goto not_found;
+ }
+
+ if (debug_mask & DEBUG_ACCESS)
+ pr_info("wake_unlock_store: %s\n", l->name);
+
+ wake_unlock(&l->wake_lock);
+not_found:
+ mutex_unlock(&tree_lock);
+ return n;
+}
+

View File

@ -0,0 +1,64 @@
From 3f8e407c418205252f00615da4d0182e8c960344 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Tue, 14 Oct 2008 16:02:39 -0700
Subject: [PATCH 070/134] PM: wakelock: Abort task freezing if a wake lock is held.
Avoids a problem where the device sometimes hangs for 20 seconds
before the screen is turned on.
---
kernel/power/process.c | 23 ++++++++++++++++++-----
1 files changed, 18 insertions(+), 5 deletions(-)
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/freezer.h>
+#include <linux/wakelock.h>
/*
* Timeout for stopping processes
@@ -36,6 +37,7 @@ static int try_to_freeze_tasks(bool sig_
struct timeval start, end;
u64 elapsed_csecs64;
unsigned int elapsed_csecs;
+ unsigned int wakeup = 0;
do_gettimeofday(&start);
@@ -62,6 +64,10 @@ static int try_to_freeze_tasks(bool sig_
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
yield(); /* Yield is okay here */
+ if (todo && has_wake_lock(WAKE_LOCK_SUSPEND)) {
+ wakeup = 1;
+ break;
+ }
if (time_after(jiffies, end_time))
break;
} while (todo);
@@ -77,11 +83,18 @@ static int try_to_freeze_tasks(bool sig_
* and caller must call thaw_processes() if something fails),
* but it cleans up leftover PF_FREEZE requests.
*/
- printk("\n");
- printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
- "(%d tasks refusing to freeze):\n",
- elapsed_csecs / 100, elapsed_csecs % 100, todo);
- show_state();
+ if(wakeup) {
+ printk("\n");
+ printk(KERN_ERR "Freezing of %s aborted\n",
+ sig_only ? "user space " : "tasks ");
+ }
+ else {
+ printk("\n");
+ printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
+ "(%d tasks refusing to freeze):\n",
+ elapsed_csecs / 100, elapsed_csecs % 100, todo);
+ show_state();
+ }
read_lock(&tasklist_lock);
do_each_thread(g, p) {
task_lock(p);

View File

@ -0,0 +1,133 @@
From 0083b2afe89dea4a06bfd967b164318ad0bccd11 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Wed, 15 Oct 2008 17:52:20 -0700
Subject: [PATCH 071/134] PM: earlysuspend: Add console switch when user requested sleep state changes.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
kernel/power/Kconfig | 17 ++++++++
kernel/power/Makefile | 1 +
kernel/power/consoleearlysuspend.c | 78 ++++++++++++++++++++++++++++++++++++
3 files changed, 96 insertions(+), 0 deletions(-)
create mode 100644 kernel/power/consoleearlysuspend.c
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -157,6 +157,23 @@ config EARLYSUSPEND
Call early suspend handlers when the user requested sleep state
changes.
+choice
+ prompt "User-space screen access"
+ default CONSOLE_EARLYSUSPEND
+ depends on HAS_EARLYSUSPEND
+
+ config NO_USER_SPACE_SCREEN_ACCESS_CONTROL
+ bool "None"
+
+ config CONSOLE_EARLYSUSPEND
+ bool "Console switch on early-suspend"
+ depends on HAS_EARLYSUSPEND && VT
+ ---help---
+ Register early suspend handler to perform a console switch to
+ when user-space should stop drawing to the screen and a switch
+ back when it should resume.
+endchoice
+
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_WAKELOCK) += wakelock.o
obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
+obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
--- /dev/null
+++ b/kernel/power/consoleearlysuspend.c
@@ -0,0 +1,78 @@
+/* kernel/power/consoleearlysuspend.c
+ *
+ * Copyright (C) 2005-2008 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 <linux/console.h>
+#include <linux/earlysuspend.h>
+#include <linux/kbd_kern.h>
+#include <linux/module.h>
+#include <linux/vt_kern.h>
+#include <linux/wait.h>
+
+#define EARLY_SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
+
+static int orig_fgconsole;
+static void console_early_suspend(struct early_suspend *h)
+{
+ acquire_console_sem();
+ orig_fgconsole = fg_console;
+ if (vc_allocate(EARLY_SUSPEND_CONSOLE))
+ goto err;
+ if (set_console(EARLY_SUSPEND_CONSOLE))
+ goto err;
+ release_console_sem();
+
+ if (vt_waitactive(EARLY_SUSPEND_CONSOLE))
+ pr_warning("console_early_suspend: Can't switch VCs.\n");
+ return;
+err:
+ pr_warning("console_early_suspend: Can't set console\n");
+ release_console_sem();
+}
+
+static void console_late_resume(struct early_suspend *h)
+{
+ int ret;
+ acquire_console_sem();
+ ret = set_console(orig_fgconsole);
+ release_console_sem();
+ if (ret) {
+ pr_warning("console_late_resume: Can't set console.\n");
+ return;
+ }
+
+ if (vt_waitactive(orig_fgconsole))
+ pr_warning("console_late_resume: Can't switch VCs.\n");
+}
+
+static struct early_suspend console_early_suspend_desc = {
+ .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
+ .suspend = console_early_suspend,
+ .resume = console_late_resume,
+};
+
+static int __init console_early_suspend_init(void)
+{
+ register_early_suspend(&console_early_suspend_desc);
+ return 0;
+}
+
+static void __exit console_early_suspend_exit(void)
+{
+ unregister_early_suspend(&console_early_suspend_desc);
+}
+
+module_init(console_early_suspend_init);
+module_exit(console_early_suspend_exit);
+

View File

@ -0,0 +1,217 @@
From ba801aa66e869f1f9f9d3a1610a2165baa024e4b Mon Sep 17 00:00:00 2001
From: Rebecca Schultz <rschultz@google.com>
Date: Thu, 17 Jul 2008 18:14:55 -0700
Subject: [PATCH 072/134] PM: earlysuspend: Removing dependence on console.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Rather than signaling a full update of the display from userspace via a
console switch, this patch introduces 2 files int /sys/power,
wait_for_fb_sleep and wait_for_fb_wake. Reading these files will block
until the requested state has been entered. When a read from
wait_for_fb_sleep returns userspace should stop drawing. When
wait_for_fb_wake returns, it should do a full update. If either are called
when the fb driver is already in the requested state, they will return
immediately.
Signed-off-by: Rebecca Schultz <rschultz@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
kernel/power/Kconfig | 9 +++
kernel/power/Makefile | 1 +
kernel/power/fbearlysuspend.c | 153 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 163 insertions(+), 0 deletions(-)
create mode 100644 kernel/power/fbearlysuspend.c
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -159,6 +159,7 @@ config EARLYSUSPEND
choice
prompt "User-space screen access"
+ default FB_EARLYSUSPEND if !FRAMEBUFFER_CONSOLE
default CONSOLE_EARLYSUSPEND
depends on HAS_EARLYSUSPEND
@@ -172,6 +173,14 @@ choice
Register early suspend handler to perform a console switch to
when user-space should stop drawing to the screen and a switch
back when it should resume.
+
+ config FB_EARLYSUSPEND
+ bool "Sysfs interface"
+ depends on HAS_EARLYSUSPEND
+ ---help---
+ Register early suspend handler that notifies and waits for
+ user-space through sysfs when user-space should stop drawing
+ to the screen and notifies user-space when it should resume.
endchoice
config HIBERNATION
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_WAKELOCK) += wakelock.o
obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o
+obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
--- /dev/null
+++ b/kernel/power/fbearlysuspend.c
@@ -0,0 +1,153 @@
+/* kernel/power/fbearlysuspend.c
+ *
+ * Copyright (C) 2005-2008 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 <linux/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+
+#include "power.h"
+
+static wait_queue_head_t fb_state_wq;
+static DEFINE_SPINLOCK(fb_state_lock);
+static enum {
+ FB_STATE_STOPPED_DRAWING,
+ FB_STATE_REQUEST_STOP_DRAWING,
+ FB_STATE_DRAWING_OK,
+} fb_state;
+
+/* tell userspace to stop drawing, wait for it to stop */
+static void stop_drawing_early_suspend(struct early_suspend *h)
+{
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
+ fb_state = FB_STATE_REQUEST_STOP_DRAWING;
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+
+ wake_up_all(&fb_state_wq);
+ ret = wait_event_timeout(fb_state_wq,
+ fb_state == FB_STATE_STOPPED_DRAWING,
+ HZ);
+ if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING))
+ pr_warning("stop_drawing_early_suspend: timeout waiting for "
+ "userspace to stop drawing\n");
+}
+
+/* tell userspace to start drawing */
+static void start_drawing_late_resume(struct early_suspend *h)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
+ fb_state = FB_STATE_DRAWING_OK;
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+ wake_up(&fb_state_wq);
+}
+
+static struct early_suspend stop_drawing_early_suspend_desc = {
+ .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
+ .suspend = stop_drawing_early_suspend,
+ .resume = start_drawing_late_resume,
+};
+
+static ssize_t wait_for_fb_sleep_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char *s = buf;
+ int ret;
+
+ ret = wait_event_interruptible(fb_state_wq,
+ fb_state != FB_STATE_DRAWING_OK);
+ if (ret && fb_state == FB_STATE_DRAWING_OK)
+ return ret;
+ else
+ s += sprintf(buf, "sleeping");
+ return s - buf;
+}
+
+static ssize_t wait_for_fb_wake_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char *s = buf;
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
+ if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) {
+ fb_state = FB_STATE_STOPPED_DRAWING;
+ wake_up(&fb_state_wq);
+ }
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
+
+ ret = wait_event_interruptible(fb_state_wq,
+ fb_state == FB_STATE_DRAWING_OK);
+ if (ret && fb_state != FB_STATE_DRAWING_OK)
+ return ret;
+ else
+ s += sprintf(buf, "awake");
+
+ return s - buf;
+}
+
+#define power_ro_attr(_name) \
+static struct kobj_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0444, \
+ }, \
+ .show = _name##_show, \
+ .store = NULL, \
+}
+
+power_ro_attr(wait_for_fb_sleep);
+power_ro_attr(wait_for_fb_wake);
+
+static struct attribute *g[] = {
+ &wait_for_fb_sleep_attr.attr,
+ &wait_for_fb_wake_attr.attr,
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = g,
+};
+
+static int __init android_power_init(void)
+{
+ int ret;
+
+ init_waitqueue_head(&fb_state_wq);
+ fb_state = FB_STATE_DRAWING_OK;
+
+ ret = sysfs_create_group(power_kobj, &attr_group);
+ if (ret) {
+ pr_err("android_power_init: sysfs_create_group failed\n");
+ return ret;
+ }
+
+ register_early_suspend(&stop_drawing_early_suspend_desc);
+ return 0;
+}
+
+static void __exit android_power_exit(void)
+{
+ unregister_early_suspend(&stop_drawing_early_suspend_desc);
+ sysfs_remove_group(power_kobj, &attr_group);
+}
+
+module_init(android_power_init);
+module_exit(android_power_exit);
+

View File

@ -0,0 +1,132 @@
From 2df5996350da51bcdee798c00ae1f2415fd9ad20 Mon Sep 17 00:00:00 2001
From: Robert Love <rlove@google.com>
Date: Mon, 12 May 2008 17:08:29 -0400
Subject: [PATCH 081/134] net: socket ioctl to reset connections matching local address
Introduce a new socket ioctl, SIOCKILLADDR, that nukes all sockets
bound to the same local address. This is useful in situations with
dynamic IPs, to kill stuck connections.
Signed-off-by: Brian Swetland <swetland@google.com>
net: fix tcp_v4_nuke_addr
Signed-off-by: Dima Zavin <dima@android.com>
---
include/linux/sockios.h | 1 +
include/net/tcp.h | 2 ++
net/ipv4/af_inet.c | 1 +
net/ipv4/devinet.c | 9 ++++++++-
net/ipv4/tcp_ipv4.c | 31 +++++++++++++++++++++++++++++++
5 files changed, 43 insertions(+), 1 deletions(-)
--- a/include/linux/sockios.h
+++ b/include/linux/sockios.h
@@ -65,6 +65,7 @@
#define SIOCDIFADDR 0x8936 /* delete PA address */
#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */
#define SIOCGIFCOUNT 0x8938 /* get number of devices */
+#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */
#define SIOCGIFBR 0x8940 /* Bridging support */
#define SIOCSIFBR 0x8941 /* Set bridging options */
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1381,6 +1381,8 @@ extern struct sk_buff **tcp4_gro_receive
extern int tcp_gro_complete(struct sk_buff *skb);
extern int tcp4_gro_complete(struct sk_buff *skb);
+extern void tcp_v4_nuke_addr(__u32 saddr);
+
#ifdef CONFIG_PROC_FS
extern int tcp4_proc_init(void);
extern void tcp4_proc_exit(void);
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -825,6 +825,7 @@ int inet_ioctl(struct socket *sock, unsi
case SIOCSIFPFLAGS:
case SIOCGIFPFLAGS:
case SIOCSIFFLAGS:
+ case SIOCKILLADDR:
err = devinet_ioctl(net, cmd, (void __user *)arg);
break;
default:
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -57,6 +57,7 @@
#include <net/arp.h>
#include <net/ip.h>
+#include <net/tcp.h>
#include <net/route.h>
#include <net/ip_fib.h>
#include <net/rtnetlink.h>
@@ -631,6 +632,7 @@ int devinet_ioctl(struct net *net, unsig
case SIOCSIFBRDADDR: /* Set the broadcast address */
case SIOCSIFDSTADDR: /* Set the destination address */
case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ case SIOCKILLADDR: /* Nuke all sockets on this address */
ret = -EACCES;
if (!capable(CAP_NET_ADMIN))
goto out;
@@ -680,7 +682,8 @@ int devinet_ioctl(struct net *net, unsig
}
ret = -EADDRNOTAVAIL;
- if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
+ if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS
+ && cmd != SIOCKILLADDR)
goto done;
switch (cmd) {
@@ -804,6 +807,10 @@ int devinet_ioctl(struct net *net, unsig
inet_insert_ifa(ifa);
}
break;
+ case SIOCKILLADDR: /* Nuke all connections on this address */
+ ret = 0;
+ tcp_v4_nuke_addr(sin->sin_addr.s_addr);
+ break;
}
done:
rtnl_unlock();
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1845,6 +1845,37 @@ void tcp_v4_destroy_sock(struct sock *sk
EXPORT_SYMBOL(tcp_v4_destroy_sock);
+/*
+ * tcp_v4_nuke_addr - destroy all sockets on the given local address
+ */
+void tcp_v4_nuke_addr(__u32 saddr)
+{
+ unsigned int bucket;
+
+ for (bucket = 0; bucket < tcp_hashinfo.ehash_size; bucket++) {
+ struct hlist_nulls_node *node;
+ struct sock *sk;
+ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket);
+
+ spin_lock_bh(lock);
+ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
+ struct inet_sock *inet = inet_sk(sk);
+
+ if (inet->rcv_saddr != saddr)
+ continue;
+ if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
+ continue;
+ if (sock_flag(sk, SOCK_DEAD))
+ continue;
+
+ sk->sk_err = ETIMEDOUT;
+ sk->sk_error_report(sk);
+ tcp_done(sk);
+ }
+ spin_unlock_bh(lock);
+ }
+}
+
#ifdef CONFIG_PROC_FS
/* Proc filesystem TCP sock list dumping. */

View File

@ -0,0 +1,128 @@
From 75090d40ffbd9b4863238e1d62b43f8598da5e5e Mon Sep 17 00:00:00 2001
From: Robert Love <rlove@google.com>
Date: Thu, 31 Jul 2008 11:12:44 -0400
Subject: [PATCH 082/134] sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size
Add a family of knobs to /sys/kernel/ipv4 for controlling the TCP window size:
tcp_wmem_min
tcp_wmem_def
tcp_wmem_max
tcp_rmem_min
tcp_rmem_def
tcp_rmem_max
This six values mirror the sysctl knobs in /proc/sys/net/ipv4/tcp_wmem and
/proc/sys/net/ipv4/tcp_rmem.
Sysfs, unlike sysctl, allows us to set and manage the files' permissions and
owners.
Signed-off-by: Robert Love <rlove@google.com>
---
net/ipv4/Makefile | 1 +
net/ipv4/sysfs_net_ipv4.c | 88 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 0 deletions(-)
create mode 100644 net/ipv4/sysfs_net_ipv4.c
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -14,6 +14,7 @@ obj-y := route.o inetpeer.o protocol
inet_fragment.o
obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
+obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o
obj-$(CONFIG_IP_FIB_HASH) += fib_hash.o
obj-$(CONFIG_IP_FIB_TRIE) += fib_trie.o
obj-$(CONFIG_PROC_FS) += proc.o
--- /dev/null
+++ b/net/ipv4/sysfs_net_ipv4.c
@@ -0,0 +1,88 @@
+/*
+ * net/ipv4/sysfs_net_ipv4.c
+ *
+ * sysfs-based networking knobs (so we can, unlike with sysctl, control perms)
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Robert Love <rlove@google.com>
+ *
+ * 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 <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/init.h>
+#include <net/tcp.h>
+
+#define CREATE_IPV4_FILE(_name, _var) \
+static ssize_t _name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%d\n", _var); \
+} \
+static ssize_t _name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int val, ret; \
+ ret = sscanf(buf, "%d", &val); \
+ if (ret != 1) \
+ return -EINVAL; \
+ if (val < 0) \
+ return -EINVAL; \
+ _var = val; \
+ return count; \
+} \
+static struct kobj_attribute _name##_attr = \
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]);
+CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]);
+CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]);
+
+CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]);
+CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]);
+CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]);
+
+static struct attribute *ipv4_attrs[] = {
+ &tcp_wmem_min_attr.attr,
+ &tcp_wmem_def_attr.attr,
+ &tcp_wmem_max_attr.attr,
+ &tcp_rmem_min_attr.attr,
+ &tcp_rmem_def_attr.attr,
+ &tcp_rmem_max_attr.attr,
+ NULL
+};
+
+static struct attribute_group ipv4_attr_group = {
+ .attrs = ipv4_attrs,
+};
+
+static __init int sysfs_ipv4_init(void)
+{
+ struct kobject *ipv4_kobject;
+ int ret;
+
+ ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj);
+ if (!ipv4_kobject)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group);
+ if (ret) {
+ kobject_put(ipv4_kobject);
+ return ret;
+ }
+
+ return 0;
+}
+
+subsys_initcall(sysfs_ipv4_init);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
From 0d1077627288e51b5604f270eae6cf9f5ccd3871 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Fri, 17 Oct 2008 15:20:55 -0700
Subject: [PATCH 087/134] Input: Hold wake lock while event queue is not empty.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Allows userspace code to process input events while
the device appears to be asleep.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/input/evdev.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -19,6 +19,7 @@
#include <linux/input.h>
#include <linux/major.h>
#include <linux/device.h>
+#include <linux/wakelock.h>
#include "input-compat.h"
struct evdev {
@@ -43,6 +44,7 @@ struct evdev_client {
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
+ struct wake_lock wake_lock;
};
static struct evdev *evdev_table[EVDEV_MINORS];
@@ -55,6 +57,7 @@ static void evdev_pass_event(struct evde
* Interrupts are disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
+ wake_lock_timeout(&client->wake_lock, 5 * HZ);
client->buffer[client->head++] = *event;
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
@@ -233,6 +236,7 @@ static int evdev_release(struct inode *i
mutex_unlock(&evdev->mutex);
evdev_detach_client(evdev, client);
+ wake_lock_destroy(&client->wake_lock);
kfree(client);
evdev_close_device(evdev);
@@ -269,6 +273,7 @@ static int evdev_open(struct inode *inod
}
spin_lock_init(&client->buffer_lock);
+ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, "evdev");
client->evdev = evdev;
evdev_attach_client(evdev, client);
@@ -332,6 +337,8 @@ static int evdev_fetch_next_event(struct
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= EVDEV_BUFFER_SIZE - 1;
+ if (client->head == client->tail)
+ wake_unlock(&client->wake_lock);
}
spin_unlock_irq(&client->buffer_lock);

View File

@ -0,0 +1,31 @@
From 474db0e1425f8da03a7d49ebba1602efa503c73f Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Fri, 17 Oct 2008 15:28:08 -0700
Subject: [PATCH 088/134] Input: Use monotonic time for event time stamps.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Since wall time can jump backwards, it cannot be used to determine if one
event occured before another or for how long a key was pressed.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/input/evdev.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -74,8 +74,11 @@ static void evdev_event(struct input_han
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
+ struct timespec ts;
- do_gettimeofday(&event.time);
+ ktime_get_ts(&ts);
+ event.time.tv_sec = ts.tv_sec;
+ event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;

View File

@ -0,0 +1,313 @@
From eb92150c047e76960ecf8989b9037fd34b43a4af Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Fri, 21 Nov 2008 21:47:23 -0800
Subject: [PATCH 090/134] input: Add keyreset driver.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Add a platform device in the board file to specify a reset key-combo.
The first time the key-combo is detected a work function that syncs
the filesystems is scheduled. If all the keys are released and then
pressed again, it calls panic. Reboot on panic should be set for
this to work.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/input/Kconfig | 9 ++
drivers/input/Makefile | 1 +
drivers/input/keyreset.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/keyreset.h | 27 ++++++
4 files changed, 266 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/keyreset.c
create mode 100644 include/linux/keyreset.h
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -149,6 +149,15 @@ config INPUT_APMPOWER
To compile this driver as a module, choose M here: the
module will be called apm-power.
+config INPUT_KEYRESET
+ tristate "Reset key"
+ depends on INPUT
+ ---help---
+ Say Y here if you want to reboot when some keys are pressed;
+
+ To compile this driver as a module, choose M here: the
+ module will be called keyreset.
+
config XEN_KBDDEV_FRONTEND
tristate "Xen virtual keyboard and mouse support"
depends on XEN_FBDEV_FRONTEND
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -23,5 +23,6 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touch
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
--- /dev/null
+++ b/drivers/input/keyreset.c
@@ -0,0 +1,229 @@
+/* drivers/input/keyreset.c
+ *
+ * Copyright (C) 2008 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 <linux/input.h>
+#include <linux/keyreset.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+
+struct keyreset_state {
+ struct input_handler input_handler;
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long key[BITS_TO_LONGS(KEY_CNT)];
+ spinlock_t lock;
+ int key_down_target;
+ int key_down;
+ int key_up;
+ int restart_disabled;
+};
+
+int restart_requested;
+static void deferred_restart(struct work_struct *dummy)
+{
+ restart_requested = 2;
+ sys_sync();
+ restart_requested = 3;
+ kernel_restart(NULL);
+}
+static DECLARE_WORK(restart_work, deferred_restart);
+
+static void keyreset_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ unsigned long flags;
+ struct keyreset_state *state = handle->private;
+
+ if (type != EV_KEY)
+ return;
+
+ if (code >= KEY_MAX)
+ return;
+
+ if (!test_bit(code, state->keybit))
+ return;
+
+ spin_lock_irqsave(&state->lock, flags);
+ if (!test_bit(code, state->key) == !value)
+ goto done;
+ __change_bit(code, state->key);
+ if (test_bit(code, state->upbit)) {
+ if (value) {
+ state->restart_disabled = 1;
+ state->key_up++;
+ } else
+ state->key_up--;
+ } else {
+ if (value)
+ state->key_down++;
+ else
+ state->key_down--;
+ }
+ if (state->key_down == 0 && state->key_up == 0)
+ state->restart_disabled = 0;
+
+ pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
+ state->key_down, state->key_up, state->restart_disabled);
+
+ if (value && !state->restart_disabled &&
+ state->key_down == state->key_down_target) {
+ state->restart_disabled = 1;
+ if (restart_requested)
+ panic("keyboard reset failed, %d", restart_requested);
+ pr_info("keyboard reset\n");
+ schedule_work(&restart_work);
+ restart_requested = 1;
+ }
+done:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static int keyreset_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i;
+ int ret;
+ struct input_handle *handle;
+ struct keyreset_state *state =
+ container_of(handler, struct keyreset_state, input_handler);
+
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "keyreset";
+ handle->private = state;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("using input dev %s for key reset\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keyreset_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id keyreset_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(input, keyreset_ids);
+
+static int keyreset_probe(struct platform_device *pdev)
+{
+ int ret;
+ int key, *keyp;
+ struct keyreset_state *state;
+ struct keyreset_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ spin_lock_init(&state->lock);
+ keyp = pdata->keys_down;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ state->key_down_target++;
+ __set_bit(key, state->keybit);
+ }
+ if (pdata->keys_up) {
+ keyp = pdata->keys_up;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ __set_bit(key, state->keybit);
+ __set_bit(key, state->upbit);
+ }
+ }
+ state->input_handler.event = keyreset_event;
+ state->input_handler.connect = keyreset_connect;
+ state->input_handler.disconnect = keyreset_disconnect;
+ state->input_handler.name = KEYRESET_NAME;
+ state->input_handler.id_table = keyreset_ids;
+ ret = input_register_handler(&state->input_handler);
+ if (ret) {
+ kfree(state);
+ return ret;
+ }
+ platform_set_drvdata(pdev, state);
+ return 0;
+}
+
+int keyreset_remove(struct platform_device *pdev)
+{
+ struct keyreset_state *state = platform_get_drvdata(pdev);
+ input_unregister_handler(&state->input_handler);
+ kfree(state);
+ return 0;
+}
+
+
+struct platform_driver keyreset_driver = {
+ .driver.name = KEYRESET_NAME,
+ .probe = keyreset_probe,
+ .remove = keyreset_remove,
+};
+
+static int __init keyreset_init(void)
+{
+ return platform_driver_register(&keyreset_driver);
+}
+
+static void __exit keyreset_exit(void)
+{
+ return platform_driver_unregister(&keyreset_driver);
+}
+
+module_init(keyreset_init);
+module_exit(keyreset_exit);
--- /dev/null
+++ b/include/linux/keyreset.h
@@ -0,0 +1,27 @@
+/*
+ * include/linux/keyreset.h - platform data structure for resetkeys driver
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_KEYRESET_H
+#define _LINUX_KEYRESET_H
+
+#define KEYRESET_NAME "keyreset"
+
+struct keyreset_platform_data {
+ int *keys_up;
+ int keys_down[]; /* 0 terminated */
+};
+
+#endif /* _LINUX_KEYRESET_H */

View File

@ -0,0 +1,21 @@
From 2cae40a6a76e9e440f146ce0402aed40a7c127ca Mon Sep 17 00:00:00 2001
From: San Mehat <san@google.com>
Date: Mon, 10 Nov 2008 16:29:50 -0800
Subject: [PATCH 093/134] mmc: Add status IRQ and status callback function to mmc platform data
Signed-off-by: San Mehat <san@google.com>
---
arch/arm/include/asm/mach/mmc.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -10,6 +10,8 @@ struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
u32 (*translate_vdd)(struct device *, unsigned int);
unsigned int (*status)(struct device *);
+ unsigned int status_irq;
+ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
};
#endif

View File

@ -0,0 +1,69 @@
From 66983857a54479d76ce6dbd8399e1276698bd3c5 Mon Sep 17 00:00:00 2001
From: San Mehat <san@google.com>
Date: Tue, 11 Nov 2008 09:35:36 -0800
Subject: [PATCH 094/134] mmc: sd: Add new CONFIG_MMC_PARANOID_SD_INIT for enabling retries during SD detection
Signed-off-by: San Mehat <san@google.com>
---
drivers/mmc/core/Kconfig | 8 ++++++++
drivers/mmc/core/sd.c | 24 ++++++++++++++++++++++--
2 files changed, 30 insertions(+), 2 deletions(-)
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,3 +14,11 @@ config MMC_UNSAFE_RESUME
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
+config MMC_PARANOID_SD_INIT
+ bool "Enable paranoid SD card initialization (EXPERIMENTAL)"
+ help
+ If you say Y here, the MMC layer will be extra paranoid
+ about re-trying SD init requests. This can be a useful
+ work-around for buggy controllers and hardware. Enable
+ if you are experiencing issues with SD detection.
+
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -336,7 +336,9 @@ static int mmc_sd_init_card(struct mmc_h
int err;
u32 cid[4];
unsigned int max_dtr;
-
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -439,11 +441,29 @@ static int mmc_sd_init_card(struct mmc_h
err = mmc_decode_scr(card);
if (err < 0)
goto free_card;
-
/*
* Fetch switch information from card.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ for (retries = 1; retries <= 3; retries++) {
+ err = mmc_read_switch(card);
+ if (!err) {
+ if (retries > 1) {
+ printk(KERN_WARNING
+ "%s: recovered\n",
+ mmc_hostname(host));
+ }
+ break;
+ } else {
+ printk(KERN_WARNING
+ "%s: read switch failed (attempt %d)\n",
+ mmc_hostname(host), retries);
+ }
+ }
+#else
err = mmc_read_switch(card);
+#endif
+
if (err)
goto free_card;
}

View File

@ -0,0 +1,274 @@
From 62d51f0d0d0b3c758b4505c3855b61288b8e90aa Mon Sep 17 00:00:00 2001
From: San Mehat <san@android.com>
Date: Mon, 14 Apr 2008 15:22:49 -0700
Subject: [PATCH 095/134] mmc: Add concept of an 'embedded' SDIO device.
This is required to support chips which use SDIO for signaling/
communication but do not implement the various card enumeration registers
as required for full SD / SDIO cards.
mmc: sdio: Fix bug where we're freeing the CIS tables we never allocated when using EMBEDDED_SDIO
mmc: Add max_blksize to embedded SDIO data
Signed-off-by: San Mehat <san@google.com>
---
arch/arm/include/asm/mach/mmc.h | 10 ++++++
drivers/mmc/core/Kconfig | 10 +++++-
drivers/mmc/core/core.c | 16 +++++++++
drivers/mmc/core/sdio.c | 67 ++++++++++++++++++++++++++++++++-------
drivers/mmc/core/sdio_bus.c | 13 +++++++-
include/linux/mmc/host.h | 17 ++++++++++
include/linux/mmc/sdio_func.h | 8 +++++
7 files changed, 127 insertions(+), 14 deletions(-)
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -5,12 +5,22 @@
#define ASMARM_MACH_MMC_H
#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+struct embedded_sdio_data {
+ struct sdio_cis cis;
+ struct sdio_cccr cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+};
struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
u32 (*translate_vdd)(struct device *, unsigned int);
unsigned int (*status)(struct device *);
unsigned int status_irq;
+ struct embedded_sdio_data *embedded_sdio;
int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
};
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,11 +14,19 @@ config MMC_UNSAFE_RESUME
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
+config MMC_EMBEDDED_SDIO
+ boolean "MMC embedded SDIO device support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ If you say Y here, support will be added for embedded SDIO
+ devices which do not contain the necessary enumeration
+ support in hardware to be properly detected.
+
config MMC_PARANOID_SD_INIT
bool "Enable paranoid SD card initialization (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
help
If you say Y here, the MMC layer will be extra paranoid
about re-trying SD init requests. This can be a useful
work-around for buggy controllers and hardware. Enable
if you are experiencing issues with SD detection.
-
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1011,6 +1011,22 @@ EXPORT_SYMBOL(mmc_resume_host);
#endif
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs)
+{
+ host->embedded_sdio_data.cis = cis;
+ host->embedded_sdio_data.cccr = cccr;
+ host->embedded_sdio_data.funcs = funcs;
+ host->embedded_sdio_data.num_funcs = num_funcs;
+}
+
+EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
+#endif
+
static int __init mmc_init(void)
{
int ret;
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -24,6 +24,10 @@
#include "sdio_ops.h"
#include "sdio_cis.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/sdio_ids.h>
+#endif
+
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
@@ -314,6 +318,11 @@ int mmc_attach_sdio(struct mmc_host *hos
*/
funcs = (ocr & 0x70000000) >> 28;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs)
+ funcs = host->embedded_sdio_data.num_funcs;
+#endif
+
/*
* Allocate card structure.
*/
@@ -351,17 +360,33 @@ int mmc_attach_sdio(struct mmc_host *hos
/*
* Read the common registers.
*/
- err = sdio_read_cccr(card);
- if (err)
- goto remove;
- /*
- * Read the common CIS tuples.
- */
- err = sdio_read_common_cis(card);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cccr)
+ memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr));
+ else {
+#endif
+ err = sdio_read_cccr(card);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cis)
+ memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis));
+ else {
+#endif
+ /*
+ * Read the common CIS tuples.
+ */
+ err = sdio_read_common_cis(card);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
/*
* Switch to high-speed (if supported).
*/
@@ -395,9 +420,27 @@ int mmc_attach_sdio(struct mmc_host *hos
* Initialize (but don't add) all present functions.
*/
for (i = 0;i < funcs;i++) {
- err = sdio_init_func(host->card, i + 1);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs) {
+ struct sdio_func *tmp;
+
+ tmp = sdio_alloc_func(host->card);
+ if (IS_ERR(tmp))
+ goto remove;
+ tmp->num = (i + 1);
+ card->sdio_func[i] = tmp;
+ tmp->class = host->embedded_sdio_data.funcs[i].f_class;
+ tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize;
+ tmp->vendor = card->cis.vendor;
+ tmp->device = card->cis.device;
+ } else {
+#endif
+ err = sdio_init_func(host->card, i + 1);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
}
mmc_release_host(host);
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -20,6 +20,10 @@
#include "sdio_cis.h"
#include "sdio_bus.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/host.h>
+#endif
+
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
@@ -202,7 +206,14 @@ static void sdio_release_func(struct dev
{
struct sdio_func *func = dev_to_sdio_func(dev);
- sdio_free_func_cis(func);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ /*
+ * If this device is embedded then we never allocated
+ * cis tables for this func
+ */
+ if (!func->card->host->embedded_sdio_data.funcs)
+#endif
+ sdio_free_func_cis(func);
if (func->info)
kfree(func->info);
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -161,6 +161,15 @@ struct mmc_host {
struct dentry *debugfs_root;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ struct {
+ struct sdio_cis *cis;
+ struct sdio_cccr *cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+ } embedded_sdio_data;
+#endif
+
unsigned long private[0] ____cacheline_aligned;
};
@@ -169,6 +178,14 @@ extern int mmc_add_host(struct mmc_host
extern void mmc_remove_host(struct mmc_host *);
extern void mmc_free_host(struct mmc_host *);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+extern void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs);
+#endif
+
static inline void *mmc_priv(struct mmc_host *host)
{
return (void *)host->private;
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -21,6 +21,14 @@ struct sdio_func;
typedef void (sdio_irq_handler_t)(struct sdio_func *);
/*
+ * Structure used to hold embedded SDIO device data from platform layer
+ */
+struct sdio_embedded_func {
+ uint8_t f_class;
+ uint32_t f_maxblksize;
+};
+
+/*
* SDIO function CIS tuple (unknown to the core)
*/
struct sdio_func_tuple {

View File

@ -0,0 +1,74 @@
From 0b9879757f15d4ac99e032647cb3ee26393031b0 Mon Sep 17 00:00:00 2001
From: San Mehat <san@android.com>
Date: Thu, 15 May 2008 09:15:37 -0700
Subject: [PATCH 097/134] mmc: Add new API call 'sdio_reset_comm' for resetting communication with an SDIO device
Signed-off-by: San Mehat <san@android.com>
---
drivers/mmc/core/sdio.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 58 insertions(+), 0 deletions(-)
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -482,3 +482,61 @@ err:
return err;
}
+int sdio_reset_comm(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ u32 ocr;
+ int err;
+
+ printk("%s():\n", __func__);
+ mmc_go_idle(host);
+
+ mmc_set_clock(host, host->f_min);
+
+ err = mmc_send_io_op_cond(host, 0, &ocr);
+ if (err)
+ goto err;
+
+ host->ocr = mmc_select_voltage(host, ocr);
+ if (!host->ocr) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+ if (err)
+ goto err;
+
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ goto err;
+ }
+
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ goto err;
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto err;
+ }
+
+ mmc_set_clock(host, card->cis.max_dtr);
+ err = sdio_enable_wide(card);
+ if (err)
+ goto err;
+
+ return 0;
+ err:
+ printk("%s: Error resetting SDIO communications (%d)\n",
+ mmc_hostname(host), err);
+ mmc_release_host(host);
+ return err;
+}
+EXPORT_SYMBOL(sdio_reset_comm);
+
+

View File

@ -0,0 +1,44 @@
From bd8df907ee8e95143d1bb7ec01ee0e4b6f7e1b79 Mon Sep 17 00:00:00 2001
From: San Mehat <san@android.com>
Date: Mon, 1 Dec 2008 08:52:34 -0800
Subject: [PATCH 098/134] mmc: sd: When resuming, try a little harder to init the card
Signed-off-by: San Mehat <san@android.com>
---
drivers/mmc/core/sd.c | 19 +++++++++++++++++++
1 files changed, 19 insertions(+), 0 deletions(-)
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -607,12 +607,31 @@ static void mmc_sd_suspend(struct mmc_ho
static void mmc_sd_resume(struct mmc_host *host)
{
int err;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ retries = 5;
+ while (retries) {
+ err = mmc_sd_init_card(host, host->ocr, host->card);
+
+ if (err) {
+ printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
+ mmc_hostname(host), err, retries);
+ mdelay(5);
+ retries--;
+ continue;
+ }
+ break;
+ }
+#else
err = mmc_sd_init_card(host, host->ocr, host->card);
+#endif
mmc_release_host(host);
if (err) {

View File

@ -0,0 +1,104 @@
From 5ca694dfd981a371e9b18cdd4a89c002ffaccbc5 Mon Sep 17 00:00:00 2001
From: San Mehat <san@android.com>
Date: Wed, 3 Dec 2008 10:22:59 -0800
Subject: [PATCH 099/134] mmc: mmcblk: Add new feature 'CONFIG_MMC_BLOCK_PARANOID_RESUME'
With this feature enabled, mmcblk will check the card-status before issuing
a transaction *only* after being resumed. This protectes us from issuing
transactions before the sdcard is ready (which can occur if the host driver
deferrs mmc_resume_host() to reduce resume latency)
Signed-off-by: San Mehat <san@android.com>
---
drivers/mmc/card/Kconfig | 7 +++++++
drivers/mmc/card/block.c | 3 +++
drivers/mmc/card/queue.c | 28 ++++++++++++++++++++++++++++
drivers/mmc/card/queue.h | 3 +++
4 files changed, 41 insertions(+), 0 deletions(-)
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -32,6 +32,13 @@ config MMC_BLOCK_BOUNCE
If unsure, say Y here.
+config MMC_BLOCK_PARANOID_RESUME
+ bool "Check card status on resume"
+ depends on MMC_BLOCK
+ default y
+ help
+ Nohelp
+
config SDIO_UART
tristate "SDIO UART/GPS class support"
help
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -643,6 +643,9 @@ static int mmc_blk_resume(struct mmc_car
if (md) {
mmc_blk_set_blksize(md, card);
+#ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME
+ md->queue.check_status = 1;
+#endif
mmc_queue_resume(&md->queue);
}
return 0;
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -14,7 +14,9 @@
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/scatterlist.h>
+#include <linux/delay.h>
+#include <linux/mmc/mmc.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "queue.h"
@@ -70,7 +72,33 @@ static int mmc_queue_thread(void *d)
continue;
}
set_current_state(TASK_RUNNING);
+#ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME
+ if (mq->check_status) {
+ struct mmc_command cmd;
+ do {
+ int err;
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = mq->card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ mmc_claim_host(mq->card->host);
+ err = mmc_wait_for_cmd(mq->card->host, &cmd, 5);
+ mmc_release_host(mq->card->host);
+
+ if (err) {
+ printk(KERN_ERR "%s: failed to get status (%d)\n",
+ __func__, err);
+ msleep(5);
+ continue;
+ }
+ printk(KERN_DEBUG "%s: status 0x%.8x\n", __func__, cmd.resp[0]);
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(cmd.resp[0]) == 7));
+ mq->check_status = 0;
+ }
+#endif
mq->issue_fn(mq, req);
} while (1);
up(&mq->thread_sem);
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -17,6 +17,9 @@ struct mmc_queue {
char *bounce_buf;
struct scatterlist *bounce_sg;
unsigned int bounce_sg_len;
+#ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME
+ int check_status;
+#endif
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);

View File

@ -0,0 +1,91 @@
From 39e3d37b1f7194277b8a3ea3536a67ec2d24491e Mon Sep 17 00:00:00 2001
From: San Mehat <san@android.com>
Date: Thu, 4 Dec 2008 11:18:00 -0800
Subject: [PATCH 100/134] mmc: sd: Add retries in re-detection
Signed-off-by: San Mehat <san@android.com>
---
drivers/mmc/core/sd.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 43 insertions(+), 3 deletions(-)
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -558,18 +558,37 @@ static void mmc_sd_remove(struct mmc_hos
*/
static void mmc_sd_detect(struct mmc_host *host)
{
- int err;
+ int err = 0;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries = 5;
+#endif
BUG_ON(!host);
BUG_ON(!host->card);
-
+
mmc_claim_host(host);
/*
* Just check if our card has been removed.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ while(retries) {
+ err = mmc_send_status(host->card, NULL);
+ printk("%s(%s): err = %d\n", __func__, mmc_hostname(host), err);
+ if (err) {
+ retries--;
+ udelay(5);
+ continue;
+ }
+ break;
+ }
+ if (!retries) {
+ printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",
+ __func__, mmc_hostname(host), err);
+ }
+#else
err = mmc_send_status(host->card, NULL);
-
+#endif
mmc_release_host(host);
if (err) {
@@ -664,6 +683,9 @@ static const struct mmc_bus_ops mmc_sd_o
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
int err;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -712,9 +734,27 @@ int mmc_attach_sd(struct mmc_host *host,
/*
* Detect and init the card.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ retries = 5;
+ while (retries) {
+ err = mmc_sd_init_card(host, host->ocr, NULL);
+ if (err) {
+ retries--;
+ continue;
+ }
+ break;
+ }
+
+ if (!retries) {
+ printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n",
+ mmc_hostname(host), err);
+ goto err;
+ }
+#else
err = mmc_sd_init_card(host, host->ocr, NULL);
if (err)
goto err;
+#endif
mmc_release_host(host);

View File

@ -0,0 +1,27 @@
From 4d3a01ba872f67bae704e37bf888c38fa83be759 Mon Sep 17 00:00:00 2001
From: San Mehat <san@android.com>
Date: Sat, 21 Mar 2009 18:48:05 -0700
Subject: [PATCH 105/134] block: block_dump: Add number of sectors to debug output
Signed-off-by: San Mehat <san@android.com>
---
block/blk-core.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1558,11 +1558,12 @@ void submit_bio(int rw, struct bio *bio)
if (unlikely(block_dump)) {
char b[BDEVNAME_SIZE];
- printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",
+ printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)\n",
current->comm, task_pid_nr(current),
(rw & WRITE) ? "WRITE" : "READ",
(unsigned long long)bio->bi_sector,
- bdevname(bio->bi_bdev, b));
+ bdevname(bio->bi_bdev, b),
+ count);
}
}

View File

@ -0,0 +1,20 @@
From 700cfcab7eee4a822be3fe0c8cfd77caac10bfed Mon Sep 17 00:00:00 2001
From: Dima Zavin <dima@android.com>
Date: Wed, 25 Mar 2009 21:29:45 -0700
Subject: [PATCH 108/134] mmc: sd: Remove debugging printk
Signed-off-by: Dima Zavin <dima@android.com>
---
drivers/mmc/core/sd.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -574,7 +574,6 @@ static void mmc_sd_detect(struct mmc_hos
#ifdef CONFIG_MMC_PARANOID_SD_INIT
while(retries) {
err = mmc_send_status(host->card, NULL);
- printk("%s(%s): err = %d\n", __func__, mmc_hostname(host), err);
if (err) {
retries--;
udelay(5);

View File

@ -0,0 +1,58 @@
From fca2519ebf25db61da132932eb3fffeaa1974f43 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Thu, 15 Jan 2009 19:07:27 -0800
Subject: [PATCH 109/134] Revert "printk: remove unused code from kernel/printk.c"
This reverts commit acff181d3574244e651913df77332e897b88bff4.
---
kernel/printk.c | 39 +++++++++++++++++++++++++++++++++++++++
1 files changed, 39 insertions(+), 0 deletions(-)
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -255,6 +255,45 @@ static inline void boot_delay_msec(void)
#endif
/*
+ * Return the number of unread characters in the log buffer.
+ */
+static int log_buf_get_len(void)
+{
+ return logged_chars;
+}
+
+/*
+ * Copy a range of characters from the log buffer.
+ */
+int log_buf_copy(char *dest, int idx, int len)
+{
+ int ret, max;
+ bool took_lock = false;
+
+ if (!oops_in_progress) {
+ spin_lock_irq(&logbuf_lock);
+ took_lock = true;
+ }
+
+ max = log_buf_get_len();
+ if (idx < 0 || idx >= max) {
+ ret = -1;
+ } else {
+ if (len > max)
+ len = max;
+ ret = len;
+ idx += (log_end - max);
+ while (len-- > 0)
+ dest[len] = LOG_BUF(idx + len);
+ }
+
+ if (took_lock)
+ spin_unlock_irq(&logbuf_lock);
+
+ return ret;
+}
+
+/*
* Commands to do_syslog:
*
* 0 -- Close the log. Currently a NOP.

View File

@ -0,0 +1,24 @@
From e500cd364086cfc018b100d08c97b2fdf229d029 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Mon, 14 Apr 2008 21:35:25 -0700
Subject: [PATCH 110/134] printk: Fix log_buf_copy termination.
If idx was non-zero and the log had wrapped, len did not get truncated
to stop at the last byte written to the log.
---
kernel/printk.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -279,8 +279,8 @@ int log_buf_copy(char *dest, int idx, in
if (idx < 0 || idx >= max) {
ret = -1;
} else {
- if (len > max)
- len = max;
+ if (len > max - idx)
+ len = max - idx;
ret = len;
idx += (log_end - max);
while (len-- > 0)

View File

@ -0,0 +1,404 @@
From aefe5257bf3ed9bf950ad962a63e5315566c2f97 Mon Sep 17 00:00:00 2001
From: Mike Lockwood <lockwood@google.com>
Date: Mon, 23 Jul 2007 09:31:54 -0400
Subject: [PATCH 120/134] [ARM] goldfish: Add audio driver for goldfish.
Signed-off-by: Mike A. Chan <mikechan@google.com>
---
arch/arm/mach-goldfish/Makefile | 2 +-
arch/arm/mach-goldfish/audio.c | 379 +++++++++++++++++++++++++++++++++++++++
2 files changed, 380 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-goldfish/audio.c
--- a/arch/arm/mach-goldfish/Makefile
+++ b/arch/arm/mach-goldfish/Makefile
@@ -4,6 +4,6 @@
# Object file lists.
-obj-y := pdev_bus.o timer.o
+obj-y := pdev_bus.o timer.o audio.o
obj-$(CONFIG_MACH_GOLDFISH) += board-goldfish.o
--- /dev/null
+++ b/arch/arm/mach-goldfish/audio.c
@@ -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 <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+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);

View File

@ -0,0 +1,72 @@
From f0266de1899f043daff3b472b8fdd6e30012cb25 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
Date: Wed, 9 Jan 2008 11:46:33 -0800
Subject: [PATCH 121/134] [ARM] goldfish: Implement suspend as wait-for-interrupt.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
arch/arm/mach-goldfish/Makefile | 2 +-
arch/arm/mach-goldfish/pm.c | 43 +++++++++++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-goldfish/pm.c
--- a/arch/arm/mach-goldfish/Makefile
+++ b/arch/arm/mach-goldfish/Makefile
@@ -4,6 +4,6 @@
# Object file lists.
-obj-y := pdev_bus.o timer.o audio.o
+obj-y := pdev_bus.o timer.o audio.o pm.o
obj-$(CONFIG_MACH_GOLDFISH) += board-goldfish.o
--- /dev/null
+++ b/arch/arm/mach-goldfish/pm.c
@@ -0,0 +1,43 @@
+/* arch/arm/mach-msm/pm.c
+ *
+ * Goldfish Power Management Routines
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <mach/system.h>
+
+static int goldfish_pm_enter(suspend_state_t state)
+{
+ arch_idle();
+ return 0;
+}
+
+static struct platform_suspend_ops goldfish_pm_ops = {
+ .enter = goldfish_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+static int __init goldfish_pm_init(void)
+{
+ suspend_set_ops(&goldfish_pm_ops);
+ return 0;
+}
+
+__initcall(goldfish_pm_init);
+

View File

@ -0,0 +1,368 @@
From 4ff5a10b94c0c41088c8bbfa5be1ebab3822371b Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
Date: Fri, 29 Jun 2007 21:41:20 -0700
Subject: [PATCH 122/134] [ARM] goldfish: tty: Adding tty driver for goldfish.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/char/Kconfig | 6 +
drivers/char/Makefile | 1 +
drivers/char/goldfish_tty.c | 323 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 330 insertions(+), 0 deletions(-)
create mode 100644 drivers/char/goldfish_tty.c
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -1106,6 +1106,12 @@ config DEVPORT
depends on ISA || PCI
default y
+config GOLDFISH_TTY
+ tristate "Goldfish TTY Driver"
+ default n
+ help
+ TTY driver for Goldfish Virtual Platform.
+
source "drivers/s390/char/Kconfig"
endmenu
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o
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/
--- /dev/null
+++ b/drivers/char/goldfish_tty.c
@@ -0,0 +1,323 @@
+/* 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 <linux/console.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+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 int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
+ uint32_t base = qtty->base;
+ return readl(base + GOLDFISH_TTY_BYTES_READY);
+}
+
+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 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,
+ .chars_in_buffer = goldfish_tty_chars_in_buffer,
+};
+
+static int __devinit 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 __devinit 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 __devexit 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_platform_driver = {
+ .probe = goldfish_tty_probe,
+ .remove = __devexit_p(goldfish_tty_remove),
+ .driver = {
+ .name = "goldfish_tty"
+ }
+};
+
+static int __init goldfish_tty_init(void)
+{
+ return platform_driver_register(&goldfish_tty_platform_driver);
+}
+
+static void __exit goldfish_tty_exit(void)
+{
+ platform_driver_unregister(&goldfish_tty_platform_driver);
+}
+
+module_init(goldfish_tty_init);
+module_exit(goldfish_tty_exit);

View File

@ -0,0 +1,234 @@
From 226fd290dc2d052e5cd437cc8464e1424e7ebf2c Mon Sep 17 00:00:00 2001
From: Brian Swetland <swetland@google.com>
Date: Sun, 13 Aug 2006 21:50:14 +0700
Subject: [PATCH 123/134] [ARM] goldfish: events: Add event driver for goldfish
this device is a direct pipe from "hardware" to the input
event subsystem, allowing us to avoid having to route
"keypad" style events through an AT keyboard driver (gross!)
Signed-off-by: Mike A. Chan <mikechan@google.com>
---
drivers/input/keyboard/Kconfig | 5 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/goldfish_events.c | 190 ++++++++++++++++++++++++++++++
3 files changed, 196 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/keyboard/goldfish_events.c
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -304,6 +304,11 @@ config KEYBOARD_GPIO
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
+
config KEYBOARD_MAPLE
tristate "Maple bus keyboard"
depends on SH_DREAMCAST && MAPLE
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
--- /dev/null
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -0,0 +1,190 @@
+/* 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+enum {
+ REG_READ = 0x00,
+ REG_SET_PAGE = 0x00,
+ REG_LEN = 0x04,
+ REG_DATA = 0x08,
+
+ PAGE_NAME = 0x00000,
+ PAGE_EVBITS = 0x10000,
+ PAGE_ABSDATA = 0x20000 | EV_ABS,
+};
+
+struct event_dev {
+ struct input_dev *input;
+ int irq;
+ unsigned addr;
+ char name[0];
+};
+
+static irqreturn_t events_interrupt(int irq, void *dev_id)
+{
+ struct event_dev *edev = dev_id;
+ unsigned type, code, value;
+
+ type = __raw_readl(edev->addr + REG_READ);
+ code = __raw_readl(edev->addr + REG_READ);
+ value = __raw_readl(edev->addr + REG_READ);
+
+ input_event(edev->input, type, code, value);
+ return IRQ_HANDLED;
+}
+
+static void events_import_bits(struct event_dev *edev, unsigned long bits[], unsigned type, size_t count)
+{
+ int i, j;
+ size_t size;
+ uint8_t val;
+ unsigned addr = edev->addr;
+ __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
+ size = __raw_readl(addr + REG_LEN) * 8;
+ if (size < count)
+ count = size;
+ addr = addr + REG_DATA;
+ for (i = 0; i < count; i += 8) {
+ val = __raw_readb(addr++);
+ for (j = 0; j < 8; j++)
+ if(val & 1 << j)
+ set_bit(i + j, bits);
+ }
+}
+
+static int events_probe(struct platform_device *pdev)
+{
+ struct input_dev *input_dev;
+ struct event_dev *edev = NULL;
+ struct resource *res;
+ unsigned keymapnamelen;
+ int i;
+ int count;
+ int irq;
+ unsigned addr;
+ int ret;
+
+ printk("*** events probe ***\n");
+
+ input_dev = input_allocate_device();
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if(!input_dev || !res) goto fail;
+
+ addr = (unsigned) ioremap(res->start, 4096);
+ irq = platform_get_irq(pdev, 0);
+
+ printk("events_probe() addr=0x%08x irq=%d\n", addr, irq);
+
+ if(!addr) goto fail;
+ if(irq < 0) goto fail;
+
+ __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
+ keymapnamelen = __raw_readl(addr + REG_LEN);
+
+ edev = kzalloc(sizeof(struct event_dev) + keymapnamelen + 1, GFP_KERNEL);
+ if (!edev) goto fail;
+
+ edev->input = input_dev;
+ edev->addr = addr;
+ edev->irq = irq;
+
+ for (i = 0; i < keymapnamelen; i++) {
+ edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
+ }
+ printk("events_probe() keymap=%s\n", edev->name);
+
+ events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
+ events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
+ events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
+ events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+ events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
+ events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
+ events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
+ events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
+ events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
+
+ __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
+ count = __raw_readl(addr + REG_LEN) / (4 * 4);
+ if (count > ABS_MAX)
+ count = ABS_MAX;
+ for(i = 0; i < count; i++) {
+ int val[4];
+ int j;
+ if (!test_bit(i, input_dev->absbit))
+ continue;
+ for(j = 0; j < ARRAY_SIZE(val); j++)
+ val[j] = __raw_readl(edev->addr + REG_DATA + (i * ARRAY_SIZE(val) + j) * 4);
+ input_set_abs_params(input_dev, i, val[0], val[1], val[2], val[3]);
+ }
+
+ platform_set_drvdata(pdev, edev);
+
+ input_dev->name = edev->name;
+ input_set_drvdata(input_dev, edev);
+
+ ret = input_register_device(input_dev);
+ if (ret)
+ goto fail;
+
+ if(request_irq(edev->irq, events_interrupt, 0,
+ "goldfish-events-keypad", edev) < 0) {
+ input_unregister_device(input_dev);
+ kfree(edev);
+ return -EINVAL;
+ }
+
+ 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");

View File

@ -0,0 +1,626 @@
From 2eccfcf4c5c50b412a0859a345d8d28fc043956b Mon Sep 17 00:00:00 2001
From: Mike Lockwood <lockwood@android.com>
Date: Thu, 7 Feb 2008 07:47:30 -0500
Subject: [PATCH 124/134] [ARM] goldfish: mmc: goldfish MMC driver building and runnning in 2.6.27.
Signed-off-by: Mike A. Chan <mikechan@google.com>
---
drivers/mmc/host/Kconfig | 8 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/goldfish.c | 583 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 592 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/host/goldfish.c
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -200,6 +200,14 @@ config MMC_MVSDIO
To compile this driver as a module, choose M here: the
module will be called mvsdio.
+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_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
endif
+obj-$(CONFIG_MMC_GOLDFISH) += goldfish.o
obj-$(CONFIG_MMC_S3C) += s3cmci.o
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
--- /dev/null
+++ b/drivers/mmc/host/goldfish.c
@@ -0,0 +1,583 @@
+/*
+ * 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ä<juha.yrjola@nokia.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/major.h>
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#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 *)sg_virt(data->sg);
+ 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) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, cmd->mrq);
+ }
+}
+
+static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
+{
+ struct goldfish_mmc_host * host = (struct goldfish_mmc_host *)dev_id;
+ u16 status;
+ int end_command;
+ int end_transfer;
+ int transfer_error;
+ int state_changed;
+
+ if (host->cmd == NULL && host->data == NULL) {
+ status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
+ dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
+ if (status != 0) {
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
+ }
+ return IRQ_HANDLED;
+ }
+
+ end_command = 0;
+ end_transfer = 0;
+ transfer_error = 0;
+ state_changed = 0;
+
+ while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
+
+ if (status & MMC_STAT_END_OF_CMD) {
+ end_command = 1;
+ }
+
+ if (status & MMC_STAT_END_OF_DATA) {
+ end_transfer = 1;
+ }
+ if (status & MMC_STAT_STATE_CHANGE) {
+ state_changed = 1;
+ }
+ }
+
+ if (end_command) {
+ goldfish_mmc_cmd_done(host, host->cmd);
+ }
+ if (transfer_error)
+ goldfish_mmc_xfer_done(host, host->data);
+ else if (end_transfer) {
+ host->dma_done = 1;
+ goldfish_mmc_end_of_data(host, host->data);
+ }
+ if (state_changed) {
+ schedule_work(&host->switch_work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+static void goldfish_mmc_switch_handler(struct work_struct *work)
+{
+/*
+ struct goldfish_mmc_host *host = container_of(work, struct goldfish_mmc_host, switch_work);
+ struct mmc_card *card;
+ static int complained = 0;
+ int cards = 0, cover_open;
+
+ cover_open = goldfish_mmc_cover_is_open(host);
+ if (cover_open != host->switch_last_state) {
+ kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
+ host->switch_last_state = cover_open;
+ }
+ mmc_detect_change(host->mmc, 0);
+ list_for_each_entry(card, &host->mmc->cards, node) {
+ if (mmc_card_present(card))
+ cards++;
+ }
+ if (goldfish_mmc_cover_is_open(host)) {
+ if (!complained) {
+ dev_info(mmc_dev(host->mmc), "cover is open\n");
+ complained = 1;
+ }
+ } else {
+ complained = 0;
+ }
+*/
+}
+
+
+static void
+goldfish_mmc_prepare_data(struct goldfish_mmc_host *host, struct mmc_request *req)
+{
+ struct mmc_data *data = req->data;
+ int block_size;
+ unsigned sg_len;
+ enum dma_data_direction dma_data_dir;
+
+ host->data = data;
+ if (data == NULL) {
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
+ host->dma_in_use = 0;
+ return;
+ }
+
+ block_size = data->blksz;
+
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
+
+ /* cope with calling layer confusion; it issues "single
+ * block" writes using multi-block scatterlists.
+ */
+ sg_len = (data->blocks == 1) ? 1 : data->sg_len;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ sg_len, dma_data_dir);
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+
+ if (dma_data_dir == DMA_TO_DEVICE) {
+ // we don't really have DMA, so we need to copy to our platform driver buffer
+ const uint8_t* src = (uint8_t *)sg_virt(data->sg);
+ memcpy(host->virt_base, src, data->sg->length);
+ }
+}
+
+static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = req;
+ goldfish_mmc_prepare_data(host, req);
+ goldfish_mmc_start_command(host, req->cmd);
+
+ /* this is to avoid accidentally being detected as an SDIO card in mmc_attach_sdio() */
+ if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
+ req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR)) {
+ req->cmd->error = -EINVAL;
+ }
+}
+
+static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ host->bus_mode = ios->bus_mode;
+ host->hw_bus_mode = host->bus_mode;
+}
+
+static int goldfish_mmc_get_ro(struct mmc_host *mmc)
+{
+ uint32_t state;
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ state = GOLDFISH_MMC_READ(host, MMC_STATE);
+ return ((state & MMC_STATE_READ_ONLY) != 0);
+}
+
+static const struct mmc_host_ops goldfish_mmc_ops = {
+ .request = goldfish_mmc_request,
+ .set_ios = goldfish_mmc_set_ios,
+ .get_ro = goldfish_mmc_get_ro,
+};
+
+static int __init goldfish_mmc_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct goldfish_mmc_host *host = NULL;
+ struct resource *res;
+ int ret = 0;
+ int irq;
+ dma_addr_t buf_addr;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (res == NULL || irq < 0)
+ return -ENXIO;
+
+ mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
+ if (mmc == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_host_failed;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->reg_base = IO_ADDRESS(res->start - IO_START);
+ host->virt_base = dma_alloc_writecombine(&pdev->dev, BUFFER_SIZE,
+ &buf_addr, GFP_KERNEL);
+ if(host->virt_base == 0) {
+ ret = -EBUSY;
+ goto dma_alloc_failed;
+ }
+ host->phys_base = buf_addr;
+
+ host->id = pdev->id;
+ host->irq = irq;
+
+ mmc->ops = &goldfish_mmc_ops;
+ mmc->f_min = 400000;
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ /* Use scatterlist DMA to reduce per-transfer costs.
+ * NOTE max_seg_size assumption that small blocks aren't
+ * normally used (except e.g. for reading SD registers).
+ */
+ mmc->max_phys_segs = 32;
+ mmc->max_hw_segs = 32;
+ mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
+ mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
+ mmc->max_req_size = BUFFER_SIZE;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
+ if (ret)
+ goto err_request_irq_failed;
+
+ host->dev = &pdev->dev;
+ platform_set_drvdata(pdev, host);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
+ if (ret)
+ dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n");
+
+ mmc_add_host(mmc);
+
+ GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
+ MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA | MMC_STAT_STATE_CHANGE
+ );
+
+ // we start with the card present
+ kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
+ mmc_detect_change(host->mmc, 0);
+
+ INIT_WORK(&host->switch_work, goldfish_mmc_switch_handler);
+
+ return 0;
+
+err_request_irq_failed:
+ dma_free_writecombine(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
+dma_alloc_failed:
+ mmc_free_host(host->mmc);
+err_alloc_host_failed:
+ return ret;
+}
+
+static int goldfish_mmc_remove(struct platform_device *pdev)
+{
+ struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ BUG_ON(host == NULL);
+
+ mmc_remove_host(host->mmc);
+ free_irq(host->irq, host);
+ dma_free_writecombine(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+static struct platform_driver goldfish_mmc_driver = {
+ .probe = goldfish_mmc_probe,
+ .remove = goldfish_mmc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init goldfish_mmc_init(void)
+{
+ return platform_driver_register(&goldfish_mmc_driver);
+}
+
+static void __exit goldfish_mmc_exit(void)
+{
+ platform_driver_unregister(&goldfish_mmc_driver);
+}
+
+module_init(goldfish_mmc_init);
+module_exit(goldfish_mmc_exit);
+

View File

@ -0,0 +1,534 @@
From bed297dad6283a5926962c1c59f95ad641824630 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
Date: Fri, 29 Jun 2007 22:20:07 -0700
Subject: [PATCH 125/134] [ARM] goldfish: NAND: Add nand driver for goldfish.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/mtd/devices/Kconfig | 5 +
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/goldfish_nand.c | 418 +++++++++++++++++++++++++++++++
drivers/mtd/devices/goldfish_nand_reg.h | 58 +++++
4 files changed, 482 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/devices/goldfish_nand.c
create mode 100644 drivers/mtd/devices/goldfish_nand_reg.h
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 6fde0a2..0e9bdd7 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -297,5 +297,10 @@ config MTD_DOCPROBE_55AA
LinuxBIOS or if you need to recover a DiskOnChip Millennium on which
you have managed to wipe the first block.
+config MTD_GOLDFISH_NAND
+ tristate "Goldfish NAND device"
+ help
+ none
+
endmenu
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0993d5c..46cf8a8 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
+obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
diff --git a/drivers/mtd/devices/goldfish_nand.c b/drivers/mtd/devices/goldfish_nand.c
new file mode 100644
index 0000000..6b4b8b1
--- /dev/null
+++ b/drivers/mtd/devices/goldfish_nand.c
@@ -0,0 +1,418 @@
+/* drivers/mtd/devices/goldfish_nand.c
+**
+** Copyright (C) 2007 Google, Inc.
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+*/
+
+#include <asm/div64.h>
+#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/mtd.h>
+#include <linux/platform_device.h>
+
+#include "goldfish_nand_reg.h"
+
+struct goldfish_nand {
+ spinlock_t lock;
+ unsigned char __iomem *base;
+ size_t mtd_count;
+ struct mtd_info mtd[0];
+};
+
+static uint32_t goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
+ uint64_t addr, uint32_t len, void *ptr)
+{
+ struct goldfish_nand *nand = mtd->priv;
+ uint32_t rv;
+ unsigned long irq_flags;
+ unsigned char __iomem *base = nand->base;
+
+ spin_lock_irqsave(&nand->lock, irq_flags);
+ writel(mtd - nand->mtd, base + NAND_DEV);
+ writel((uint32_t)(addr >> 32), base + NAND_ADDR_HIGH);
+ writel((uint32_t)addr, base + NAND_ADDR_LOW);
+ writel(len, base + NAND_TRANSFER_SIZE);
+ writel(ptr, base + NAND_DATA);
+ writel(cmd, base + NAND_COMMAND);
+ rv = readl(base + NAND_RESULT);
+ spin_unlock_irqrestore(&nand->lock, irq_flags);
+ return rv;
+}
+
+static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ loff_t ofs = instr->addr;
+ uint32_t len = instr->len;
+ uint32_t rem;
+
+ if (ofs + len > mtd->size)
+ goto invalid_arg;
+ rem = do_div(ofs, mtd->writesize);
+ if(rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if(len % mtd->writesize)
+ goto invalid_arg;
+ len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
+
+ if(goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
+ printk("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size "
+ "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
+ return -EIO;
+ }
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+
+invalid_arg:
+ printk("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size "
+ "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ uint32_t rem;
+
+ if(ofs + ops->len > mtd->size)
+ goto invalid_arg;
+ if(ops->datbuf && ops->len && ops->len != mtd->writesize)
+ goto invalid_arg;
+ if(ops->ooblen + ops->ooboffs > mtd->oobsize)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->writesize);
+ if(rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if(ops->datbuf)
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
+ ops->len, ops->datbuf);
+ ofs += mtd->writesize + ops->ooboffs;
+ if(ops->oobbuf)
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
+ ops->ooblen, ops->oobbuf);
+ return 0;
+
+invalid_arg:
+ printk("goldfish_nand_read_oob: invalid read, start %llx, len %x, "
+ "ooblen %x, dev_size %llx, write_size %x\n",
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ uint32_t rem;
+
+ if(ofs + ops->len > mtd->size)
+ goto invalid_arg;
+ if(ops->len && ops->len != mtd->writesize)
+ goto invalid_arg;
+ if(ops->ooblen + ops->ooboffs > mtd->oobsize)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->writesize);
+ if(rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if(ops->datbuf)
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
+ ops->len, ops->datbuf);
+ ofs += mtd->writesize + ops->ooboffs;
+ if(ops->oobbuf)
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
+ ops->ooblen, ops->oobbuf);
+ return 0;
+
+invalid_arg:
+ printk("goldfish_nand_write_oob: invalid write, start %llx, len %x, "
+ "ooblen %x, dev_size %llx, write_size %x\n",
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ uint32_t rem;
+
+ if(from + len > mtd->size)
+ goto invalid_arg;
+ if(len != mtd->writesize)
+ goto invalid_arg;
+
+ rem = do_div(from, mtd->writesize);
+ if(rem)
+ goto invalid_arg;
+ from *= (mtd->writesize + mtd->oobsize);
+
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
+ return 0;
+
+invalid_arg:
+ printk("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %llx"
+ ", write_size %x\n", from, len, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ uint32_t rem;
+
+ if(to + len > mtd->size)
+ goto invalid_arg;
+ if(len != mtd->writesize)
+ goto invalid_arg;
+
+ rem = do_div(to, mtd->writesize);
+ if(rem)
+ goto invalid_arg;
+ to *= (mtd->writesize + mtd->oobsize);
+
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
+ return 0;
+
+invalid_arg:
+ printk("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %llx"
+ ", write_size %x\n", to, len, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ uint32_t rem;
+
+ if(ofs >= mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->erasesize);
+ if(rem)
+ goto invalid_arg;
+ ofs *= mtd->erasesize / mtd->writesize;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
+
+invalid_arg:
+ printk("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, "
+ "write_size %x\n", ofs, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ uint32_t rem;
+
+ if(ofs >= mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->erasesize);
+ if(rem)
+ goto invalid_arg;
+ ofs *= mtd->erasesize / mtd->writesize;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if(goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
+ return -EIO;
+ return 0;
+
+invalid_arg:
+ printk("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, "
+ "write_size %x\n", ofs, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)
+{
+ uint32_t name_len;
+ uint32_t result;
+ uint32_t flags;
+ unsigned long irq_flags;
+ unsigned char __iomem *base = nand->base;
+ struct mtd_info *mtd = &nand->mtd[id];
+ char *name;
+
+ spin_lock_irqsave(&nand->lock, irq_flags);
+ writel(id, base + NAND_DEV);
+ flags = readl(base + NAND_DEV_FLAGS);
+ name_len = readl(base + NAND_DEV_NAME_LEN);
+ mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
+ mtd->size = readl(base + NAND_DEV_SIZE_LOW);
+ mtd->size |= (uint64_t)readl(base + NAND_DEV_SIZE_HIGH) << 32;
+ mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
+ mtd->oobavail = mtd->oobsize;
+ mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
+ (mtd->writesize + mtd->oobsize) * mtd->writesize;
+ do_div(mtd->size, mtd->writesize + mtd->oobsize);
+ mtd->size *= mtd->writesize;
+ printk("goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
+ id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize);
+ spin_unlock_irqrestore(&nand->lock, irq_flags);
+
+ mtd->priv = nand;
+
+ mtd->name = name = kmalloc(name_len + 1, GFP_KERNEL);
+ if(name == NULL)
+ return -ENOMEM;
+
+ result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, name);
+ if(result != name_len) {
+ kfree(mtd->name);
+ mtd->name = NULL;
+ printk("goldfish_nand_init_device failed to get dev name %d != %d\n",
+ result, name_len);
+ return -ENODEV;
+ }
+ ((char *) mtd->name)[name_len] = '\0';
+
+ /* Setup the MTD structure */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ if(flags & NAND_DEV_FLAG_READ_ONLY)
+ mtd->flags &= ~MTD_WRITEABLE;
+
+ mtd->owner = THIS_MODULE;
+ mtd->erase = goldfish_nand_erase;
+ mtd->read = goldfish_nand_read;
+ mtd->write = goldfish_nand_write;
+ mtd->read_oob = goldfish_nand_read_oob;
+ mtd->write_oob = goldfish_nand_write_oob;
+ mtd->block_isbad = goldfish_nand_block_isbad;
+ mtd->block_markbad = goldfish_nand_block_markbad;
+
+ if (add_mtd_device(mtd)) {
+ kfree(mtd->name);
+ mtd->name = NULL;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int goldfish_nand_probe(struct platform_device *pdev)
+{
+ uint32_t num_dev;
+ int i;
+ int err;
+ uint32_t num_dev_working;
+ uint32_t version;
+ struct resource *r;
+ struct goldfish_nand *nand;
+ unsigned char __iomem *base;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if(r == NULL) {
+ err = -ENODEV;
+ goto err_no_io_base;
+ }
+
+ base = ioremap(r->start, PAGE_SIZE);
+ if(base == NULL) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+ version = readl(base + NAND_VERSION);
+ if(version != NAND_VERSION_CURRENT) {
+ printk("goldfish_nand_init: version mismatch, got %d, expected %d\n",
+ version, NAND_VERSION_CURRENT);
+ err = -ENODEV;
+ goto err_no_dev;
+ }
+ num_dev = readl(base + NAND_NUM_DEV);
+ if(num_dev == 0) {
+ err = -ENODEV;
+ goto err_no_dev;
+ }
+
+ nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
+ if(nand == NULL) {
+ err = -ENOMEM;
+ goto err_nand_alloc_failed;
+ }
+ spin_lock_init(&nand->lock);
+ nand->base = base;
+ nand->mtd_count = num_dev;
+ platform_set_drvdata(pdev, nand);
+
+ num_dev_working = 0;
+ for(i = 0; i < num_dev; i++) {
+ err = goldfish_nand_init_device(nand, i);
+ if(err == 0)
+ num_dev_working++;
+ }
+ if(num_dev_working == 0) {
+ err = -ENODEV;
+ goto err_no_working_dev;
+ }
+ return 0;
+
+err_no_working_dev:
+ kfree(nand);
+err_nand_alloc_failed:
+err_no_dev:
+ iounmap(base);
+err_ioremap:
+err_no_io_base:
+ return err;
+}
+
+static int goldfish_nand_remove(struct platform_device *pdev)
+{
+ struct goldfish_nand *nand = platform_get_drvdata(pdev);
+ int i;
+ for(i = 0; i < nand->mtd_count; i++) {
+ if(nand->mtd[i].name) {
+ del_mtd_device(&nand->mtd[i]);
+ kfree(nand->mtd[i].name);
+ }
+ }
+ iounmap(nand->base);
+ kfree(nand);
+ return 0;
+}
+
+static struct platform_driver goldfish_nand_driver = {
+ .probe = goldfish_nand_probe,
+ .remove = goldfish_nand_remove,
+ .driver = {
+ .name = "goldfish_nand"
+ }
+};
+
+static int __init goldfish_nand_init(void)
+{
+ return platform_driver_register(&goldfish_nand_driver);
+}
+
+static void __exit goldfish_nand_exit(void)
+{
+ platform_driver_unregister(&goldfish_nand_driver);
+}
+
+
+module_init(goldfish_nand_init);
+module_exit(goldfish_nand_exit);
+
diff --git a/drivers/mtd/devices/goldfish_nand_reg.h b/drivers/mtd/devices/goldfish_nand_reg.h
new file mode 100644
index 0000000..7c17a44
--- /dev/null
+++ b/drivers/mtd/devices/goldfish_nand_reg.h
@@ -0,0 +1,58 @@
+/* drivers/mtd/devices/goldfish_nand_reg.h
+**
+** Copyright (C) 2007 Google, Inc.
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+*/
+
+#ifndef GOLDFISH_NAND_REG_H
+#define GOLDFISH_NAND_REG_H
+
+enum nand_cmd {
+ NAND_CMD_GET_DEV_NAME, // Write device name for NAND_DEV to NAND_DATA (vaddr)
+ NAND_CMD_READ,
+ NAND_CMD_WRITE,
+ NAND_CMD_ERASE,
+ NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not
+ NAND_CMD_BLOCK_BAD_SET
+};
+
+enum nand_dev_flags {
+ NAND_DEV_FLAG_READ_ONLY = 0x00000001
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+ // Global
+ NAND_VERSION = 0x000,
+ NAND_NUM_DEV = 0x004,
+ NAND_DEV = 0x008,
+
+ // Dev info
+ NAND_DEV_FLAGS = 0x010,
+ NAND_DEV_NAME_LEN = 0x014,
+ NAND_DEV_PAGE_SIZE = 0x018,
+ NAND_DEV_EXTRA_SIZE = 0x01c,
+ NAND_DEV_ERASE_SIZE = 0x020,
+ NAND_DEV_SIZE_LOW = 0x028,
+ NAND_DEV_SIZE_HIGH = 0x02c,
+
+ // Command
+ NAND_RESULT = 0x040,
+ NAND_COMMAND = 0x044,
+ NAND_DATA = 0x048,
+ NAND_TRANSFER_SIZE = 0x04c,
+ NAND_ADDR_LOW = 0x050,
+ NAND_ADDR_HIGH = 0x054,
+};
+
+#endif
--
1.6.2

View File

@ -0,0 +1,308 @@
From 7041346d379e563e9ee66f9dd31277e4fb6b1b1f Mon Sep 17 00:00:00 2001
From: Mike Lockwood <lockwood@android.com>
Date: Wed, 6 Feb 2008 11:37:10 -0500
Subject: [PATCH 126/134] [ARM] goldfish: POWER: New power supply driver for goldfish.
Signed-off-by: Mike A. Chan <mikechan@google.com>
---
drivers/power/Kconfig | 5 +
drivers/power/Makefile | 3 +-
drivers/power/goldfish_battery.c | 254 ++++++++++++++++++++++++++++++++++++++
3 files changed, 261 insertions(+), 1 deletions(-)
create mode 100644 drivers/power/goldfish_battery.c
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 33da112..593c616 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -88,4 +88,9 @@ config CHARGER_PCF50633
help
Say Y to include support for NXP PCF50633 Main Battery Charger.
+config BATTERY_GOLDFISH
+ tristate "Goldfish battery driver"
+ help
+ Say Y to enable support for the battery and AC power in the Goldfish emulator.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 2fcf41d..5be0fbb 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -19,10 +19,11 @@ obj-$(CONFIG_APM_POWER) += apm_power.o
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
+obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
-obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
\ No newline at end of file
+obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c
new file mode 100644
index 0000000..868dc83
--- /dev/null
+++ b/drivers/power/goldfish_battery.c
@@ -0,0 +1,254 @@
+/* drivers/power/goldfish_battery.c
+ *
+ * Power supply driver for the goldfish emulator
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+
+struct goldfish_battery_data {
+ uint32_t reg_base;
+ int irq;
+ spinlock_t lock;
+
+ struct power_supply battery;
+ struct power_supply ac;
+};
+
+#define GOLDFISH_BATTERY_READ(data, addr) (readl(data->reg_base + addr))
+#define GOLDFISH_BATTERY_WRITE(data, addr, x) (writel(x, data->reg_base + addr))
+
+
+/* temporary variable used between goldfish_battery_probe() and goldfish_battery_open() */
+static struct goldfish_battery_data *battery_data;
+
+enum {
+ /* status register */
+ BATTERY_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ BATTERY_INT_ENABLE = 0x04,
+
+ BATTERY_AC_ONLINE = 0x08,
+ BATTERY_STATUS = 0x0C,
+ BATTERY_HEALTH = 0x10,
+ BATTERY_PRESENT = 0x14,
+ BATTERY_CAPACITY = 0x18,
+
+ BATTERY_STATUS_CHANGED = 1U << 0,
+ AC_STATUS_CHANGED = 1U << 1,
+ BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+static int goldfish_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct goldfish_battery_data *data = container_of(psy,
+ struct goldfish_battery_data, ac);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int goldfish_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct goldfish_battery_data *data = container_of(psy,
+ struct goldfish_battery_data, battery);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property goldfish_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property goldfish_ac_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
+{
+ unsigned long irq_flags;
+ struct goldfish_battery_data *data = dev_id;
+ uint32_t status;
+
+ spin_lock_irqsave(&data->lock, irq_flags);
+
+ /* read status flags, which will clear the interrupt */
+ status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
+ status &= BATTERY_INT_MASK;
+
+ if (status & BATTERY_STATUS_CHANGED)
+ power_supply_changed(&data->battery);
+ if (status & AC_STATUS_CHANGED)
+ power_supply_changed(&data->ac);
+
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+
+static int goldfish_battery_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_battery_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ ret = -ENOMEM;
+ goto err_data_alloc_failed;
+ }
+ spin_lock_init(&data->lock);
+
+ data->battery.properties = goldfish_battery_props;
+ data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
+ data->battery.get_property = goldfish_battery_get_property;
+ data->battery.name = "battery";
+ data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ data->ac.properties = goldfish_ac_props;
+ data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
+ data->ac.get_property = goldfish_ac_get_property;
+ data->ac.name = "ac";
+ data->ac.type = POWER_SUPPLY_TYPE_MAINS;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ printk(KERN_ERR "%s: platform_get_resource failed\n", pdev->name);
+ 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(KERN_ERR "%s: platform_get_irq failed\n", pdev->name);
+ ret = -ENODEV;
+ goto err_no_irq;
+ }
+
+ ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data);
+ if (ret)
+ goto err_request_irq_failed;
+
+ ret = power_supply_register(&pdev->dev, &data->ac);
+ if (ret)
+ goto err_ac_failed;
+
+ ret = power_supply_register(&pdev->dev, &data->battery);
+ if (ret)
+ goto err_battery_failed;
+
+ platform_set_drvdata(pdev, data);
+ battery_data = data;
+
+ GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
+ return 0;
+
+err_battery_failed:
+ power_supply_unregister(&data->ac);
+err_ac_failed:
+ free_irq(data->irq, data);
+err_request_irq_failed:
+err_no_irq:
+err_no_io_base:
+ kfree(data);
+err_data_alloc_failed:
+ return ret;
+}
+
+static int goldfish_battery_remove(struct platform_device *pdev)
+{
+ struct goldfish_battery_data *data = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&data->battery);
+ power_supply_unregister(&data->ac);
+
+ free_irq(data->irq, data);
+ kfree(data);
+ battery_data = NULL;
+ return 0;
+}
+
+static struct platform_driver goldfish_battery_device = {
+ .probe = goldfish_battery_probe,
+ .remove = goldfish_battery_remove,
+ .driver = {
+ .name = "goldfish-battery"
+ }
+};
+
+static int __init goldfish_battery_init(void)
+{
+ return platform_driver_register(&goldfish_battery_device);
+}
+
+static void __exit goldfish_battery_exit(void)
+{
+ platform_driver_unregister(&goldfish_battery_device);
+}
+
+module_init(goldfish_battery_init);
+module_exit(goldfish_battery_exit);
+
+MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
--
1.6.2

View File

@ -0,0 +1,186 @@
From fce981438497199217ed19f14c4d0f1a1aa309f3 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
Date: Fri, 29 Jun 2007 21:46:31 -0700
Subject: [PATCH 127/134] [ARM] goldfish: RTC: Add RTC driver for goldfish.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Gets the current time from the host.
Alarms are not supported yet.
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/rtc/Kconfig | 6 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-goldfish.c | 138 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-goldfish.c
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -691,6 +691,12 @@ config RTC_DRV_BFIN
This driver can also be built as a module. If so, the module
will be called rtc-bfin.
+config RTC_DRV_GOLDFISH
+ tristate "GOLDFISH"
+ depends on ARCH_GOLDFISH
+ help
+ RTC driver for Goldfish Virtual Platform
+
config RTC_DRV_RS5C313
tristate "Ricoh RS5C313"
depends on SH_LANDISK
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds32
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
+obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
--- /dev/null
+++ b/drivers/rtc/rtc-goldfish.c
@@ -0,0 +1,138 @@
+/* drivers/rtc/rtc-goldfish.c
+**
+** Copyright (C) 2007 Google, Inc.
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+*/
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <mach/timer.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+struct goldfish_rtc {
+ uint32_t base;
+ uint32_t irq;
+ struct rtc_device *rtc;
+};
+
+static irqreturn_t
+goldfish_rtc_interrupt(int irq, void *dev_id)
+{
+ struct goldfish_rtc *qrtc = dev_id;
+ unsigned long events = 0;
+
+ writel(1, qrtc->base + TIMER_CLEAR_INTERRUPT);
+ events = RTC_IRQF | RTC_AF;
+
+ rtc_update_irq(qrtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int64_t time;
+ struct goldfish_rtc *qrtc = platform_get_drvdata(to_platform_device(dev));
+
+ time = readl(qrtc->base + TIMER_TIME_LOW);
+ time |= (int64_t)readl(qrtc->base + TIMER_TIME_HIGH) << 32;
+ do_div(time, NSEC_PER_SEC);
+
+ rtc_time_to_tm(time, tm);
+ return 0;
+}
+
+static struct rtc_class_ops goldfish_rtc_ops = {
+// .ioctl = goldfish_rtc_ioctl,
+ .read_time = goldfish_rtc_read_time,
+// .set_time = goldfish_rtc_set_time,
+// .read_alarm = goldfish_rtc_read_alarm,
+// .set_alarm = goldfish_rtc_set_alarm,
+};
+
+
+static int goldfish_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_rtc *qrtc;
+
+ qrtc = kzalloc(sizeof(*qrtc), GFP_KERNEL);
+ if(qrtc == NULL) {
+ ret = -ENOMEM;
+ goto err_qrtc_alloc_failed;
+ }
+ platform_set_drvdata(pdev, qrtc);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if(r == NULL) {
+ ret = -ENODEV;
+ goto err_no_io_base;
+ }
+ qrtc->base = IO_ADDRESS(r->start - IO_START);
+ qrtc->irq = platform_get_irq(pdev, 0);
+ if(qrtc->irq < 0) {
+ ret = -ENODEV;
+ goto err_no_irq;
+ }
+ qrtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &goldfish_rtc_ops, THIS_MODULE);
+ if (IS_ERR(qrtc->rtc)) {
+ ret = PTR_ERR(qrtc->rtc);
+ goto err_rtc_device_register_failed;
+ }
+
+ ret = request_irq(qrtc->irq, goldfish_rtc_interrupt, 0, pdev->name, qrtc);
+ if(ret)
+ goto request_irq;
+
+ return 0;
+
+ free_irq(qrtc->irq, qrtc);
+request_irq:
+ rtc_device_unregister(qrtc->rtc);
+err_rtc_device_register_failed:
+err_no_irq:
+err_no_io_base:
+ kfree(qrtc);
+err_qrtc_alloc_failed:
+ return ret;
+}
+
+static int goldfish_rtc_remove(struct platform_device *pdev)
+{
+ struct goldfish_rtc *qrtc = platform_get_drvdata(pdev);
+ free_irq(qrtc->irq, qrtc);
+ rtc_device_unregister(qrtc->rtc);
+ kfree(qrtc);
+ return 0;
+}
+
+static struct platform_driver goldfish_timer = {
+ .probe = goldfish_rtc_probe,
+ .remove = goldfish_rtc_remove,
+ .driver = {
+ .name = "goldfish_rtc"
+ }
+};
+
+static int __init goldfish_rtc_init(void)
+{
+ return platform_driver_register(&goldfish_timer);
+}
+
+module_init(goldfish_rtc_init);
+

View File

@ -0,0 +1,384 @@
From f8101c780fdc050eda9f93c94f8841de9f384b4f Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
Date: Fri, 29 Jun 2007 22:14:27 -0700
Subject: [PATCH 128/134] [ARM] goldfish: fb: Add fb driver for goldfish.
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Supports panning for double buffering.
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
drivers/video/Kconfig | 9 ++
drivers/video/Makefile | 1 +
drivers/video/goldfishfb.c | 334 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 344 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/goldfishfb.c
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2005,6 +2005,15 @@ config FB_XILINX
framebuffer. ML300 carries a 640*480 LCD display on the board,
ML403 uses a standard DB15 VGA connector.
+config FB_GOLDFISH
+ tristate "Goldfish Framebuffer"
+ depends on FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Framebuffer driver for Goldfish Virtual Platform
+
config FB_COBALT
tristate "Cobalt server LCD frame buffer support"
depends on FB && MIPS_COBALT
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
+obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
obj-$(CONFIG_FB_68328) += 68328fb.o
obj-$(CONFIG_FB_GBE) += gbefb.o
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
--- /dev/null
+++ b/drivers/video/goldfishfb.c
@@ -0,0 +1,334 @@
+/* drivers/video/goldfishfb.c
+**
+** Copyright (C) 2007 Google, Inc.
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_ANDROID_POWER
+#include <linux/android_power.h>
+#endif
+
+#include <mach/hardware.h>
+
+enum {
+ FB_GET_WIDTH = 0x00,
+ FB_GET_HEIGHT = 0x04,
+ FB_INT_STATUS = 0x08,
+ FB_INT_ENABLE = 0x0c,
+ FB_SET_BASE = 0x10,
+ FB_SET_ROTATION = 0x14,
+ FB_SET_BLANK = 0x18,
+ FB_GET_PHYS_WIDTH = 0x1c,
+ FB_GET_PHYS_HEIGHT = 0x20,
+
+ FB_INT_VSYNC = 1U << 0,
+ FB_INT_BASE_UPDATE_DONE = 1U << 1
+};
+
+struct goldfish_fb {
+ uint32_t reg_base;
+ int irq;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ int base_update_count;
+ int rotation;
+ struct fb_info fb;
+ u32 cmap[16];
+#ifdef CONFIG_ANDROID_POWER
+ android_early_suspend_t early_suspend;
+#endif
+};
+
+static irqreturn_t
+goldfish_fb_interrupt(int irq, void *dev_id)
+{
+ unsigned long irq_flags;
+ struct goldfish_fb *fb = dev_id;
+ uint32_t status;
+
+ spin_lock_irqsave(&fb->lock, irq_flags);
+ status = readl(fb->reg_base + FB_INT_STATUS);
+ if(status & FB_INT_BASE_UPDATE_DONE) {
+ fb->base_update_count++;
+ wake_up(&fb->wait);
+ }
+ spin_unlock_irqrestore(&fb->lock, irq_flags);
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
+{
+ unsigned int mask = (1 << bf->length) - 1;
+
+ return (val >> (16 - bf->length) & mask) << bf->offset;
+}
+
+static int
+goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ unsigned int blue, unsigned int transp, struct fb_info *info)
+{
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+
+ if (regno < 16) {
+ fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
+ convert_bitfield(blue, &fb->fb.var.blue) |
+ convert_bitfield(green, &fb->fb.var.green) |
+ convert_bitfield(red, &fb->fb.var.red);
+ return 0;
+ }
+ else {
+ return 1;
+ }
+}
+
+static int goldfish_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ if((var->rotate & 1) != (info->var.rotate & 1)) {
+ if((var->xres != info->var.yres) ||
+ (var->yres != info->var.xres) ||
+ (var->xres_virtual != info->var.yres) ||
+ (var->yres_virtual > info->var.xres * 2) ||
+ (var->yres_virtual < info->var.xres )) {
+ return -EINVAL;
+ }
+ }
+ else {
+ if((var->xres != info->var.xres) ||
+ (var->yres != info->var.yres) ||
+ (var->xres_virtual != info->var.xres) ||
+ (var->yres_virtual > info->var.yres * 2) ||
+ (var->yres_virtual < info->var.yres )) {
+ return -EINVAL;
+ }
+ }
+ if((var->xoffset != info->var.xoffset) ||
+ (var->bits_per_pixel != info->var.bits_per_pixel) ||
+ (var->grayscale != info->var.grayscale)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int goldfish_fb_set_par(struct fb_info *info)
+{
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+ if(fb->rotation != fb->fb.var.rotate) {
+ info->fix.line_length = info->var.xres * 2;
+ fb->rotation = fb->fb.var.rotate;
+ writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
+ }
+ return 0;
+}
+
+
+static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ unsigned long irq_flags;
+ int base_update_count;
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+
+ spin_lock_irqsave(&fb->lock, irq_flags);
+ base_update_count = fb->base_update_count;
+ writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, fb->reg_base + FB_SET_BASE);
+ spin_unlock_irqrestore(&fb->lock, irq_flags);
+ wait_event_timeout(fb->wait, fb->base_update_count != base_update_count, HZ / 15);
+ if(fb->base_update_count == base_update_count)
+ printk("goldfish_fb_pan_display: timeout wating for base update\n");
+ return 0;
+}
+
+#ifdef CONFIG_ANDROID_POWER
+static void goldfish_fb_early_suspend(android_early_suspend_t *h)
+{
+ struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend);
+ writel(1, fb->reg_base + FB_SET_BLANK);
+}
+
+static void goldfish_fb_late_resume(android_early_suspend_t *h)
+{
+ struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend);
+ writel(0, fb->reg_base + FB_SET_BLANK);
+}
+#endif
+
+static struct fb_ops goldfish_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = goldfish_fb_check_var,
+ .fb_set_par = goldfish_fb_set_par,
+ .fb_setcolreg = goldfish_fb_setcolreg,
+ .fb_pan_display = goldfish_fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+
+static int goldfish_fb_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_fb *fb;
+ size_t framesize;
+ uint32_t width, height;
+ dma_addr_t fbpaddr;
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if(fb == NULL) {
+ ret = -ENOMEM;
+ goto err_fb_alloc_failed;
+ }
+ spin_lock_init(&fb->lock);
+ init_waitqueue_head(&fb->wait);
+ platform_set_drvdata(pdev, fb);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if(r == NULL) {
+ ret = -ENODEV;
+ goto err_no_io_base;
+ }
+ fb->reg_base = IO_ADDRESS(r->start - IO_START);
+
+ fb->irq = platform_get_irq(pdev, 0);
+ if(fb->irq < 0) {
+ ret = -ENODEV;
+ goto err_no_irq;
+ }
+
+ width = readl(fb->reg_base + FB_GET_WIDTH);
+ height = readl(fb->reg_base + FB_GET_HEIGHT);
+
+ fb->fb.fbops = &goldfish_fb_ops;
+ fb->fb.flags = FBINFO_FLAG_DEFAULT;
+ fb->fb.pseudo_palette = fb->cmap;
+ //strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id));
+ fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+ fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ fb->fb.fix.line_length = width * 2;
+ fb->fb.fix.accel = FB_ACCEL_NONE;
+ fb->fb.fix.ypanstep = 1;
+
+ fb->fb.var.xres = width;
+ fb->fb.var.yres = height;
+ fb->fb.var.xres_virtual = width;
+ fb->fb.var.yres_virtual = height * 2;
+ fb->fb.var.bits_per_pixel = 16;
+ fb->fb.var.activate = FB_ACTIVATE_NOW;
+ fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
+ fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
+
+ fb->fb.var.red.offset = 11;
+ fb->fb.var.red.length = 5;
+ fb->fb.var.green.offset = 5;
+ fb->fb.var.green.length = 6;
+ fb->fb.var.blue.offset = 0;
+ fb->fb.var.blue.length = 5;
+
+ framesize = width * height * 2 * 2;
+ fb->fb.screen_base = dma_alloc_writecombine(&pdev->dev, framesize,
+ &fbpaddr, GFP_KERNEL);
+ printk("allocating frame buffer %d * %d, got %p\n", width, height, fb->fb.screen_base);
+ if(fb->fb.screen_base == 0) {
+ ret = -ENOMEM;
+ goto err_alloc_screen_base_failed;
+ }
+ fb->fb.fix.smem_start = fbpaddr;
+ fb->fb.fix.smem_len = framesize;
+
+ ret = fb_set_var(&fb->fb, &fb->fb.var);
+ if(ret)
+ goto err_fb_set_var_failed;
+
+ ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb);
+ if(ret)
+ goto err_request_irq_failed;
+
+ writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
+ goldfish_fb_pan_display(&fb->fb.var, &fb->fb); // updates base
+
+ ret = register_framebuffer(&fb->fb);
+ if(ret)
+ goto err_register_framebuffer_failed;
+
+#ifdef CONFIG_ANDROID_POWER
+ fb->early_suspend.suspend = goldfish_fb_early_suspend;
+ fb->early_suspend.resume = goldfish_fb_late_resume;
+ android_register_early_suspend(&fb->early_suspend);
+#endif
+
+ return 0;
+
+
+err_register_framebuffer_failed:
+ free_irq(fb->irq, fb);
+err_request_irq_failed:
+err_fb_set_var_failed:
+ dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start);
+err_alloc_screen_base_failed:
+err_no_irq:
+err_no_io_base:
+ kfree(fb);
+err_fb_alloc_failed:
+ return ret;
+}
+
+static int goldfish_fb_remove(struct platform_device *pdev)
+{
+ size_t framesize;
+ struct goldfish_fb *fb = platform_get_drvdata(pdev);
+
+ framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2;
+
+#ifdef CONFIG_ANDROID_POWER
+ android_unregister_early_suspend(&fb->early_suspend);
+#endif
+ unregister_framebuffer(&fb->fb);
+ free_irq(fb->irq, fb);
+ dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start);
+ kfree(fb);
+ return 0;
+}
+
+
+static struct platform_driver goldfish_fb_driver = {
+ .probe = goldfish_fb_probe,
+ .remove = goldfish_fb_remove,
+ .driver = {
+ .name = "goldfish_fb"
+ }
+};
+
+static int __init goldfish_fb_init(void)
+{
+ return platform_driver_register(&goldfish_fb_driver);
+}
+
+static void __exit goldfish_fb_exit(void)
+{
+ platform_driver_unregister(&goldfish_fb_driver);
+}
+
+module_init(goldfish_fb_init);
+module_exit(goldfish_fb_exit);
+

View File

@ -0,0 +1,854 @@
From fcd69dd4537320a25a294c82362cc7abe4fe773c Mon Sep 17 00:00:00 2001
From: Ye Wen <ywen@google.com>
Date: Fri, 14 Jul 2006 14:51:45 +0700
Subject: [PATCH 129/134] [ARM] goldfish: qemutrace: Kernel instrumentation for tracing the events.
Like fork, context switch, execve and exit.
This code is to complement Jack's tracing facility.
To turn tracing on:
echo 1 > /sysfs/qemu_trace/state
To turn tracing off: echo 0 > /sysfs/qemu_trace/state
I also added java methods to Debug.java to turn tracing on and off.
The kernel driver also supports adding dynamic symbols to the trace.
To add a symbol 'foo' with hex address 'abcd1234' to the trace:
echo 'abcd1234 foo' > /sysfs/qemu_trace/symbol
Signed-off-by: Mike Chan <mike@android.com>
[ARM] goldfish: qemutrace: Improved support for tracing thread and process names.
Added a new pseudo file /sys/qemu_trace/process_name to allow user
programs to add a trace record for a process name change. Removed
the tracing of thread and process names from the exit() system call
because that was not sufficiently general. Added tracing of thread
names in set_task_comm() and daemonize(). Added tracing of the
thread group id to fork() and clone().
Signed-off-by: Jack Veenstra <veenstra@google.com>
Signed-off-by: Mike Chan <mike@android.com>
---
arch/arm/kernel/entry-armv.S | 5 +
drivers/misc/Kconfig | 5 +
drivers/misc/Makefile | 1 +
drivers/misc/qemutrace/Makefile | 2 +
drivers/misc/qemutrace/qemu_trace.c | 386 +++++++++++++++++++++++++++++
drivers/misc/qemutrace/qemu_trace.h | 22 ++
drivers/misc/qemutrace/qemu_trace_sysfs.c | 182 ++++++++++++++
fs/exec.c | 14 +
kernel/exit.c | 14 +
kernel/fork.c | 8 +
kernel/sched.c | 9 +
mm/mmap.c | 13 +
12 files changed, 661 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/qemutrace/Makefile
create mode 100644 drivers/misc/qemutrace/qemu_trace.c
create mode 100644 drivers/misc/qemutrace/qemu_trace.h
create mode 100644 drivers/misc/qemutrace/qemu_trace_sysfs.c
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -733,6 +733,11 @@ ENTRY(__switch_to)
ldr r0, =thread_notify_head
mov r1, #THREAD_NOTIFY_SWITCH
bl atomic_notifier_call_chain
+#ifdef CONFIG_QEMU_TRACE
+/*
+ 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
UNWIND(.fnend )
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -233,6 +233,11 @@ config ISL29003
This driver can also be built as a module. If so, the module
will be called isl29003.
+config QEMU_TRACE
+ tristate "Virtual Device for QEMU tracing"
+ ---help---
+ This is a virtual device for QEMU tracing.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -20,4 +20,5 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_C2PORT) += c2port/
+obj-$(CONFIG_QEMU_TRACE) += qemutrace/
obj-y += eeprom/
--- /dev/null
+++ b/drivers/misc/qemutrace/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_QEMU_TRACE) := qemu_trace.o
+obj-$(CONFIG_QEMU_TRACE) += qemu_trace_sysfs.o
--- /dev/null
+++ b/drivers/misc/qemutrace/qemu_trace.c
@@ -0,0 +1,386 @@
+/* drivers/misc/qemutrace/qemu_trace.c
+ *
+ * Copyright (C) 2007-2008 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#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_UNMAP_START 14
+#define TRACE_DEV_REG_UNMAP_END 15
+#define TRACE_DEV_REG_NAME 16
+#define TRACE_DEV_REG_TGID 17
+#define TRACE_DEV_REG_DYN_SYM 50
+#define TRACE_DEV_REG_DYN_SYM_ADDR 51
+#define TRACE_DEV_REG_REMOVE_ADDR 52
+#define TRACE_DEV_REG_ENABLE 100
+
+static unsigned char __iomem *qt_base;
+static int init_called;
+
+/* PIDs that start before our device registered */
+#define MAX_INIT_PIDS 2048
+static int tb_next = 0;
+static int init_pids[MAX_INIT_PIDS];
+static DEFINE_SPINLOCK(qemu_trace_lock);
+
+void qemu_trace_start(void)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(1, qt_base + (TRACE_DEV_REG_ENABLE << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+void qemu_trace_stop(void)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(0, qt_base + (TRACE_DEV_REG_ENABLE << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+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_add_mapping(unsigned int addr, const char *symbol)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ /* Write the address first, then the symbol name. */
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(addr, qt_base + (TRACE_DEV_REG_DYN_SYM_ADDR << 2));
+ writel(symbol, qt_base + (TRACE_DEV_REG_DYN_SYM << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+void qemu_trace_remove_mapping(unsigned int addr)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(addr, qt_base + (TRACE_DEV_REG_REMOVE_ADDR << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+
+/* trace the context switch */
+void qemu_trace_cs(struct task_struct *next)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(task_pid_nr(next), qt_base);
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_cs);
+
+/* trace the execve */
+void qemu_trace_execve(int argc, char __user * __user *argv)
+{
+ unsigned long irq_flags;
+ 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 ++))
+ return;
+ len = strnlen_user(str, PAGE_SIZE);
+ if (len == 0)
+ return;
+ if (copy_from_user(ptr, str, len))
+ return;
+ ptr += len;
+ }
+
+ if (ptr > page) {
+ int len = ptr - page;
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(len, qt_base + (TRACE_DEV_REG_CMDLINE_LEN << 2));
+ writel(page, qt_base + (TRACE_DEV_REG_CMDLINE << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+ }
+}
+EXPORT_SYMBOL(qemu_trace_execve);
+
+/* trace the mmap */
+void qemu_trace_mmap(struct vm_area_struct *vma)
+{
+ unsigned long irq_flags;
+ char page[PAGE_SIZE];
+ char *p;
+
+ if (qt_base == NULL)
+ return;
+
+ if (vma->vm_file == NULL)
+ return;
+
+ p = d_path(&vma->vm_file->f_path, page, PAGE_SIZE);
+ if (IS_ERR(p))
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ 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));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_mmap);
+
+/* trace the munmap */
+void qemu_trace_munmap(unsigned long start, unsigned long end)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(start, qt_base + (TRACE_DEV_REG_UNMAP_START << 2));
+ writel(end, qt_base + (TRACE_DEV_REG_UNMAP_END << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_munmap);
+
+/* trace the fork */
+void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ if (qt_base == NULL) {
+ if (tb_next >= MAX_INIT_PIDS) {
+ if (!init_called)
+ printk(KERN_ERR
+ "QEMU Trace: too many PIDs before "
+ "device registered ignoring %d\n",
+ forked->pid);
+ } else {
+ init_pids[tb_next] = task_pid_nr(forked);
+ tb_next++;
+ }
+ } else {
+ writel(task_tgid_nr(forked), qt_base + (TRACE_DEV_REG_TGID << 2));
+ if (clone_flags & CLONE_VM)
+ writel(task_pid_nr(forked), qt_base + (TRACE_DEV_REG_CLONE << 2));
+ else
+ writel(task_pid_nr(forked), qt_base + (TRACE_DEV_REG_FORK << 2));
+ }
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_fork);
+
+/* trace the exit */
+void qemu_trace_exit(int code)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(code, qt_base + (TRACE_DEV_REG_EXIT << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_exit);
+
+/* trace the thread name */
+void qemu_trace_thread_name(const char *name)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(name, qt_base + (TRACE_DEV_REG_NAME << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_thread_name);
+
+/* trace the process name */
+void qemu_trace_process_name(const char *name)
+{
+ unsigned long irq_flags;
+
+ if (qt_base == NULL)
+ return;
+
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(name, qt_base + (TRACE_DEV_REG_NAME << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+}
+EXPORT_SYMBOL(qemu_trace_process_name);
+
+static void qemu_trace_pid_exec(struct task_struct *tsk)
+{
+ unsigned long irq_flags;
+ 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_path, page, PAGE_SIZE);
+ if (!IS_ERR(p)) {
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ 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));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+ }
+ }
+ vma = vma->vm_next;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+}
+
+static void qemu_trace_dump_init_threads(void)
+{
+ unsigned long irq_flags;
+ int i;
+
+ for (i = 0; i < tb_next; i++) {
+ struct task_struct *tsk;
+ struct pid *pid = find_get_pid(init_pids[i]);
+ if (pid == NULL)
+ continue;
+
+ if ((tsk = get_pid_task(pid, PIDTYPE_PID)) != NULL) {
+ /* first give the pid and name */
+ task_lock(tsk);
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
+ writel(task_tgid_nr(tsk), qt_base + (TRACE_DEV_REG_TGID << 2));
+ writel(task_pid_nr(tsk), qt_base + (TRACE_DEV_REG_INIT_PID << 2));
+ writel(tsk->comm, qt_base + (TRACE_DEV_REG_INIT_NAME << 2));
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
+ 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;
+
+ /* not thread safe, but this should not happen */
+ if (qt_base != NULL) {
+ printk(KERN_ERR "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(KERN_INFO "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");
--- /dev/null
+++ b/drivers/misc/qemutrace/qemu_trace.h
@@ -0,0 +1,22 @@
+/* drivers/misc/qemutrace/qemu_trace.h
+ *
+ * Copyright (C) 2007-2008 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.
+ *
+ */
+
+void qemu_trace_start(void);
+void qemu_trace_stop(void);
+int qemu_trace_get_tracing(void);
+void qemu_trace_add_mapping(unsigned int addr, const char *symbol);
+void qemu_trace_remove_mapping(unsigned int addr);
+void qemu_trace_process_name(const char *name);
--- /dev/null
+++ b/drivers/misc/qemutrace/qemu_trace_sysfs.c
@@ -0,0 +1,182 @@
+/* drivers/misc/qemu_sysfs.c
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ * Author: Jack Veenstra
+ *
+ * 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 <linux/list.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sysdev.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/wait.h>
+#include "qemu_trace.h"
+
+MODULE_DESCRIPTION("Qemu Trace Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static struct kobject *qemu_trace_kobj;
+
+static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
+{
+ int val = qemu_trace_get_tracing();
+ buf[0] = '0' + val;
+ buf[1] = '\n';
+ return 2;
+}
+
+static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, 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 kobject *kobj, struct kobj_attribute *attr, 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.
+//
+// To remove the mapping for (addr, symbol) in the trace file, write just the
+// address. As before, the address is in hex without the leading '0x'. It can
+// be newline-terminated or zero-terminated.
+static ssize_t symbol_store(struct kobject *kobj, struct kobj_attribute *attr, 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 if (*cp == 0 || *cp == '\n') {
+ qemu_trace_remove_mapping(addr);
+ return n;
+ } 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_add_mapping(addr, sym);
+ kfree(sym);
+ return n;
+}
+
+static ssize_t process_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+/* This expects a string that is the process name. If the string contains
+ * a trailing newline, that is removed in the emulator tracing code because
+ * it is simpler to do it there.
+ */
+static ssize_t process_name_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ if (n <= 0 || buf == NULL)
+ return -EINVAL;
+
+ qemu_trace_process_name(buf);
+ return n;
+}
+
+
+#define qemu_trace_attr(_name) \
+static struct kobj_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0666, \
+ }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
+qemu_trace_attr(state);
+qemu_trace_attr(symbol);
+qemu_trace_attr(process_name);
+
+static struct attribute * qemu_trace_attrs[] = {
+ &state_attr.attr,
+ &symbol_attr.attr,
+ &process_name_attr.attr,
+ NULL,
+};
+
+static struct attribute_group qemu_trace_attr_group = {
+ .attrs = qemu_trace_attrs,
+};
+
+static int __init qemu_trace_init(void)
+{
+ int ret;
+
+ qemu_trace_kobj = kobject_create_and_add("qemu_trace", NULL);
+ if (qemu_trace_kobj == NULL) {
+ printk("qemu_trace_init: kobject_create_and_add failed\n");
+ ret = -ENOMEM;
+ return ret;
+ }
+ ret = sysfs_create_group(qemu_trace_kobj, &qemu_trace_attr_group);
+ if (ret) {
+ printk("qemu_trace_init: sysfs_create_group failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ kobject_del(qemu_trace_kobj);
+ qemu_trace_kobj = NULL;
+ return ret;
+}
+
+static void __exit qemu_trace_exit(void)
+{
+ sysfs_remove_group(qemu_trace_kobj, &qemu_trace_attr_group);
+ kobject_del(qemu_trace_kobj);
+}
+
+core_initcall(qemu_trace_init);
+module_exit(qemu_trace_exit);
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -59,6 +59,9 @@
#include <asm/mmu_context.h>
#include <asm/tlb.h>
#include "internal.h"
+#ifdef CONFIG_QEMU_TRACE
+ void qemu_trace_thread_name(char *name);
+#endif
int core_uses_pid;
char core_pattern[CORENAME_MAX_SIZE] = "core";
@@ -922,6 +925,9 @@ void set_task_comm(struct task_struct *t
task_lock(tsk);
strlcpy(tsk->comm, buf, sizeof(tsk->comm));
task_unlock(tsk);
+#ifdef CONFIG_QEMU_TRACE
+ qemu_trace_thread_name(buf);
+#endif
}
int flush_old_exec(struct linux_binprm * bprm)
@@ -1245,6 +1251,10 @@ void free_bprm(struct linux_binprm *bprm
kfree(bprm);
}
+#ifdef CONFIG_QEMU_TRACE
+extern void qemu_trace_execve(int argc, char __user * __user * argv);
+#endif
+
/*
* sys_execve() executes a new program.
*/
@@ -1324,6 +1334,10 @@ int do_execve(char * filename,
goto out;
current->flags &= ~PF_KTHREAD;
+#ifdef CONFIG_QEMU_TRACE
+ qemu_trace_execve(bprm->argc, argv);
+#endif
+
retval = search_binary_handler(bprm,regs);
if (retval < 0)
goto out;
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -60,6 +60,11 @@ DEFINE_TRACE(sched_process_free);
DEFINE_TRACE(sched_process_exit);
DEFINE_TRACE(sched_process_wait);
+#ifdef CONFIG_QEMU_TRACE
+void qemu_trace_thread_name(char *name);
+void qemu_trace_exit(int code);
+#endif
+
static void exit_mm(struct task_struct * tsk);
static void __unhash_process(struct task_struct *p)
@@ -426,6 +431,9 @@ void daemonize(const char *name, ...)
va_start(args, name);
vsnprintf(current->comm, sizeof(current->comm), name, args);
va_end(args);
+#ifdef CONFIG_QEMU_TRACE
+ qemu_trace_thread_name(current->comm);
+#endif
/*
* If we were started as result of loading a module, close all of the
@@ -1012,6 +1020,12 @@ NORET_TYPE void do_exit(long code)
preempt_disable();
/* causes final put_task_struct in finish_task_switch(). */
tsk->state = TASK_DEAD;
+
+#ifdef CONFIG_QEMU_TRACE
+ /* Emit a trace record for the exit() call. */
+ qemu_trace_exit(code);
+#endif
+
schedule();
BUG();
/* Avoid "noreturn function does return". */
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1333,6 +1333,10 @@ struct task_struct * __cpuinit fork_idle
return task;
}
+#ifdef CONFIG_QEMU_TRACE
+extern void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags);
+#endif
+
/*
* Ok, this is the main fork-routine.
*
@@ -1434,6 +1438,10 @@ long do_fork(unsigned long clone_flags,
tracehook_report_clone_complete(trace, regs,
clone_flags, nr, p);
+#ifdef CONFIG_QEMU_TRACE
+ qemu_trace_fork(p, clone_flags);
+#endif
+
if (clone_flags & CLONE_VFORK) {
freezer_do_not_count();
wait_for_completion(&vfork);
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -2747,6 +2747,10 @@ asmlinkage void schedule_tail(struct tas
put_user(task_pid_vnr(current), current->set_child_tid);
}
+#ifdef CONFIG_QEMU_TRACE
+void qemu_trace_cs(struct task_struct *next);
+#endif
+
/*
* context_switch - switch to the new MM and the new
* thread's register state.
@@ -2789,6 +2793,11 @@ context_switch(struct rq *rq, struct tas
spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
#endif
+#ifdef CONFIG_QEMU_TRACE
+ /* Emit a trace record for the context switch. */
+ qemu_trace_cs(next);
+#endif
+
/* Here we just switch the register state and the stack. */
switch_to(prev, next, prev);
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -903,6 +903,11 @@ void vm_stat_account(struct mm_struct *m
}
#endif /* CONFIG_PROC_FS */
+#ifdef CONFIG_QEMU_TRACE
+extern void qemu_trace_mmap(struct vm_area_struct * vma);
+extern void qemu_trace_munmap(unsigned long start, unsigned long end);
+#endif
+
/*
* The caller must hold down_write(current->mm->mmap_sem).
*/
@@ -1209,6 +1214,10 @@ munmap_back:
pgoff = vma->vm_pgoff;
vm_flags = vma->vm_flags;
+#ifdef CONFIG_QEMU_TRACE
+ qemu_trace_mmap(vma);
+#endif
+
if (vma_wants_writenotify(vma))
vma->vm_page_prot = vm_get_page_prot(vm_flags & ~VM_SHARED);
@@ -1935,6 +1944,10 @@ int do_munmap(struct mm_struct *mm, unsi
* Remove the vma's, and unmap the actual pages
*/
detach_vmas_to_be_unmapped(mm, vma, prev, end);
+
+#ifdef CONFIG_QEMU_TRACE
+ qemu_trace_munmap(start, end);
+#endif
unmap_region(mm, vma, prev, start, end);
/* Fix up all other VM information */

View File

@ -0,0 +1,84 @@
From 2309613958ee518f94f1a7ba900e08e604e06048 Mon Sep 17 00:00:00 2001
From: Jack Veenstra <veenstra@android.com>
Date: Fri, 1 May 2009 18:50:10 -0700
Subject: [PATCH 133/134] [ARM] goldfish: qemutrace: Add mmap support.
This makes a page of data available for writing from user-space to allow
the Dalvik interpreter to send method trace information to the emulator.
Signed-off-by: Jack Veenstra <veenstra@android.com>
---
drivers/misc/qemutrace/qemu_trace.c | 35 ++++++++++++++++++++++++++++++++++-
1 files changed, 34 insertions(+), 1 deletions(-)
--- a/drivers/misc/qemutrace/qemu_trace.c
+++ b/drivers/misc/qemutrace/qemu_trace.c
@@ -53,6 +53,7 @@
static unsigned char __iomem *qt_base;
static int init_called;
+static uint32_t qemu_trace_paddr;
/* PIDs that start before our device registered */
#define MAX_INIT_PIDS 2048
@@ -330,8 +331,30 @@ static void qemu_trace_dump_init_threads
}
}
+static int qemu_trace_mmap_fop(struct file *file, struct vm_area_struct *vma)
+{
+ int ret = io_remap_pfn_range(vma, vma->vm_start,
+ (qemu_trace_paddr >> PAGE_SHIFT) + 1,
+ PAGE_SIZE, vma->vm_page_prot);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static const struct file_operations qemu_trace_fops = {
+ .owner = THIS_MODULE,
+ .mmap = qemu_trace_mmap_fop,
+};
+
+static struct miscdevice qemu_trace_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "qemu_trace",
+ .fops = &qemu_trace_fops,
+};
+
static int qemu_trace_probe(struct platform_device *pdev)
{
+ int err;
struct resource *r;
/* not thread safe, but this should not happen */
@@ -340,18 +363,28 @@ static int qemu_trace_probe(struct platf
return -ENODEV;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL)
+ if (r == NULL || r->end - r->start < 2 * PAGE_SIZE - 1)
return -EINVAL;
+ qemu_trace_paddr = r->start;
qt_base = ioremap(r->start, PAGE_SIZE);
printk(KERN_INFO "QEMU TRACE Device: The mapped IO base is %p\n", qt_base);
qemu_trace_dump_init_threads();
+ err = misc_register(&qemu_trace_device);
+ if (err)
+ goto err_misc_register;
return 0;
+
+err_misc_register:
+ iounmap(qt_base);
+ qt_base = NULL;
+ return err;
}
static int qemu_trace_remove(struct platform_device *pdev)
{
+ misc_deregister(&qemu_trace_device);
iounmap(qt_base);
qt_base = NULL;
return 0;

View File

@ -0,0 +1,94 @@
--- a/drivers/mtd/devices/goldfish_nand.c
+++ b/drivers/mtd/devices/goldfish_nand.c
@@ -65,7 +65,7 @@ static int goldfish_nand_erase(struct mt
if(rem)
goto invalid_arg;
ofs *= (mtd->writesize + mtd->oobsize);
-
+
if(len % mtd->writesize)
goto invalid_arg;
len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
@@ -94,15 +94,12 @@ static int goldfish_nand_read_oob(struct
if(ofs + ops->len > mtd->size)
goto invalid_arg;
- if(ops->datbuf && ops->len && ops->len != mtd->writesize)
- goto invalid_arg;
if(ops->ooblen + ops->ooboffs > mtd->oobsize)
goto invalid_arg;
rem = do_div(ofs, mtd->writesize);
- if(rem)
- goto invalid_arg;
ofs *= (mtd->writesize + mtd->oobsize);
+ ofs += rem;
if(ops->datbuf)
ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
@@ -131,7 +128,7 @@ static int goldfish_nand_write_oob(struc
goto invalid_arg;
if(ops->ooblen + ops->ooboffs > mtd->oobsize)
goto invalid_arg;
-
+
rem = do_div(ofs, mtd->writesize);
if(rem)
goto invalid_arg;
@@ -160,15 +157,24 @@ static int goldfish_nand_read(struct mtd
if(from + len > mtd->size)
goto invalid_arg;
- if(len != mtd->writesize)
- goto invalid_arg;
+
+ *retlen = 0;
rem = do_div(from, mtd->writesize);
- if(rem)
- goto invalid_arg;
from *= (mtd->writesize + mtd->oobsize);
+ from += rem;
- *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
+ do {
+ *retlen += goldfish_nand_cmd(mtd, NAND_CMD_READ, from, min(len, mtd->writesize - rem), buf);
+ if (len > mtd->writesize - rem) {
+ len -= mtd->writesize - rem;
+ buf += mtd->writesize - rem;
+ from += mtd->writesize + mtd->oobsize - rem;
+ rem = 0;
+ } else {
+ len = 0;
+ }
+ } while (len);
return 0;
invalid_arg:
@@ -184,15 +190,23 @@ static int goldfish_nand_write(struct mt
if(to + len > mtd->size)
goto invalid_arg;
- if(len != mtd->writesize)
- goto invalid_arg;
rem = do_div(to, mtd->writesize);
if(rem)
goto invalid_arg;
to *= (mtd->writesize + mtd->oobsize);
- *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
+ *retlen = 0;
+ do {
+ *retlen += goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, min(len, mtd->writesize), (void *)buf);
+ if (len > mtd->writesize) {
+ len -= mtd->writesize;
+ buf += mtd->writesize;
+ to += mtd->writesize + mtd->oobsize;
+ } else {
+ len = 0;
+ }
+ } while (len);
return 0;
invalid_arg: