diff --git a/Android.mk b/Android.mk index fa2f6f86617..7c5725869a6 100644 --- a/Android.mk +++ b/Android.mk @@ -15,16 +15,4 @@ # LOCAL_PATH := $(my-dir) -ifneq ($(TARGET_SIMULATOR),true) - include $(call first-makefiles-under,$(LOCAL_PATH)) -else - include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \ - adb \ - libcutils \ - libsysutils \ - liblog \ - libnetutils \ - libpixelflinger \ - libzipfile \ - )) -endif +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 00000000000..8611d3bc2c6 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1,52 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop new file mode 100644 index 00000000000..18b05948da3 --- /dev/null +++ b/ThirdPartyProject.prop @@ -0,0 +1,10 @@ +# Copyright 2010 Google Inc. All Rights Reserved. +#Fri Jul 16 10:03:09 PDT 2010 +currentVersion=2.6.32 +version=2.6.32 +isNative=true +feedurl=http\://kernel.org/pub/linux/kernel/v2.6/ +name=linux +keywords=linux +onDevice=true +homepage=http\://kernel.org diff --git a/adb/Android.mk b/adb/Android.mk index f3eaa6e0a7f..919da4dec73 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -25,17 +25,28 @@ ifeq ($(HOST_OS),darwin) LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon endif +ifeq ($(HOST_OS),freebsd) + USB_SRCS := usb_libusb.c + EXTRA_SRCS := get_my_path_freebsd.c + LOCAL_LDLIBS += -lpthread -lusb +endif + ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c EXTRA_SRCS := get_my_path_windows.c EXTRA_STATIC_LIBS := AdbWinApi - LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api/ ifneq ($(strip $(USE_CYGWIN)),) + # Pure cygwin case LOCAL_LDLIBS += -lpthread - else + LOCAL_C_INCLUDES += /usr/include/w32api/ddk + endif + ifneq ($(strip $(USE_MINGW)),) + # MinGW under Linux case LOCAL_LDLIBS += -lws2_32 USE_SYSDEPS_WIN32 := 1 + LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk endif + LOCAL_C_INCLUDES += development/host/windows/usb/api/ endif LOCAL_SRC_FILES := \ @@ -51,7 +62,6 @@ LOCAL_SRC_FILES := \ file_sync_client.c \ $(EXTRA_SRCS) \ $(USB_SRCS) \ - shlist.c \ utils.c \ usb_vendors.c @@ -62,8 +72,17 @@ else LOCAL_SRC_FILES += fdevent.c endif -LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter -LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY +LOCAL_CFLAGS += -g -DADB_HOST=1 -Wall -Wno-unused-parameter +# adb can't be built without optimizations, so we enforce -O2 if no +# other optimization flag is set - but we don't override what the global +# flags are saying if something else is given (-Os or -O3 are useful) +ifeq ($(findstring -O, $(HOST_GLOBAL_CFLAGS)),) +LOCAL_CFLAGS += -O2 +endif +ifneq ($(findstring -O0, $(HOST_GLOBAL_CFLAGS)),) +LOCAL_CFLAGS += -O2 +endif +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := adb LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS) @@ -73,7 +92,7 @@ endif include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),windows) $(LOCAL_INSTALLED_MODULE): \ @@ -85,11 +104,7 @@ endif # adbd device daemon # ========================================================= -# build adbd in all non-simulator builds -BUILD_ADBD := false -ifneq ($(TARGET_SIMULATOR),true) - BUILD_ADBD := true -endif +BUILD_ADBD := true # build adbd for the Linux simulator build # so we can use it to test the adb USB gadget driver on x86 @@ -103,6 +118,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb.c \ + backup_service.c \ fdevent.c \ transport.c \ transport_local.c \ @@ -117,7 +133,16 @@ LOCAL_SRC_FILES := \ log_service.c \ utils.c -LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS := -g -DADB_HOST=0 -Wall -Wno-unused-parameter +# adb can't be built without optimizations, so we enforce -O2 if no +# other optimization flag is set - but we don't override what the global +# flags are saying if something else is given (-Os or -O3 are useful) +ifeq ($(findstring -O, $(TARGET_GLOBAL_CFLAGS)),) +LOCAL_CFLAGS += -O2 +endif +ifneq ($(findstring -O0, $(TARGET_GLOBAL_CFLAGS)),) +LOCAL_CFLAGS += -O2 +endif LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE # TODO: This should probably be board specific, whether or not the kernel has @@ -126,19 +151,68 @@ ifeq ($(TARGET_ARCH),arm) LOCAL_CFLAGS += -DANDROID_GADGET=1 endif +ifeq ($(BOARD_ALWAYS_INSECURE),true) + LOCAL_CFLAGS += -DBOARD_ALWAYS_INSECURE +endif + LOCAL_MODULE := adbd LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_STATIC_LIBRARIES := libcutils - LOCAL_LDLIBS += -lpthread - include $(BUILD_HOST_EXECUTABLE) -else - LOCAL_STATIC_LIBRARIES := libcutils libc - include $(BUILD_EXECUTABLE) +LOCAL_STATIC_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) + +endif + + +# adb host tool for device-as-host +# ========================================================= +ifneq ($(SDK_ONLY),true) +include $(CLEAR_VARS) + +LOCAL_LDLIBS := -lrt -lncurses -lpthread + +LOCAL_SRC_FILES := \ + adb.c \ + console.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + commandline.c \ + adb_client.c \ + sockets.c \ + services.c \ + file_sync_client.c \ + get_my_path_linux.c \ + usb_linux.c \ + utils.c \ + usb_vendors.c \ + fdevent.c + +LOCAL_CFLAGS := \ + -g \ + -DADB_HOST=1 \ + -DADB_HOST_ON_TARGET=1 \ + -Wall \ + -Wno-unused-parameter \ + -D_XOPEN_SOURCE \ + -D_GNU_SOURCE + +# adb can't be built without optimizations, so we enforce -O2 if no +# other optimization flag is set - but we don't override what the global +# flags are saying if something else is given (-Os or -O3 are useful) +ifeq ($(findstring -O, $(TARGET_GLOBAL_CFLAGS)),) +LOCAL_CFLAGS += -O2 endif +ifneq ($(findstring -O0, $(TARGET_GLOBAL_CFLAGS)),) +LOCAL_CFLAGS += -O2 +endif + +LOCAL_MODULE := adb + +LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils +include $(BUILD_EXECUTABLE) endif diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT index 6a5191aeeff..c40695af6f6 100644 --- a/adb/OVERVIEW.TXT +++ b/adb/OVERVIEW.TXT @@ -35,7 +35,7 @@ As a whole, everything works through the following components: (through USB for devices, through TCP for emulators) and provide a few services for clients that run on the host. - The ADB server considers that a device is ONLINE when it has succesfully + The ADB server considers that a device is ONLINE when it has successfully connected to the adbd program within it. Otherwise, the device is OFFLINE, meaning that the ADB server detected a new device/emulator, but could not connect to the adbd daemon. diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index b0124a4da70..be4d50becc2 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -74,7 +74,7 @@ host-usb: host-local: A variant of host-serial used to target the single emulator instance - running on the host. This will fail if therre is none or more than one. + running on the host. This will fail if there is none or more than one. host: When asking for information related to a device, 'host:' can also be @@ -146,7 +146,7 @@ remount: dev: Opens a device file and connects the client directly to it for read/write purposes. Useful for debugging, but may require special - priviledges and thus may not run on all devices. is a full + privileges and thus may not run on all devices. is a full path from the root of the filesystem. tcp: @@ -173,7 +173,7 @@ log: framebuffer: This service is used to send snapshots of the framebuffer to a client. - It requires sufficient priviledges but works as follow: + It requires sufficient privileges but works as follow: After the OKAY, the service sends 16-byte binary structure containing the following fields (little-endian format): @@ -190,14 +190,14 @@ framebuffer: one byte through the channel, which will trigger the service to send it 'size' bytes of framebuffer data. - If the adbd daemon doesn't have sufficient priviledges to open + If the adbd daemon doesn't have sufficient privileges to open the framebuffer device, the connection is simply closed immediately. dns: This service is an exception because it only runs within the ADB server. It is used to implement USB networking, i.e. to provide a network connection to the device through the host machine (note: this is the exact opposite of - network thetering). + network tethering). It is used to perform a gethostbyname(
) on the host and return the corresponding IP address as a 4-byte string. @@ -209,7 +209,7 @@ recover: - creating a file named /tmp/update - reading 'size' bytes from the client and writing them to /tmp/update - - when everything is read succesfully, create a file named /tmp/update.start + - when everything is read successfully, create a file named /tmp/update.start This service can only work when the device is in recovery mode. Otherwise, the /tmp directory doesn't exist and the connection will be closed immediately. diff --git a/adb/adb.c b/adb/adb.c index c1646b807b5..150276972c0 100644 --- a/adb/adb.c +++ b/adb/adb.c @@ -36,6 +36,9 @@ #include "usb_vendors.h" #endif +#if ADB_TRACE +ADB_MUTEX_DEFINE( D_lock ); +#endif int HOST = 0; @@ -90,6 +93,7 @@ void adb_trace_init(void) { "sysdeps", TRACE_SYSDEPS }, { "transport", TRACE_TRANSPORT }, { "jdwp", TRACE_JDWP }, + { "services", TRACE_SERVICES }, { NULL, 0 } }; @@ -144,9 +148,6 @@ void put_apacket(apacket *p) void handle_online(void) { D("adb: online\n"); -#if !ADB_HOST - property_set("adb.connected","1"); -#endif } void handle_offline(atransport *t) @@ -154,9 +155,6 @@ void handle_offline(atransport *t) D("adb: offline\n"); //Close the associated usb run_transport_disconnects(t); -#if !ADB_HOST - property_set("adb.connected",""); -#endif } #if TRACE_PACKETS @@ -308,8 +306,10 @@ void handle_packet(apacket *p, atransport *t) { asocket *s; - D("handle_packet() %d\n", p->msg.command); - + D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], + ((char*) (&(p->msg.command)))[1], + ((char*) (&(p->msg.command)))[2], + ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ @@ -595,14 +595,6 @@ static int install_listener(const char *local_name, const char *connect_to, atra return 0; } -#ifdef HAVE_FORKEXEC -static void sigchld_handler(int n) -{ - int status; - while(waitpid(-1, &status, WNOHANG) > 0) ; -} -#endif - #ifdef HAVE_WIN32_PROC static BOOL WINAPI ctrlc_handler(DWORD type) { @@ -645,6 +637,7 @@ void start_logging(void) fd = unix_open("/dev/null", O_RDONLY); dup2(fd, 0); + adb_close(fd); fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640); if(fd < 0) { @@ -652,6 +645,7 @@ void start_logging(void) } dup2(fd, 1); dup2(fd, 2); + adb_close(fd); fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); #endif } @@ -686,14 +680,16 @@ void start_device_log(void) dup2(fd, 1); dup2(fd, 2); fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); + adb_close(fd); fd = unix_open("/dev/null", O_RDONLY); dup2(fd, 0); + adb_close(fd); } #endif #if ADB_HOST -int launch_server() +int launch_server(int server_port) { #ifdef HAVE_WIN32_PROC /* we need to start the server in the background */ @@ -783,7 +779,7 @@ int launch_server() fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno); return -1; } - get_my_path(path); + get_my_path(path, PATH_MAX); pid_t pid = fork(); if(pid < 0) return -1; @@ -809,9 +805,10 @@ int launch_server() // wait for the "OK\n" message adb_close(fd[1]); int ret = adb_read(fd[0], temp, 3); + int saved_errno = errno; adb_close(fd[0]); if (ret < 0) { - fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno); + fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno); return -1; } if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { @@ -828,7 +825,17 @@ int launch_server() } #endif -int adb_main(int is_daemon) +/* Constructs a local name of form tcp:port. + * target_str points to the target string, it's content will be overwritten. + * target_size is the capacity of the target string. + * server_port is the port number to use for the local name. + */ +void build_local_name(char* target_str, size_t target_size, int server_port) +{ + snprintf(target_str, target_size, "tcp:%d", server_port); +} + +int adb_main(int is_daemon, int server_port) { #if !ADB_HOST int secure = 0; @@ -840,7 +847,7 @@ int adb_main(int is_daemon) #ifdef HAVE_WIN32_PROC SetConsoleCtrlHandler( ctrlc_handler, TRUE ); #elif defined(HAVE_FORKEXEC) - signal(SIGCHLD, sigchld_handler); + // No SIGCHLD. Let the service subproc handle its children. signal(SIGPIPE, SIG_IGN); #endif @@ -851,9 +858,11 @@ int adb_main(int is_daemon) HOST = 1; usb_vendors_init(); usb_init(); - local_init(ADB_LOCAL_TRANSPORT_PORT); + local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); - if(install_listener("tcp:5037", "*smartsocket*", NULL)) { + char local_name[30]; + build_local_name(local_name, sizeof(local_name), server_port); + if(install_listener(local_name, "*smartsocket*", NULL)) { exit(1); } #else @@ -862,12 +871,14 @@ int adb_main(int is_daemon) */ property_get("ro.kernel.qemu", value, ""); if (strcmp(value, "1") != 0) { - property_get("ro.secure", value, ""); + property_get("ro.secure", value, "1"); if (strcmp(value, "1") == 0) { // don't run as root if ro.secure is set... +#ifndef BOARD_ALWAYS_INSECURE secure = 1; +#endif - // ... except we allow running as root in userdebug builds if the + // ... except we allow running as root in userdebug builds if the // service.adb.root property has been set by the "adb root" command property_get("ro.debuggable", value, ""); if (strcmp(value, "1") == 0) { @@ -879,13 +890,15 @@ int adb_main(int is_daemon) } } - /* don't listen on port 5037 if we are running in secure mode */ + /* don't listen on a port (default 5037) if running in secure mode */ /* don't run as root if we are running in secure mode */ if (secure) { struct __user_cap_header_struct header; struct __user_cap_data_struct cap; - prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) { + exit(1); + } /* add extra groups: ** AID_ADB to access the USB driver @@ -895,14 +908,23 @@ int adb_main(int is_daemon) ** AID_GRAPHICS to access the frame buffer ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump) ** AID_SDCARD_RW to allow writing to the SD card + ** AID_MOUNT to allow unmounting the SD card before rebooting + ** AID_NET_BW_STATS to read out qtaguid statistics */ gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS, - AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW }; - setgroups(sizeof(groups)/sizeof(groups[0]), groups); + AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW, AID_MOUNT, + AID_NET_BW_STATS }; + if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) { + exit(1); + } /* then switch user and group to "shell" */ - setgid(AID_SHELL); - setuid(AID_SHELL); + if (setgid(AID_SHELL) != 0) { + exit(1); + } + if (setuid(AID_SHELL) != 0) { + exit(1); + } /* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */ header.version = _LINUX_CAPABILITY_VERSION; @@ -911,18 +933,23 @@ int adb_main(int is_daemon) cap.inheritable = 0; capset(&header, &cap); - D("Local port 5037 disabled\n"); + D("Local port disabled\n"); } else { - if(install_listener("tcp:5037", "*smartsocket*", NULL)) { + char local_name[30]; + build_local_name(local_name, sizeof(local_name), server_port); + if(install_listener(local_name, "*smartsocket*", NULL)) { exit(1); } } /* for the device, start the usb transport if the - ** android usb device exists and "service.adb.tcp" - ** is not set, otherwise start the network transport. + ** android usb device exists and the "service.adb.tcp.port" and + ** "persist.adb.tcp.port" properties are not set. + ** Otherwise start the network transport. */ - property_get("service.adb.tcp.port", value, "0"); + property_get("service.adb.tcp.port", value, ""); + if (!value[0]) + property_get("persist.adb.tcp.port", value, ""); if (sscanf(value, "%d", &port) == 1 && port > 0) { // listen on TCP port specified by service.adb.tcp.port property local_init(port); @@ -931,9 +958,11 @@ int adb_main(int is_daemon) usb_init(); } else { // listen on default port - local_init(ADB_LOCAL_TRANSPORT_PORT); + local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); } + D("adb_main(): pre init_jdwp()\n"); init_jdwp(); + D("adb_main(): post init_jdwp()\n"); #endif if (is_daemon) @@ -947,6 +976,7 @@ int adb_main(int is_daemon) #endif start_logging(); } + D("Event loop starting\n"); fdevent_loop(); @@ -955,6 +985,105 @@ int adb_main(int is_daemon) return 0; } +#if ADB_HOST +void connect_device(char* host, char* buffer, int buffer_size) +{ + int port, fd; + char* portstr = strchr(host, ':'); + char hostbuf[100]; + char serial[100]; + + strncpy(hostbuf, host, sizeof(hostbuf) - 1); + if (portstr) { + if (portstr - host >= sizeof(hostbuf)) { + snprintf(buffer, buffer_size, "bad host name %s", host); + return; + } + // zero terminate the host at the point we found the colon + hostbuf[portstr - host] = 0; + if (sscanf(portstr + 1, "%d", &port) == 0) { + snprintf(buffer, buffer_size, "bad port number %s", portstr); + return; + } + } else { + port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; + } + + snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port); + if (find_transport(serial)) { + snprintf(buffer, buffer_size, "already connected to %s", serial); + return; + } + + fd = socket_network_client(hostbuf, port, SOCK_STREAM); + if (fd < 0) { + snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port); + return; + } + + D("client: connected on remote on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + register_socket_transport(fd, serial, port, 0); + snprintf(buffer, buffer_size, "connected to %s", serial); +} + +void connect_emulator(char* port_spec, char* buffer, int buffer_size) +{ + char* port_separator = strchr(port_spec, ','); + if (!port_separator) { + snprintf(buffer, buffer_size, + "unable to parse '%s' as ,", + port_spec); + return; + } + + // Zero-terminate console port and make port_separator point to 2nd port. + *port_separator++ = 0; + int console_port = strtol(port_spec, NULL, 0); + int adb_port = strtol(port_separator, NULL, 0); + if (!(console_port > 0 && adb_port > 0)) { + *(port_separator - 1) = ','; + snprintf(buffer, buffer_size, + "Invalid port numbers: Expected positive numbers, got '%s'", + port_spec); + return; + } + + /* Check if the emulator is already known. + * Note: There's a small but harmless race condition here: An emulator not + * present just yet could be registered by another invocation right + * after doing this check here. However, local_connect protects + * against double-registration too. From here, a better error message + * can be produced. In the case of the race condition, the very specific + * error message won't be shown, but the data doesn't get corrupted. */ + atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port); + if (known_emulator != NULL) { + snprintf(buffer, buffer_size, + "Emulator on port %d already registered.", adb_port); + return; + } + + /* Check if more emulators can be registered. Similar unproblematic + * race condition as above. */ + int candidate_slot = get_available_local_transport_index(); + if (candidate_slot < 0) { + snprintf(buffer, buffer_size, "Cannot accept more emulators."); + return; + } + + /* Preconditions met, try to connect to the emulator. */ + if (!local_connect_arbitrary_ports(console_port, adb_port)) { + snprintf(buffer, buffer_size, + "Connected to emulator on ports %d,%d", console_port, adb_port); + } else { + snprintf(buffer, buffer_size, + "Could not connect to emulator on ports %d,%d", + console_port, adb_port); + } +} +#endif + int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) { atransport *transport = NULL; @@ -985,7 +1114,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r type = kTransportAny; } else if (!strncmp(service, "transport:", strlen("transport:"))) { service += strlen("transport:"); - serial = strdup(service); + serial = service; } transport = acquire_one_transport(CS_ANY, type, serial, &error_string); @@ -1012,43 +1141,16 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r return 0; } - // add a new TCP transport + // add a new TCP transport, device or emulator if (!strncmp(service, "connect:", 8)) { char buffer[4096]; - int port, fd; char* host = service + 8; - char* portstr = strchr(host, ':'); - - if (!portstr) { - snprintf(buffer, sizeof(buffer), "unable to parse %s as :", host); - goto done; - } - if (find_transport(host)) { - snprintf(buffer, sizeof(buffer), "Already connected to %s", host); - goto done; - } - - // zero terminate host by overwriting the ':' - *portstr++ = 0; - if (sscanf(portstr, "%d", &port) == 0) { - snprintf(buffer, sizeof(buffer), "bad port number %s", portstr); - goto done; - } - - fd = socket_network_client(host, port, SOCK_STREAM); - if (fd < 0) { - snprintf(buffer, sizeof(buffer), "unable to connect to %s:%d", host, port); - goto done; + if (!strncmp(host, "emu:", 4)) { + connect_emulator(host + 4, buffer, sizeof(buffer)); + } else { + connect_device(host, buffer, sizeof(buffer)); } - - D("client: connected on remote on fd %d\n", fd); - close_on_exec(fd); - disable_tcp_nagle(fd); - snprintf(buf, sizeof buf, "%s:%d", host, port); - register_socket_transport(fd, buf, port, 0); - snprintf(buffer, sizeof(buffer), "connected to %s:%d", host, port); - -done: + // Send response for emulator and device snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer); writex(reply_fd, buf, strlen(buf)); return 0; @@ -1059,12 +1161,23 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r char buffer[4096]; memset(buffer, 0, sizeof(buffer)); char* serial = service + 11; - atransport *t = find_transport(serial); - - if (t) { - unregister_transport(t); + if (serial[0] == 0) { + // disconnect from all TCP devices + unregister_all_tcp_transports(); } else { - snprintf(buffer, sizeof(buffer), "No such device %s", serial); + char hostbuf[100]; + // assume port 5555 if no port is specified + if (!strchr(serial, ':')) { + snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial); + serial = hostbuf; + } + atransport *t = find_transport(serial); + + if (t) { + unregister_transport(t); + } else { + snprintf(buffer, sizeof(buffer), "No such device %s", serial); + } } snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer); @@ -1161,9 +1274,10 @@ int recovery_mode = 0; int main(int argc, char **argv) { - adb_trace_init(); #if ADB_HOST adb_sysdeps_init(); + adb_trace_init(); + D("Handling commandline()\n"); return adb_commandline(argc - 1, argv + 1); #else if((argc > 1) && (!strcmp(argv[1],"recovery"))) { @@ -1172,6 +1286,7 @@ int main(int argc, char **argv) } start_device_log(); - return adb_main(0); + D("Handling main()\n"); + return adb_main(0, DEFAULT_ADB_PORT); #endif } diff --git a/adb/adb.h b/adb/adb.h index b95868262e7..ac31f11cc20 100644 --- a/adb/adb.h +++ b/adb/adb.h @@ -19,6 +19,8 @@ #include +#include "transport.h" /* readx(), writex() */ + #define MAX_PAYLOAD 4096 #define A_SYNC 0x434e5953 @@ -33,7 +35,7 @@ #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 25 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -183,6 +185,7 @@ struct atransport /* used to identify transports for clients */ char *serial; char *product; + int adb_port; // Use for emulators (local transport) /* a list of adisconnect callbacks called when the transport is kicked */ int kicked; @@ -236,9 +239,9 @@ void fatal_errno(const char *fmt, ...); void handle_packet(apacket *p, atransport *t); void send_packet(apacket *p, atransport *t); -void get_my_path(char s[PATH_MAX]); -int launch_server(); -int adb_main(int is_daemon); +void get_my_path(char *s, size_t maxLen); +int launch_server(int server_port); +int adb_main(int is_daemon, int server_port); /* transports are ref-counted @@ -262,6 +265,9 @@ void run_transport_disconnects( atransport* t ); void kick_transport( atransport* t ); /* initialize a transport object's func pointers and state */ +#if ADB_HOST +int get_available_local_transport_index(); +#endif int init_socket_transport(atransport *t, int s, int port, int local); void init_usb_transport(atransport *t, usb_handle *usb, int state); @@ -271,8 +277,9 @@ void close_usb_devices(); /* cause new transports to be init'd and added to the list */ void register_socket_transport(int s, const char *serial, int port, int local); -/* this should only be used for the "adb disconnect" command */ +/* these should only be used for the "adb disconnect" command */ void unregister_transport(atransport *t); +void unregister_all_tcp_transports(); void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable); @@ -280,6 +287,9 @@ void register_usb_transport(usb_handle *h, const char *serial, unsigned writeabl void unregister_usb_transport(usb_handle *usb); atransport *find_transport(const char *serial); +#if ADB_HOST +atransport* find_emulator_transport_by_adb_port(int adb_port); +#endif int service_to_fd(const char *name); #if ADB_HOST @@ -294,6 +304,11 @@ int create_jdwp_connection_fd(int jdwp_pid); #endif #if !ADB_HOST +typedef enum { + BACKUP, + RESTORE +} BackupOperation; +int backup_service(BackupOperation operation, char* args); void framebuffer_service(int fd, void *cookie); void log_service(int fd, void *cookie); void remount_service(int fd, void *cookie); @@ -307,13 +322,6 @@ void put_apacket(apacket *p); int check_header(apacket *p); int check_data(apacket *p); -/* convenience wrappers around read/write that will retry on -** EINTR and/or short read/write. Returns 0 on success, -1 -** on error or EOF. -*/ -int readx(int fd, void *ptr, size_t len); -int writex(int fd, const void *ptr, size_t len); - /* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ #define ADB_TRACE 1 @@ -323,33 +331,56 @@ int writex(int fd, const void *ptr, size_t len); * the adb_trace_init() function implemented in adb.c */ typedef enum { - TRACE_ADB = 0, + TRACE_ADB = 0, /* 0x001 */ TRACE_SOCKETS, TRACE_PACKETS, TRACE_TRANSPORT, - TRACE_RWX, + TRACE_RWX, /* 0x010 */ TRACE_USB, TRACE_SYNC, TRACE_SYSDEPS, - TRACE_JDWP, + TRACE_JDWP, /* 0x100 */ + TRACE_SERVICES, } AdbTrace; #if ADB_TRACE - int adb_trace_mask; - + extern int adb_trace_mask; + extern unsigned char adb_trace_output_count; void adb_trace_init(void); # define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) /* you must define TRACE_TAG before using this macro */ - #define D(...) \ +# define D(...) \ + do { \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + fprintf(stderr, "%s::%s():", \ + __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__ ); \ + fflush(stderr); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } \ + } while (0) +# define DR(...) \ do { \ - if (ADB_TRACING) \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + errno = save_errno; \ fprintf(stderr, __VA_ARGS__ ); \ + fflush(stderr); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } \ } while (0) #else # define D(...) ((void)0) +# define DR(...) ((void)0) # define ADB_TRACING 0 #endif @@ -358,8 +389,16 @@ typedef enum { #define print_packet(tag,p) do {} while (0) #endif -#define ADB_PORT 5037 -#define ADB_LOCAL_TRANSPORT_PORT 5555 +#if ADB_HOST_ON_TARGET +/* adb and adbd are coexisting on the target, so use 5038 for adb + * to avoid conflicting with adbd's usage of 5037 + */ +# define DEFAULT_ADB_PORT 5038 +#else +# define DEFAULT_ADB_PORT 5037 +#endif + +#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555 #define ADB_CLASS 0xff #define ADB_SUBCLASS 0x42 @@ -368,6 +407,7 @@ typedef enum { void local_init(int port); int local_connect(int port); +int local_connect_arbitrary_ports(int console_port, int adb_port); /* usb host/client interface */ void usb_init(); @@ -396,6 +436,7 @@ int connection_state(atransport *t); #define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ extern int HOST; +extern int SHELL_EXIT_NOTIFY_FD; #define CHUNK_SIZE (64*1024) diff --git a/adb/adb_client.c b/adb/adb_client.c index 243f0fa5339..9a812f0cf68 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -16,12 +16,19 @@ static transport_type __adb_transport = kTransportAny; static const char* __adb_serial = NULL; +static int __adb_server_port = DEFAULT_ADB_PORT; + void adb_set_transport(transport_type type, const char* serial) { __adb_transport = type; __adb_serial = serial; } +void adb_set_tcp_specifics(int server_port) +{ + __adb_server_port = server_port; +} + int adb_get_emulator_console_port(void) { const char* serial = __adb_serial; @@ -174,7 +181,7 @@ int _adb_connect(const char *service) } snprintf(tmp, sizeof tmp, "%04x", len); - fd = socket_loopback_client(ADB_PORT, SOCK_STREAM); + fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); if(fd < 0) { strcpy(__adb_error, "cannot connect to daemon"); return -2; @@ -195,6 +202,7 @@ int _adb_connect(const char *service) return -1; } + D("_adb_connect: return fd %d\n", fd); return fd; } @@ -203,10 +211,12 @@ int adb_connect(const char *service) // first query the adb server's version int fd = _adb_connect("host:version"); + D("adb_connect: service %s\n", service); if(fd == -2) { - fprintf(stdout,"* daemon not running. starting it now *\n"); + fprintf(stdout,"* daemon not running. starting it now on port %d *\n", + __adb_server_port); start_server: - if(launch_server(0)) { + if(launch_server(__adb_server_port)) { fprintf(stderr,"* failed to start daemon *\n"); return -1; } else { @@ -258,6 +268,7 @@ int adb_connect(const char *service) if(fd == -2) { fprintf(stderr,"** daemon still not running"); } + D("adb_connect: return fd %d\n", fd); return fd; error: @@ -314,5 +325,3 @@ char *adb_query(const char *service) adb_close(fd); return 0; } - - diff --git a/adb/adb_client.h b/adb/adb_client.h index 80615790c46..40ab18970cd 100644 --- a/adb/adb_client.h +++ b/adb/adb_client.h @@ -25,6 +25,10 @@ char *adb_query(const char *service); */ void adb_set_transport(transport_type type, const char* serial); +/* Set TCP specifics of the transport to use +*/ +void adb_set_tcp_specifics(int server_port); + /* Return the console port of the currently connected emulator (if any) * of -1 if there is no emulator, and -2 if there is more than one. * assumes adb_set_transport() was alled previously... diff --git a/adb/backup_service.c b/adb/backup_service.c new file mode 100644 index 00000000000..669ff862355 --- /dev/null +++ b/adb/backup_service.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb.h" + +typedef struct { + pid_t pid; + int fd; +} backup_harvest_params; + +// socketpair but do *not* mark as close_on_exec +static int backup_socketpair(int sv[2]) { + int rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv ); + if (rc < 0) + return -1; + + return 0; +} + +// harvest the child process then close the read end of the socketpair +static void* backup_child_waiter(void* args) { + int status; + backup_harvest_params* params = (backup_harvest_params*) args; + + waitpid(params->pid, &status, 0); + adb_close(params->fd); + free(params); + return NULL; +} + +/* returns the data socket passing the backup data here for forwarding */ +int backup_service(BackupOperation op, char* args) { + pid_t pid; + int s[2]; + char* operation; + int socketnum; + + // Command string and choice of stdin/stdout for the pipe depend on our invocation + if (op == BACKUP) { + operation = "backup"; + socketnum = STDOUT_FILENO; + } else { + operation = "restore"; + socketnum = STDIN_FILENO; + } + + D("backup_service(%s, %s)\n", operation, args); + + // set up the pipe from the subprocess to here + // parent will read s[0]; child will write s[1] + if (backup_socketpair(s)) { + D("can't create backup/restore socketpair\n"); + fprintf(stderr, "unable to create backup/restore socketpair\n"); + return -1; + } + + D("Backup/restore socket pair: (send=%d, receive=%d)\n", s[1], s[0]); + close_on_exec(s[0]); // only the side we hold on to + + // spin off the child process to run the backup command + pid = fork(); + if (pid < 0) { + // failure + D("can't fork for %s\n", operation); + fprintf(stderr, "unable to fork for %s\n", operation); + adb_close(s[0]); + adb_close(s[1]); + return -1; + } + + // Great, we're off and running. + if (pid == 0) { + // child -- actually run the backup here + char* p; + int argc; + char portnum[16]; + char** bu_args; + + // fixed args: [0] is 'bu', [1] is the port number, [2] is the 'operation' string + argc = 3; + for (p = (char*)args; p && *p; ) { + argc++; + while (*p && *p != ':') p++; + if (*p == ':') p++; + } + + bu_args = (char**) alloca(argc*sizeof(char*) + 1); + + // run through again to build the argv array + argc = 0; + bu_args[argc++] = "bu"; + snprintf(portnum, sizeof(portnum), "%d", s[1]); + bu_args[argc++] = portnum; + bu_args[argc++] = operation; + for (p = (char*)args; p && *p; ) { + bu_args[argc++] = p; + while (*p && *p != ':') p++; + if (*p == ':') { + *p = 0; + p++; + } + } + bu_args[argc] = NULL; + + // Close the half of the socket that we don't care about, route 'bu's console + // to the output socket, and off we go + adb_close(s[0]); + + // off we go + execvp("/system/bin/bu", (char * const *)bu_args); + // oops error - close up shop and go home + fprintf(stderr, "Unable to exec 'bu', bailing\n"); + exit(-1); + } else { + adb_thread_t t; + backup_harvest_params* params; + + // parent, i.e. adbd -- close the sending half of the socket + D("fork() returned pid %d\n", pid); + adb_close(s[1]); + + // spin a thread to harvest the child process + params = (backup_harvest_params*) malloc(sizeof(backup_harvest_params)); + params->pid = pid; + params->fd = s[0]; + if (adb_thread_create(&t, backup_child_waiter, params)) { + adb_close(s[0]); + free(params); + D("Unable to create child harvester\n"); + return -1; + } + } + + // we'll be reading from s[0] as the data is sent by the child process + return s[0]; +} diff --git a/adb/commandline.c b/adb/commandline.c index 055aa104e73..ffc120f8ae3 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -37,20 +37,9 @@ #include "adb_client.h" #include "file_sync_service.h" -#ifdef SH_HISTORY -#include "shlist.h" -#include "history.h" -#endif - -enum { - IGNORE_DATA, - WIPE_DATA, - FLASH_DATA -}; - static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); -void get_my_path(char s[PATH_MAX]); +void get_my_path(char *s, size_t maxLen); int find_sync_dirs(const char *srcarg, char **android_srcdir_out, char **data_srcdir_out); int install_app(transport_type transport, char* serial, int argc, char** argv); @@ -97,7 +86,7 @@ void help() " returns an error if more than one emulator is running.\n" " -s - directs command to the USB device or emulator with\n" " the given serial number. Overrides ANDROID_SERIAL\n" - " envivornment variable.\n" + " environment variable.\n" " -p - simple product name like 'sooner', or\n" " a relative/absolute path to a product\n" " out directory like 'out/target/product/sooner'.\n" @@ -105,13 +94,18 @@ void help() " environment variable is used, which must\n" " be an absolute path.\n" " devices - list all connected devices\n" - " connect : - connect to a device via TCP/IP" - " disconnect : - disconnect from a TCP/IP device" + " connect [:] - connect to a device via TCP/IP\n" + " Port 5555 is used by default if no port number is specified.\n" + " disconnect [[:]] - disconnect from a TCP/IP device.\n" + " Port 5555 is used by default if no port number is specified.\n" + " Using this command with no additional arguments\n" + " will disconnect from all connected TCP/IP devices.\n" "\n" "device commands:\n" " adb push - copy file/dir to device\n" - " adb pull - copy file/dir from device\n" + " adb pull [] - copy file/dir from device\n" " adb sync [ ] - copy host->device only if changed\n" + " (-l means list but don't copy)\n" " (see 'adb help all')\n" " adb shell - run remote shell interactively\n" " adb shell - run remote shell command\n" @@ -126,22 +120,37 @@ void help() " dev:\n" " jdwp: (remote only)\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" - " adb install [-l] [-r] - push this package file to the device and install it\n" + " adb install [-l] [-r] [-s] - push this package file to the device and install it\n" " ('-l' means forward-lock the app)\n" " ('-r' means reinstall the app, keeping its data)\n" + " ('-s' means install on SD card instead of internal storage)\n" " adb uninstall [-k] - remove this app package from the device\n" " ('-k' means keep the data and cache directories)\n" " adb bugreport - return all information from the device\n" " that should be included in a bug report.\n" "\n" + " adb backup [-f ] [-apk|-noapk] [-shared|-noshared] [-all] [-system|-nosystem] []\n" + " - write an archive of the device's data to .\n" + " If no -f option is supplied then the data is written\n" + " to \"backup.ab\" in the current directory.\n" + " (-apk|-noapk enable/disable backup of the .apks themselves\n" + " in the archive; the default is noapk.)\n" + " (-shared|-noshared enable/disable backup of the device's\n" + " shared storage / SD card contents; the default is noshared.)\n" + " (-all means to back up all installed applications)\n" + " (-system|-nosystem toggles whether -all automatically includes\n" + " system applications; the default is to include system apps)\n" + " ( is the list of applications to be backed up. If\n" + " the -all or -shared flags are passed, then the package\n" + " list is optional. Applications explicitly given on the\n" + " command line will be included even if -nosystem would\n" + " ordinarily cause them to be omitted.)\n" + "\n" + " adb restore - restore device contents from the backup archive\n" + "\n" " adb help - show this help message\n" " adb version - show version num\n" "\n" - "DATAOPTS:\n" - " (no option) - don't touch the data partition\n" - " -w - wipe the data partition\n" - " -d - flash the data partition\n" - "\n" "scripting:\n" " adb wait-for-device - block until device is online\n" " adb start-server - ensure that there is a server running\n" @@ -151,8 +160,9 @@ void help() " adb status-window - continuously print device status for a specified device\n" " adb remount - remounts the /system partition on the device read-write\n" " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" + " adb reboot-bootloader - reboots the device into the bootloader\n" " adb root - restarts the adbd daemon with root permissions\n" - " adb usb - restarts the adbd daemon listening on USB" + " adb usb - restarts the adbd daemon listening on USB\n" " adb tcpip - restarts the adbd daemon listening on TCP on the specified port" "\n" "networking:\n" @@ -168,6 +178,12 @@ void help() "\n" " - If it is \"system\" or \"data\", only the corresponding partition\n" " is updated.\n" + "\n" + "environmental variables:\n" + " ADB_TRACE - Print debug information. A comma separated list of the following values\n" + " 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n" + " ANDROID_SERIAL - The serial number to connect to. -s takes priority over this if given.\n" + " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n" ); } @@ -210,7 +226,9 @@ static void read_and_dump(int fd) int len; while(fd >= 0) { + D("read_and_dump(): pre adb_read(fd=%d)\n", fd); len = adb_read(fd, buf, 4096); + D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len); if(len == 0) { break; } @@ -219,28 +237,43 @@ static void read_and_dump(int fd) if(errno == EINTR) continue; break; } - /* we want to output to stdout, so no adb_write here !! */ - unix_write(1, buf, len); + fwrite(buf, 1, len, stdout); + fflush(stdout); } } -#ifdef SH_HISTORY -int shItemCmp( void *val, void *idata ) -{ - return( (strcmp( val, idata ) == 0) ); +static void copy_to_file(int inFd, int outFd) { + const size_t BUFSIZE = 32 * 1024; + char* buf = (char*) malloc(BUFSIZE); + int len; + long total = 0; + + D("copy_to_file(%d -> %d)\n", inFd, outFd); + for (;;) { + len = adb_read(inFd, buf, BUFSIZE); + if (len == 0) { + D("copy_to_file() : read 0 bytes; exiting\n"); + break; + } + if (len < 0) { + if (errno == EINTR) { + D("copy_to_file() : EINTR, retrying\n"); + continue; + } + D("copy_to_file() : error %d\n", errno); + break; + } + adb_write(outFd, buf, len); + total += len; + } + D("copy_to_file() finished after %lu bytes\n", total); + free(buf); } -#endif static void *stdin_read_thread(void *x) { int fd, fdi; unsigned char buf[1024]; -#ifdef SH_HISTORY - unsigned char realbuf[1024], *buf_ptr; - SHLIST history; - SHLIST *item = &history; - int cmdlen = 0, ins_flag = 0; -#endif int r, n; int state = 0; @@ -249,108 +282,44 @@ static void *stdin_read_thread(void *x) fdi = fds[1]; free(fds); -#ifdef SH_HISTORY - shListInitList( &history ); -#endif for(;;) { /* fdi is really the client's stdin, so use read, not adb_read here */ + D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi); r = unix_read(fdi, buf, 1024); + D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi); if(r == 0) break; if(r < 0) { if(errno == EINTR) continue; break; } -#ifdef SH_HISTORY - if( (r == 3) && /* Arrow processing */ - (memcmp( (void *)buf, SH_ARROW_ANY, 2 ) == 0) ) { - switch( buf[2] ) { - case SH_ARROW_UP: - item = shListGetNextItem( &history, item ); - break; - case SH_ARROW_DOWN: - item = shListGetPrevItem( &history, item ); - break; - default: - item = NULL; - break; - } - memset( buf, SH_DEL_CHAR, cmdlen ); - if( item != NULL ) { - n = snprintf( (char *)(&buf[cmdlen]), sizeof buf - cmdlen, "%s", (char *)(item->data) ); - memcpy( realbuf, item->data, n ); - } - else { /* Clean buffer */ - item = &history; - n = 0; - } - r = n + cmdlen; - cmdlen = n; - ins_flag = 0; - if( r == 0 ) - continue; - } - else { -#endif - for(n = 0; n < r; n++){ - switch(buf[n]) { - case '\n': -#ifdef SH_HISTORY - if( ins_flag && (SH_BLANK_CHAR <= realbuf[0]) ) { - buf_ptr = malloc(cmdlen + 1); - if( buf_ptr != NULL ) { - memcpy( buf_ptr, realbuf, cmdlen ); - buf_ptr[cmdlen] = '\0'; - if( (item = shListFindItem( &history, (void *)buf_ptr, shItemCmp )) == NULL ) { - shListInsFirstItem( &history, (void *)buf_ptr ); - item = &history; - } - } - } - cmdlen = 0; - ins_flag = 0; -#endif - state = 1; - break; - case '\r': - state = 1; - break; - case '~': - if(state == 1) state++; - break; - case '.': - if(state == 2) { - fprintf(stderr,"\n* disconnect *\n"); - #ifdef HAVE_TERMIO_H - stdin_raw_restore(fdi); - #endif - exit(0); - } - default: -#ifdef SH_HISTORY - if( buf[n] == SH_DEL_CHAR ) { - if( cmdlen > 0 ) - cmdlen--; - } - else { - realbuf[cmdlen] = buf[n]; - cmdlen++; - } - ins_flag = 1; + for(n = 0; n < r; n++){ + switch(buf[n]) { + case '\n': + state = 1; + break; + case '\r': + state = 1; + break; + case '~': + if(state == 1) state++; + break; + case '.': + if(state == 2) { + fprintf(stderr,"\n* disconnect *\n"); +#ifdef HAVE_TERMIO_H + stdin_raw_restore(fdi); #endif - state = 0; + exit(0); } + default: + state = 0; } -#ifdef SH_HISTORY } -#endif r = adb_write(fd, buf, r); if(r <= 0) { break; } } -#ifdef SH_HISTORY - shListDelAllItems( &history, (shListFree)free ); -#endif return 0; } @@ -608,6 +577,107 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) return 0; } +static int mkdirs(char *path) +{ + int ret; + char *x = path + 1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(path, 0775); + *x = OS_PATH_SEPARATOR; + if((ret < 0) && (errno != EEXIST)) { + return ret; + } + x++; + } + return 0; +} + +static int backup(int argc, char** argv) { + char buf[4096]; + char default_name[32]; + const char* filename = strcpy(default_name, "./backup.ab"); + int fd, outFd; + int i, j; + + /* find, extract, and use any -f argument */ + for (i = 1; i < argc; i++) { + if (!strcmp("-f", argv[i])) { + if (i == argc-1) { + fprintf(stderr, "adb: -f passed with no filename\n"); + return usage(); + } + filename = argv[i+1]; + for (j = i+2; j <= argc; ) { + argv[i++] = argv[j++]; + } + argc -= 2; + argv[argc] = NULL; + } + } + + /* bare "adb backup" or "adb backup -f filename" are not valid invocations */ + if (argc < 2) return usage(); + + adb_unlink(filename); + mkdirs((char *)filename); + outFd = adb_creat(filename, 0640); + if (outFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + snprintf(buf, sizeof(buf), "backup"); + for (argc--, argv++; argc; argc--, argv++) { + strncat(buf, ":", sizeof(buf) - strlen(buf) - 1); + strncat(buf, argv[0], sizeof(buf) - strlen(buf) - 1); + } + + D("backup. filename=%s buf=%s\n", filename, buf); + fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(outFd); + return -1; + } + + copy_to_file(fd, outFd); + + adb_close(fd); + adb_close(outFd); + return 0; +} + +static int restore(int argc, char** argv) { + const char* filename; + int fd, tarFd; + + if (argc != 2) return usage(); + + filename = argv[1]; + tarFd = adb_open(filename, O_RDONLY); + if (tarFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + fd = adb_connect("restore:"); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(tarFd); + return -1; + } + + copy_to_file(tarFd, fd); + + adb_close(fd); + adb_close(tarFd); + return 0; +} + #define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make" static int top_works(const char *top) { @@ -673,7 +743,7 @@ static char *find_top(char path_buf[PATH_MAX]) /* If the CWD isn't under a good-looking top, see if the * executable is. */ - get_my_path(dir); + get_my_path(dir, PATH_MAX); top = find_top_from(dir, path_buf); } return top; @@ -754,11 +824,13 @@ int adb_commandline(int argc, char **argv) char buf[4096]; int no_daemon = 0; int is_daemon = 0; + int is_server = 0; int persist = 0; int r; int quote; transport_type ttype = kTransportAny; char* serial = NULL; + char* server_port_str = NULL; /* If defined, this should be an absolute path to * the directory containing all of the various system images @@ -774,9 +846,24 @@ int adb_commandline(int argc, char **argv) serial = getenv("ANDROID_SERIAL"); - /* modifiers and flags */ + /* Validate and assign the server port */ + server_port_str = getenv("ANDROID_ADB_SERVER_PORT"); + int server_port = DEFAULT_ADB_PORT; + if (server_port_str && strlen(server_port_str) > 0) { + server_port = (int) strtol(server_port_str, NULL, 0); + if (server_port <= 0) { + fprintf(stderr, + "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number. Got \"%s\"\n", + server_port_str); + return usage(); + } + } + + /* modifiers and flags */ while(argc > 0) { - if(!strcmp(argv[0],"nodaemon")) { + if(!strcmp(argv[0],"server")) { + is_server = 1; + } else if(!strcmp(argv[0],"nodaemon")) { no_daemon = 1; } else if (!strcmp(argv[0], "fork-server")) { /* this is a special flag used only when the ADB client launches the ADB Server */ @@ -803,7 +890,7 @@ int adb_commandline(int argc, char **argv) if (isdigit(argv[0][2])) { serial = argv[0] + 2; } else { - if(argc < 2) return usage(); + if(argc < 2 || argv[0][2] != '\0') return usage(); serial = argv[1]; argc--; argv++; @@ -821,12 +908,13 @@ int adb_commandline(int argc, char **argv) } adb_set_transport(ttype, serial); + adb_set_tcp_specifics(server_port); - if ((argc > 0) && (!strcmp(argv[0],"server"))) { + if (is_server) { if (no_daemon || is_daemon) { - r = adb_main(is_daemon); + r = adb_main(is_daemon, server_port); } else { - r = launch_server(); + r = launch_server(server_port); } if(r) { fprintf(stderr,"* could not start server *\n"); @@ -854,13 +942,33 @@ int adb_commandline(int argc, char **argv) } } - if(!strcmp(argv[0], "connect") || !strcmp(argv[0], "disconnect")) { + if(!strcmp(argv[0], "connect")) { char *tmp; if (argc != 2) { - fprintf(stderr, "Usage: adb %s :\n", argv[0]); + fprintf(stderr, "Usage: adb connect [:]\n"); return 1; } - snprintf(buf, sizeof buf, "host:%s:%s", argv[0], argv[1]); + snprintf(buf, sizeof buf, "host:connect:%s", argv[1]); + tmp = adb_query(buf); + if(tmp) { + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + if(!strcmp(argv[0], "disconnect")) { + char *tmp; + if (argc > 2) { + fprintf(stderr, "Usage: adb disconnect [[:]]\n"); + return 1; + } + if (argc == 2) { + snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]); + } else { + snprintf(buf, sizeof buf, "host:disconnect:"); + } tmp = adb_query(buf); if(tmp) { printf("%s\n", tmp); @@ -874,12 +982,25 @@ int adb_commandline(int argc, char **argv) return adb_send_emulator_command(argc, argv); } - if(!strcmp(argv[0], "shell")) { + if(!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) { int r; int fd; + char h = (argv[0][0] == 'h'); + + if (h) { + printf("\x1b[41;33m"); + fflush(stdout); + } + if(argc < 2) { - return interactive_shell(); + D("starting interactive shell\n"); + r = interactive_shell(); + if (h) { + printf("\x1b[0m"); + fflush(stdout); + } + return r; } snprintf(buf, sizeof buf, "shell:%s", argv[1]); @@ -891,16 +1012,19 @@ int adb_commandline(int argc, char **argv) /* quote empty strings and strings with spaces */ quote = (**argv == 0 || strchr(*argv, ' ')); if (quote) - strcat(buf, "\""); + strcat(buf, "\""); strcat(buf, *argv++); if (quote) - strcat(buf, "\""); + strcat(buf, "\""); } for(;;) { + D("interactive shell loop. buff=%s\n", buf); fd = adb_connect(buf); if(fd >= 0) { + D("about to read_and_dump(fd=%d)\n", fd); read_and_dump(fd); + D("read_and_dump() done.\n"); adb_close(fd); r = 0; } else { @@ -913,6 +1037,11 @@ int adb_commandline(int argc, char **argv) adb_sleep_ms(1000); do_cmd(ttype, serial, "wait-for-device", 0); } else { + if (h) { + printf("\x1b[0m"); + fflush(stdout); + } + D("interactive shell loop. return r=%d\n", r); return r; } } @@ -929,10 +1058,13 @@ int adb_commandline(int argc, char **argv) } if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot") + || !strcmp(argv[0], "reboot-bootloader") || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb") || !strcmp(argv[0], "root")) { char command[100]; - if (argc > 1) + if (!strcmp(argv[0], "reboot-bootloader")) + snprintf(command, sizeof(command), "reboot:bootloader"); + else if (argc > 1) snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]); else snprintf(command, sizeof(command), "%s:", argv[0]); @@ -947,10 +1079,8 @@ int adb_commandline(int argc, char **argv) } if(!strcmp(argv[0], "bugreport")) { - if (argc != 1) { - return 1; - } - do_cmd(ttype, serial, "shell", "dumpstate", "-", 0); + if (argc != 1) return usage(); + do_cmd(ttype, serial, "shell", "bugreport", 0); return 0; } @@ -990,9 +1120,13 @@ int adb_commandline(int argc, char **argv) if(!strcmp(argv[0], "forward")) { if(argc != 3) return usage(); if (serial) { - snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial,argv[1],argv[2]); + snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial, argv[1], argv[2]); + } else if (ttype == kTransportUsb) { + snprintf(buf, sizeof buf, "host-usb:forward:%s;%s", argv[1], argv[2]); + } else if (ttype == kTransportLocal) { + snprintf(buf, sizeof buf, "host-local:forward:%s;%s", argv[1], argv[2]); } else { - snprintf(buf, sizeof buf, "host:forward:%s;%s",argv[1],argv[2]); + snprintf(buf, sizeof buf, "host:forward:%s;%s", argv[1], argv[2]); } if(adb_command(buf)) { fprintf(stderr,"error: %s\n", adb_error()); @@ -1014,8 +1148,13 @@ int adb_commandline(int argc, char **argv) } if(!strcmp(argv[0], "pull")) { - if(argc != 3) return usage(); - return do_sync_pull(argv[1], argv[2]); + if (argc == 2) { + return do_sync_pull(argv[1], "."); + } else if (argc == 3) { + return do_sync_pull(argv[1], argv[2]); + } else { + return usage(); + } } if(!strcmp(argv[0], "install")) { @@ -1030,10 +1169,19 @@ int adb_commandline(int argc, char **argv) if(!strcmp(argv[0], "sync")) { char *srcarg, *android_srcpath, *data_srcpath; + int listonly = 0; + int ret; if(argc < 2) { /* No local path was specified. */ srcarg = NULL; + } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) { + listonly = 1; + if (argc == 3) { + srcarg = argv[2]; + } else { + srcarg = NULL; + } } else if(argc == 2) { /* A local path or "android"/"data" arg was specified. */ srcarg = argv[1]; @@ -1044,9 +1192,9 @@ int adb_commandline(int argc, char **argv) if(ret != 0) return usage(); if(android_srcpath != NULL) - ret = do_sync_sync(android_srcpath, "/system"); + ret = do_sync_sync(android_srcpath, "/system", listonly); if(ret == 0 && data_srcpath != NULL) - ret = do_sync_sync(data_srcpath, "/data"); + ret = do_sync_sync(data_srcpath, "/data", listonly); free(android_srcpath); free(data_srcpath); @@ -1089,6 +1237,14 @@ int adb_commandline(int argc, char **argv) return adb_connect("host:start-server"); } + if (!strcmp(argv[0], "backup")) { + return backup(argc, argv); + } + + if (!strcmp(argv[0], "restore")) { + return restore(argc, argv); + } + if (!strcmp(argv[0], "jdwp")) { int fd = adb_connect("jdwp"); if (fd >= 0) { @@ -1240,42 +1396,115 @@ static int delete_file(transport_type transport, char* serial, char* filename) return 0; } -int install_app(transport_type transport, char* serial, int argc, char** argv) +static const char* get_basename(const char* filename) { - struct stat st; - int err; - const char *const WHERE = "/data/local/tmp/%s"; - char to[PATH_MAX]; - char* filename = argv[argc - 1]; - const char* p; - - p = adb_dirstop(filename); - if (p) { - p++; - snprintf(to, sizeof to, WHERE, p); + const char* basename = adb_dirstop(filename); + if (basename) { + basename++; + return basename; } else { - snprintf(to, sizeof to, WHERE, filename); + return filename; } - if (p[0] == '\0') { +} + +static int check_file(const char* filename) +{ + struct stat st; + + if (filename == NULL) { + return 0; } - err = stat(filename, &st); - if (err != 0) { + if (stat(filename, &st) != 0) { fprintf(stderr, "can't find '%s' to install\n", filename); return 1; } + if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "can't install '%s' because it's not a file\n", - filename); + fprintf(stderr, "can't install '%s' because it's not a file\n", filename); + return 1; + } + + return 0; +} + +int install_app(transport_type transport, char* serial, int argc, char** argv) +{ + static const char *const DATA_DEST = "/data/local/tmp/%s"; + static const char *const SD_DEST = "/sdcard/tmp/%s"; + const char* where = DATA_DEST; + char apk_dest[PATH_MAX]; + char verification_dest[PATH_MAX]; + char* apk_file; + char* verification_file = NULL; + int file_arg = -1; + int err; + int i; + + for (i = 1; i < argc; i++) { + if (*argv[i] != '-') { + file_arg = i; + break; + } else if (!strcmp(argv[i], "-i")) { + // Skip the installer package name. + i++; + } else if (!strcmp(argv[i], "-s")) { + where = SD_DEST; + } + } + + if (file_arg < 0) { + fprintf(stderr, "can't find filename in arguments\n"); + return 1; + } else if (file_arg + 2 < argc) { + fprintf(stderr, "too many files specified; only takes APK file and verifier file\n"); + return 1; + } + + apk_file = argv[file_arg]; + + if (file_arg != argc - 1) { + verification_file = argv[file_arg + 1]; + } + + if (check_file(apk_file) || check_file(verification_file)) { return 1; } - if (!(err = do_sync_push(filename, to, 1 /* verify APK */))) { - /* file in place; tell the Package Manager to install it */ - argv[argc - 1] = to; /* destination name, not source location */ - pm_command(transport, serial, argc, argv); - delete_file(transport, serial, to); + snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); + if (verification_file != NULL) { + snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file)); + + if (!strcmp(apk_dest, verification_dest)) { + fprintf(stderr, "APK and verification file can't have the same name\n"); + return 1; + } } + err = do_sync_push(apk_file, apk_dest, 1 /* verify APK */); + if (err) { + return err; + } else { + argv[file_arg] = apk_dest; /* destination name, not source location */ + } + + if (verification_file != NULL) { + err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */); + if (err) { + goto cleanup_apk; + } else { + argv[file_arg + 1] = verification_dest; /* destination name, not source location */ + } + } + + pm_command(transport, serial, argc, argv); + + if (verification_file != NULL) { + delete_file(transport, serial, verification_dest); + } + +cleanup_apk: + delete_file(transport, serial, apk_dest); + return err; } diff --git a/adb/fdevent.c b/adb/fdevent.c index c179b201d32..5c374a71bff 100644 --- a/adb/fdevent.c +++ b/adb/fdevent.c @@ -15,6 +15,8 @@ ** limitations under the License. */ +#include + #include #include #include @@ -27,10 +29,20 @@ #include #include "fdevent.h" +#include "transport.h" +#include "sysdeps.h" + -#define TRACE(x...) fprintf(stderr,x) +/* !!! Do not enable DEBUG for the adb that will run as the server: +** both stdout and stderr are used to communicate between the client +** and server. Any extra output will cause failures. +*/ +#define DEBUG 0 /* non-0 will break adb server */ -#define DEBUG 0 +// This socket is used when a subproc shell service exists. +// It wakes up the fdevent_loop() and cause the correct handling +// of the shell's pseudo-tty master. I.e. force close it. +int SHELL_EXIT_NOTIFY_FD = -1; static void fatal(const char *fn, const char *fmt, ...) { @@ -45,15 +57,28 @@ static void fatal(const char *fn, const char *fmt, ...) #define FATAL(x...) fatal(__FUNCTION__, x) #if DEBUG +#define D(...) \ + do { \ + adb_mutex_lock(&D_lock); \ + int save_errno = errno; \ + fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } while(0) static void dump_fde(fdevent *fde, const char *info) { + adb_mutex_lock(&D_lock); fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd, fde->state & FDE_READ ? 'R' : ' ', fde->state & FDE_WRITE ? 'W' : ' ', fde->state & FDE_ERROR ? 'E' : ' ', info); + adb_mutex_unlock(&D_lock); } #else +#define D(...) ((void)0) #define dump_fde(fde, info) do { } while(0) #endif @@ -67,6 +92,7 @@ static void dump_fde(fdevent *fde, const char *info) static void fdevent_plist_enqueue(fdevent *node); static void fdevent_plist_remove(fdevent *node); static fdevent *fdevent_plist_dequeue(void); +static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata); static fdevent list_pending = { .next = &list_pending, @@ -270,9 +296,72 @@ static void fdevent_update(fdevent *fde, unsigned events) FD_CLR(fde->fd, &error_fds); } - fde->state = (fde->state & FDE_STATEMASK) | events; + fde->state = (fde->state & FDE_STATEMASK) | events; +} + +/* Looks at fd_table[] for bad FDs and sets bit in fds. +** Returns the number of bad FDs. +*/ +static int fdevent_fd_check(fd_set *fds) +{ + int i, n = 0; + fdevent *fde; + + for(i = 0; i < select_n; i++) { + fde = fd_table[i]; + if(fde == 0) continue; + if(fcntl(i, F_GETFL, NULL) < 0) { + FD_SET(i, fds); + n++; + // fde->state |= FDE_DONT_CLOSE; + + } + } + return n; } +#if !DEBUG +static inline void dump_all_fds(const char *extra_msg) {} +#else +static void dump_all_fds(const char *extra_msg) +{ +int i; + fdevent *fde; + // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank + char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff; + size_t max_chars = FD_SETSIZE * 6 + 1; + int printed_out; +#define SAFE_SPRINTF(...) \ + do { \ + printed_out = snprintf(pb, max_chars, __VA_ARGS__); \ + if (printed_out <= 0) { \ + D("... snprintf failed.\n"); \ + return; \ + } \ + if (max_chars < (unsigned int)printed_out) { \ + D("... snprintf out of space.\n"); \ + return; \ + } \ + pb += printed_out; \ + max_chars -= printed_out; \ + } while(0) + + for(i = 0; i < select_n; i++) { + fde = fd_table[i]; + SAFE_SPRINTF("%d", i); + if(fde == 0) { + SAFE_SPRINTF("? "); + continue; + } + if(fcntl(i, F_GETFL, NULL) < 0) { + SAFE_SPRINTF("b"); + } + SAFE_SPRINTF(" "); + } + D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff); +} +#endif + static void fdevent_process() { int i, n; @@ -284,28 +373,49 @@ static void fdevent_process() memcpy(&wfd, &write_fds, sizeof(fd_set)); memcpy(&efd, &error_fds, sizeof(fd_set)); - n = select(select_n, &rfd, &wfd, &efd, 0); + dump_all_fds("pre select()"); + + n = select(select_n, &rfd, &wfd, &efd, NULL); + int saved_errno = errno; + D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0); + + dump_all_fds("post select()"); if(n < 0) { - if(errno == EINTR) return; - perror("select"); - return; + switch(saved_errno) { + case EINTR: return; + case EBADF: + // Can't trust the FD sets after an error. + FD_ZERO(&wfd); + FD_ZERO(&efd); + FD_ZERO(&rfd); + break; + default: + D("Unexpected select() error=%d\n", saved_errno); + return; + } + } + if(n <= 0) { + // We fake a read, as the rest of the code assumes + // that errors will be detected at that point. + n = fdevent_fd_check(&rfd); } for(i = 0; (i < select_n) && (n > 0); i++) { events = 0; - if(FD_ISSET(i, &rfd)) events |= FDE_READ; - if(FD_ISSET(i, &wfd)) events |= FDE_WRITE; - if(FD_ISSET(i, &efd)) events |= FDE_ERROR; + if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; } + if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; } + if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; } if(events) { - n--; - fde = fd_table[i]; - if(fde == 0) FATAL("missing fde for fd %d\n", i); + if(fde == 0) + FATAL("missing fde for fd %d\n", i); fde->events |= events; + D("got events fde->fd=%d events=%04x, state=%04x\n", + fde->fd, fde->events, fde->state); if(fde->state & FDE_PENDING) continue; fde->state |= FDE_PENDING; fdevent_plist_enqueue(fde); @@ -350,14 +460,14 @@ static void fdevent_unregister(fdevent *fde) } if(fd_table[fde->fd] != fde) { - FATAL("fd_table out of sync"); + FATAL("fd_table out of sync [%d]\n", fde->fd); } fd_table[fde->fd] = 0; if(!(fde->state & FDE_DONT_CLOSE)) { dump_fde(fde, "close"); - close(fde->fd); + adb_close(fde->fd); } } @@ -394,6 +504,74 @@ static fdevent *fdevent_plist_dequeue(void) return node; } +static void fdevent_call_fdfunc(fdevent* fde) +{ + unsigned events = fde->events; + fde->events = 0; + if(!(fde->state & FDE_PENDING)) return; + fde->state &= (~FDE_PENDING); + dump_fde(fde, "callback"); + fde->func(fde->fd, events, fde->arg); +} + +static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata) +{ + + D("subproc handling on fd=%d ev=%04x\n", fd, ev); + + // Hook oneself back into the fde's suitable for select() on read. + if((fd < 0) || (fd >= fd_table_max)) { + FATAL("fd %d out of range for fd_table \n", fd); + } + fdevent *fde = fd_table[fd]; + fdevent_add(fde, FDE_READ); + + if(ev & FDE_READ){ + int subproc_fd; + + if(readx(fd, &subproc_fd, sizeof(subproc_fd))) { + FATAL("Failed to read the subproc's fd from fd=%d\n", fd); + } + if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) { + D("subproc_fd %d out of range 0, fd_table_max=%d\n", + subproc_fd, fd_table_max); + return; + } + fdevent *subproc_fde = fd_table[subproc_fd]; + if(!subproc_fde) { + D("subproc_fd %d cleared from fd_table\n", subproc_fd); + return; + } + if(subproc_fde->fd != subproc_fd) { + // Already reallocated? + D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd); + return; + } + + subproc_fde->force_eof = 1; + + int rcount = 0; + ioctl(subproc_fd, FIONREAD, &rcount); + D("subproc with fd=%d has rcount=%d err=%d\n", + subproc_fd, rcount, errno); + + if(rcount) { + // If there is data left, it will show up in the select(). + // This works because there is no other thread reading that + // data when in this fd_func(). + return; + } + + D("subproc_fde.state=%04x\n", subproc_fde->state); + subproc_fde->events |= FDE_READ; + if(subproc_fde->state & FDE_PENDING) { + return; + } + subproc_fde->state |= FDE_PENDING; + fdevent_call_fdfunc(subproc_fde); + } +} + fdevent *fdevent_create(int fd, fd_func func, void *arg) { fdevent *fde = (fdevent*) malloc(sizeof(fdevent)); @@ -412,11 +590,12 @@ void fdevent_destroy(fdevent *fde) fdevent_remove(fde); } -void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) { memset(fde, 0, sizeof(fdevent)); fde->state = FDE_ACTIVE; fde->fd = fd; + fde->force_eof = 0; fde->func = func; fde->arg = arg; @@ -437,7 +616,7 @@ void fdevent_remove(fdevent *fde) if(fde->state & FDE_ACTIVE) { fdevent_disconnect(fde); - dump_fde(fde, "disconnect"); + dump_fde(fde, "disconnect"); fdevent_unregister(fde); } @@ -484,23 +663,33 @@ void fdevent_del(fdevent *fde, unsigned events) fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK))); } +void fdevent_subproc_setup() +{ + int s[2]; + + if(adb_socketpair(s)) { + FATAL("cannot create shell-exit socket-pair\n"); + } + SHELL_EXIT_NOTIFY_FD = s[0]; + fdevent *fde; + fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL); + if(!fde) + FATAL("cannot create fdevent for shell-exit handler\n"); + fdevent_add(fde, FDE_READ); +} + void fdevent_loop() { fdevent *fde; + fdevent_subproc_setup(); for(;;) { -#if DEBUG - fprintf(stderr,"--- ---- waiting for events\n"); -#endif + D("--- ---- waiting for events\n"); + fdevent_process(); while((fde = fdevent_plist_dequeue())) { - unsigned events = fde->events; - fde->events = 0; - fde->state &= (~FDE_PENDING); - dump_fde(fde, "callback"); - fde->func(fde->fd, events, fde->arg); + fdevent_call_fdfunc(fde); } } } - diff --git a/adb/fdevent.h b/adb/fdevent.h index 6b7e7ec133e..a0ebe2a7ee2 100644 --- a/adb/fdevent.h +++ b/adb/fdevent.h @@ -70,6 +70,8 @@ struct fdevent fdevent *prev; int fd; + int force_eof; + unsigned short state; unsigned short events; diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index 0ebfe73958f..64e393cc852 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -57,9 +57,9 @@ static void END() if (t == 0) /* prevent division by 0 :-) */ t = 1000000; - fprintf(stderr,"%lld KB/s (%d bytes in %lld.%03llds)\n", + fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n", ((((long long) total_bytes) * 1000000LL) / t) / 1024LL, - total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); + (long long) total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); } void sync_quit(int fd) @@ -641,8 +641,9 @@ static int local_build_list(copyinfo **filelist, } else { ci = mkcopyinfo(lpath, rpath, name, 0); if(lstat(ci->src, &st)) { - closedir(d); fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); + closedir(d); + return -1; } if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { @@ -670,7 +671,7 @@ static int local_build_list(copyinfo **filelist, } -static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps) +static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly) { copyinfo *filelist = 0; copyinfo *ci, *next; @@ -718,8 +719,9 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i for(ci = filelist; ci != 0; ci = next) { next = ci->next; if(ci->flag == 0) { - fprintf(stderr,"push: %s -> %s\n", ci->src, ci->dst); - if(sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){ + fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst); + if(!listonly && + sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){ return 1; } pushed++; @@ -757,7 +759,7 @@ int do_sync_push(const char *lpath, const char *rpath, int verifyApk) if(S_ISDIR(st.st_mode)) { BEGIN(); - if(copy_local_dir_remote(fd, lpath, rpath, 0)) { + if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) { return 1; } else { END(); @@ -959,7 +961,7 @@ int do_sync_pull(const char *rpath, const char *lpath) return 1; } - if(S_ISREG(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { + if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { if(stat(lpath, &st) == 0) { if(S_ISDIR(st.st_mode)) { /* if we're copying a remote file to a local directory, @@ -1001,7 +1003,7 @@ int do_sync_pull(const char *rpath, const char *lpath) } } -int do_sync_sync(const char *lpath, const char *rpath) +int do_sync_sync(const char *lpath, const char *rpath, int listonly) { fprintf(stderr,"syncing %s...\n",rpath); @@ -1012,7 +1014,7 @@ int do_sync_sync(const char *lpath, const char *rpath) } BEGIN(); - if(copy_local_dir_remote(fd, lpath, rpath, 1)){ + if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){ return 1; } else { END(); diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index a231e93d624..d3e841b2d5e 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -193,9 +193,11 @@ static int handle_send_file(int s, char *path, mode_t mode, char *buffer) if(fd < 0) continue; if(writex(fd, buffer, len)) { + int saved_errno = errno; adb_close(fd); adb_unlink(path); fd = -1; + errno = saved_errno; if(fail_errno(s)) return -1; } } diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h index 4ee40ba24ef..e402e068263 100644 --- a/adb/file_sync_service.h +++ b/adb/file_sync_service.h @@ -17,7 +17,7 @@ #ifndef _FILE_SYNC_SERVICE_H_ #define _FILE_SYNC_SERVICE_H_ -#ifdef __ppc__ +#ifdef HAVE_BIG_ENDIAN static inline unsigned __swap_uint32(unsigned x) { return (((x) & 0xFF000000) >> 24) @@ -79,7 +79,7 @@ typedef union { void file_sync_service(int fd, void *cookie); int do_sync_ls(const char *path); int do_sync_push(const char *lpath, const char *rpath, int verifyApk); -int do_sync_sync(const char *lpath, const char *rpath); +int do_sync_sync(const char *lpath, const char *rpath, int listonly); int do_sync_pull(const char *rpath, const char *lpath); #define SYNC_DATA_MAX (64*1024) diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c index 2f456947082..862dd91c84b 100644 --- a/adb/framebuffer_service.c +++ b/adb/framebuffer_service.c @@ -51,56 +51,125 @@ struct fbinfo { void framebuffer_service(int fd, void *cookie) { - struct fb_var_screeninfo vinfo; - int fb, offset; - char x[256]; - struct fbinfo fbinfo; - unsigned i, bytespp; - - fb = open("/dev/graphics/fb0", O_RDONLY); - if(fb < 0) goto done; + unsigned int i; + char buf[640]; + int fd_screencap; + int w, h, f; + int fds[2]; + + if (pipe(fds) < 0) goto done; + + pid_t pid = fork(); + if (pid < 0) goto done; + + if (pid == 0) { + dup2(fds[1], STDOUT_FILENO); + close(fds[0]); + close(fds[1]); + const char* command = "screencap"; + const char *args[2] = {command, NULL}; + execvp(command, (char**)args); + exit(1); + } - if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done; - fcntl(fb, F_SETFD, FD_CLOEXEC); + fd_screencap = fds[0]; - bytespp = vinfo.bits_per_pixel / 8; + /* read w, h & format */ + if(readx(fd_screencap, &w, 4)) goto done; + if(readx(fd_screencap, &h, 4)) goto done; + if(readx(fd_screencap, &f, 4)) goto done; fbinfo.version = DDMS_RAWIMAGE_VERSION; - fbinfo.bpp = vinfo.bits_per_pixel; - fbinfo.size = vinfo.xres * vinfo.yres * bytespp; - fbinfo.width = vinfo.xres; - fbinfo.height = vinfo.yres; - fbinfo.red_offset = vinfo.red.offset; - fbinfo.red_length = vinfo.red.length; - fbinfo.green_offset = vinfo.green.offset; - fbinfo.green_length = vinfo.green.length; - fbinfo.blue_offset = vinfo.blue.offset; - fbinfo.blue_length = vinfo.blue.length; - fbinfo.alpha_offset = vinfo.transp.offset; - fbinfo.alpha_length = vinfo.transp.length; - - /* HACK: for several of our 3d cores a specific alignment - * is required so the start of the fb may not be an integer number of lines - * from the base. As a result we are storing the additional offset in - * xoffset. This is not the correct usage for xoffset, it should be added - * to each line, not just once at the beginning */ - offset = vinfo.xoffset * bytespp; - - offset += vinfo.xres * vinfo.yoffset * bytespp; + /* see hardware/hardware.h */ + switch (f) { + case 1: /* RGBA_8888 */ + fbinfo.bpp = 32; + fbinfo.size = w * h * 4; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 0; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 16; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 8; + break; + case 2: /* RGBX_8888 */ + fbinfo.bpp = 32; + fbinfo.size = w * h * 4; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 0; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 16; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 0; + break; + case 3: /* RGB_888 */ + fbinfo.bpp = 24; + fbinfo.size = w * h * 3; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 0; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 16; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 0; + break; + case 4: /* RGB_565 */ + fbinfo.bpp = 16; + fbinfo.size = w * h * 2; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 11; + fbinfo.red_length = 5; + fbinfo.green_offset = 5; + fbinfo.green_length = 6; + fbinfo.blue_offset = 0; + fbinfo.blue_length = 5; + fbinfo.alpha_offset = 0; + fbinfo.alpha_length = 0; + break; + case 5: /* BGRA_8888 */ + fbinfo.bpp = 32; + fbinfo.size = w * h * 4; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 16; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 0; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 8; + break; + default: + goto done; + } + /* write header */ if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done; - lseek(fb, offset, SEEK_SET); - for(i = 0; i < fbinfo.size; i += 256) { - if(readx(fb, &x, 256)) goto done; - if(writex(fd, &x, 256)) goto done; + /* write data */ + for(i = 0; i < fbinfo.size; i += sizeof(buf)) { + if(readx(fd_screencap, buf, sizeof(buf))) goto done; + if(writex(fd, buf, sizeof(buf))) goto done; } - - if(readx(fb, &x, fbinfo.size % 256)) goto done; - if(writex(fd, &x, fbinfo.size % 256)) goto done; + if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done; + if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done; done: - if(fb >= 0) close(fb); + close(fds[0]); + close(fds[1]); close(fd); } diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c index 6125cb427ab..5b95d1534c7 100644 --- a/adb/get_my_path_darwin.c +++ b/adb/get_my_path_darwin.c @@ -17,7 +17,7 @@ #import #include -void get_my_path(char s[PATH_MAX]) +void get_my_path(char *s, size_t maxLen) { ProcessSerialNumber psn; GetCurrentProcess(&psn); @@ -25,6 +25,6 @@ void get_my_path(char s[PATH_MAX]) dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8); + CFStringGetCString(value, s, maxLen, kCFStringEncodingUTF8); } diff --git a/adb/get_my_path_freebsd.c b/adb/get_my_path_freebsd.c new file mode 100644 index 00000000000..b06ec6694f6 --- /dev/null +++ b/adb/get_my_path_freebsd.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 bsdroid project + * Alexey Tarasov + * + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +void +get_my_path(char *exe, size_t maxLen) +{ + char proc[64]; + + snprintf(proc, sizeof(proc), "/proc/%d/file", getpid()); + + int err = readlink(proc, exe, maxLen - 1); + + exe[err > 0 ? err : 0] = '\0'; +} + diff --git a/adb/get_my_path_linux.c b/adb/get_my_path_linux.c index f516e591d2a..179c3ddae11 100644 --- a/adb/get_my_path_linux.c +++ b/adb/get_my_path_linux.c @@ -19,15 +19,15 @@ #include #include -void get_my_path(char exe[PATH_MAX]) +void get_my_path(char *exe, size_t maxLen) { char proc[64]; snprintf(proc, sizeof proc, "/proc/%d/exe", getpid()); - int err = readlink(proc, exe, PATH_MAX - 1); + int err = readlink(proc, exe, maxLen - 1); if(err > 0) { - exe[err] = 0; + exe[err] = '\0'; } else { - exe[0] = 0; + exe[0] = '\0'; } } diff --git a/adb/get_my_path_windows.c b/adb/get_my_path_windows.c index fc7143c107b..ddf28161844 100644 --- a/adb/get_my_path_windows.c +++ b/adb/get_my_path_windows.c @@ -18,14 +18,17 @@ #include #include -void get_my_path(char exe[PATH_MAX]) +void get_my_path(char *exe, size_t maxLen) { - char* r; + char *r; - GetModuleFileName( NULL, exe, PATH_MAX-1 ); - exe[PATH_MAX-1] = 0; - r = strrchr( exe, '\\' ); - if (r) - *r = 0; + /* XXX: should be GetModuleFileNameA */ + if (GetModuleFileName(NULL, exe, maxLen) > 0) { + r = strrchr(exe, '\\'); + if (r != NULL) + *r = '\0'; + } else { + exe[0] = '\0'; + } } diff --git a/adb/history.h b/adb/history.h deleted file mode 100755 index ef86ad9b1d0..00000000000 --- a/adb/history.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _HISTORY_H_ -#define _HISTORY_H_ - -#define SH_ARROW_ANY "\x1b\x5b" -#define SH_ARROW_UP '\x41' -#define SH_ARROW_DOWN '\x42' -#define SH_ARROW_RIGHT '\x43' -#define SH_ARROW_LEFT '\x44' -#define SH_DEL_CHAR '\x7F' -#define SH_BLANK_CHAR '\x20' - -#endif - diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c index 0c26f7b66b1..cd62b552a9f 100644 --- a/adb/jdwp_service.c +++ b/adb/jdwp_service.c @@ -5,6 +5,7 @@ #include #include #include +#include /* here's how these things work. @@ -320,6 +321,7 @@ jdwp_process_event( int socket, unsigned events, void* _proc ) struct iovec iov; char dummy = '!'; char buffer[sizeof(struct cmsghdr) + sizeof(int)]; + int flags; iov.iov_base = &dummy; iov.iov_len = 1; @@ -337,10 +339,27 @@ jdwp_process_event( int socket, unsigned events, void* _proc ) cmsg->cmsg_type = SCM_RIGHTS; ((int*)CMSG_DATA(cmsg))[0] = fd; + flags = fcntl(proc->socket,F_GETFL,0); + + if (flags == -1) { + D("failed to get cntl flags for socket %d: %s\n", + proc->pid, strerror(errno)); + goto CloseProcess; + + } + + if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) { + D("failed to remove O_NONBLOCK flag for socket %d: %s\n", + proc->pid, strerror(errno)); + goto CloseProcess; + } + for (;;) { ret = sendmsg(proc->socket, &msg, 0); - if (ret >= 0) + if (ret >= 0) { + adb_close(fd); break; + } if (errno == EINTR) continue; D("sending new file descriptor to JDWP %d failed: %s\n", @@ -354,6 +373,12 @@ jdwp_process_event( int socket, unsigned events, void* _proc ) for (n = 1; n < proc->out_count; n++) proc->out_fds[n-1] = proc->out_fds[n]; + if (fcntl(proc->socket, F_SETFL, flags) == -1) { + D("failed to set O_NONBLOCK flag for socket %d: %s\n", + proc->pid, strerror(errno)); + goto CloseProcess; + } + if (--proc->out_count == 0) fdevent_del( proc->fde, FDE_WRITE ); } @@ -474,6 +499,7 @@ jdwp_control_init( JdwpControl* control, /* only wait for incoming connections */ fdevent_add(control->fde, FDE_READ); + close_on_exec(s); D("jdwp control socket started (%d)\n", control->listen_socket); return 0; diff --git a/adb/mutex_list.h b/adb/mutex_list.h index eebe0dfcbbd..652dd7341ca 100644 --- a/adb/mutex_list.h +++ b/adb/mutex_list.h @@ -1,8 +1,11 @@ -/* the list of mutexes used by addb */ +/* the list of mutexes used by adb */ +/* #ifndef __MUTEX_LIST_H + * Do not use an include-guard. This file is included once to declare the locks + * and once in win32 to actually do the runtime initialization. + */ #ifndef ADB_MUTEX #error ADB_MUTEX not defined when including this file #endif - ADB_MUTEX(dns_lock) ADB_MUTEX(socket_list_lock) ADB_MUTEX(transport_lock) @@ -11,4 +14,13 @@ ADB_MUTEX(local_transports_lock) #endif ADB_MUTEX(usb_lock) +// Sadly logging to /data/adb/adb-... is not thread safe. +// After modifying adb.h::D() to count invocations: +// DEBUG(jpa):0:Handling main() +// DEBUG(jpa):1:[ usb_init - starting thread ] +// (Oopsies, no :2:, and matching message is also gone.) +// DEBUG(jpa):3:[ usb_thread - opening device ] +// DEBUG(jpa):4:jdwp control socket started (10) +ADB_MUTEX(D_lock) + #undef ADB_MUTEX diff --git a/adb/remount_service.c b/adb/remount_service.c index 26bc841c942..4cb41e7d190 100644 --- a/adb/remount_service.c +++ b/adb/remount_service.c @@ -30,19 +30,19 @@ static int system_ro = 1; -/* Returns the mount number of the requested partition from /proc/mtd */ -static int find_mount(const char *findme) +/* Returns the device used to mount a directory in /proc/mounts */ +static char *find_mount(const char *dir) { int fd; int res; int size; char *token = NULL; const char delims[] = "\n"; - char buf[1024]; + char buf[4096]; - fd = unix_open("/proc/mtd", O_RDONLY); + fd = unix_open("/proc/mounts", O_RDONLY); if (fd < 0) - return -errno; + return NULL; buf[sizeof(buf) - 1] = '\0'; size = adb_read(fd, buf, sizeof(buf) - 1); @@ -51,33 +51,41 @@ static int find_mount(const char *findme) token = strtok(buf, delims); while (token) { - char mtdname[16]; - int mtdnum, mtdsize, mtderasesize; - - res = sscanf(token, "mtd%d: %x %x %15s", - &mtdnum, &mtdsize, &mtderasesize, mtdname); - - if (res == 4 && !strcmp(mtdname, findme)) - return mtdnum; + char mount_dev[256]; + char mount_dir[256]; + int mount_freq; + int mount_passno; + + res = sscanf(token, "%255s %255s %*s %*s %d %d\n", + mount_dev, mount_dir, &mount_freq, &mount_passno); + mount_dev[255] = 0; + mount_dir[255] = 0; + if (res == 4 && (strcmp(dir, mount_dir) == 0)) + return strdup(mount_dev); token = strtok(NULL, delims); } - return -1; + return NULL; } /* Init mounts /system as read only, remount to enable writes. */ static int remount_system() { - int num; - char source[64]; + char *dev; + if (system_ro == 0) { return 0; } - if ((num = find_mount("\"system\"")) < 0) + + dev = find_mount("/system"); + + if (!dev) return -1; - snprintf(source, sizeof source, "/dev/block/mtdblock%d", num); - system_ro = mount(source, "/system", "yaffs2", MS_REMOUNT, NULL); + system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL); + + free(dev); + return system_ro; } diff --git a/adb/services.c b/adb/services.c index b5df5542b93..0fae9a2c9a0 100644 --- a/adb/services.c +++ b/adb/services.c @@ -22,7 +22,7 @@ #include "sysdeps.h" -#define TRACE_TAG TRACE_ADB +#define TRACE_TAG TRACE_SERVICES #include "adb.h" #include "file_sync_service.h" @@ -30,9 +30,10 @@ # ifndef HAVE_WINSOCK # include # include +# include # endif #else -# include +# include #endif typedef struct stinfo stinfo; @@ -110,6 +111,8 @@ void restart_root_service(int fd, void *cookie) { char buf[100]; char value[PROPERTY_VALUE_MAX]; + char build_type[PROPERTY_VALUE_MAX]; + char cm_version[PROPERTY_VALUE_MAX]; if (getuid() == 0) { snprintf(buf, sizeof(buf), "adbd is already running as root\n"); @@ -124,14 +127,23 @@ void restart_root_service(int fd, void *cookie) return; } - property_set("service.adb.root", "1"); + property_get("persist.sys.root_access", value, "1"); + property_get("ro.build.type", build_type, ""); + property_get("ro.cm.version", cm_version, ""); + + if (strlen(cm_version) > 0 && strcmp(build_type, "eng") != 0 && (atoi(value) & 2) != 2) { + snprintf(buf, sizeof(buf), "root access is disabled by system setting - enable in settings -> development options\n"); + writex(fd, buf, strlen(buf)); + adb_close(fd); + return; + } + snprintf(buf, sizeof(buf), "restarting adbd as root\n"); writex(fd, buf, strlen(buf)); adb_close(fd); - // quit, and init will restart us as root - sleep(1); - exit(1); + // This will cause a property trigger in init.rc to restart us + property_set("service.adb.root", "1"); } } @@ -176,11 +188,24 @@ void restart_usb_service(int fd, void *cookie) void reboot_service(int fd, void *arg) { char buf[100]; - int ret; + int pid, ret; sync(); - ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, - LINUX_REBOOT_CMD_RESTART2, (char *)arg); + + /* Attempt to unmount the SD card first. + * No need to bother checking for errors. + */ + pid = fork(); + if (pid == 0) { + /* ask vdc to unmount it */ + execl("/system/bin/vdc", "/system/bin/vdc", "volume", "unmount", + getenv("EXTERNAL_STORAGE"), "force", NULL); + } else if (pid > 0) { + /* wait until vdc succeeds or fails */ + waitpid(pid, &ret, 0); + } + + ret = android_reboot(ANDROID_RB_RESTART2, 0, (char *) arg); if (ret < 0) { snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno)); writex(fd, buf, strlen(buf)); @@ -254,15 +279,16 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) return s[0]; } -static int create_subprocess(const char *cmd, const char *arg0, const char *arg1) +#if !ADB_HOST +static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) { #ifdef HAVE_WIN32_PROC - fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); - return -1; + D("create_subprocess(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1); + fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); + return -1; #else /* !HAVE_WIN32_PROC */ char *devname; int ptm; - pid_t pid; ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY); if(ptm < 0){ @@ -274,38 +300,38 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 if(grantpt(ptm) || unlockpt(ptm) || ((devname = (char*) ptsname(ptm)) == 0)){ printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno)); + adb_close(ptm); return -1; } - pid = fork(); - if(pid < 0) { + *pid = fork(); + if(*pid < 0) { printf("- fork failed: %s -\n", strerror(errno)); + adb_close(ptm); return -1; } - if(pid == 0){ + if(*pid == 0){ int pts; setsid(); pts = unix_open(devname, O_RDWR); - if(pts < 0) exit(-1); + if(pts < 0) { + fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname); + exit(-1); + } dup2(pts, 0); dup2(pts, 1); dup2(pts, 2); + adb_close(pts); adb_close(ptm); - execl(cmd, cmd, arg0, arg1, NULL); - fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", - cmd, strerror(errno), errno); - exit(-1); - } else { -#if !ADB_HOST - // set child's OOM adjustment to zero + // set OOM adjustment to zero char text[64]; - snprintf(text, sizeof text, "/proc/%d/oom_adj", pid); + snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid()); int fd = adb_open(text, O_WRONLY); if (fd >= 0) { adb_write(fd, "0", 1); @@ -313,16 +339,100 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 } else { D("adb: unable to open %s\n", text); } -#endif + execl(cmd, cmd, arg0, arg1, NULL); + fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", + cmd, strerror(errno), errno); + exit(-1); + } else { + // Don't set child's OOM adjustment to zero. + // Let the child do it itself, as sometimes the parent starts + // running before the child has a /proc/pid/oom_adj. + // """adb: unable to open /proc/644/oom_adj""" seen in some logs. return ptm; } #endif /* !HAVE_WIN32_PROC */ } +#endif /* !ABD_HOST */ #if ADB_HOST #define SHELL_COMMAND "/bin/sh" +#define ALTERNATE_SHELL_COMMAND "" #else #define SHELL_COMMAND "/system/bin/sh" +#define ALTERNATE_SHELL_COMMAND "/sbin/sh" +#endif + +#if !ADB_HOST +static void subproc_waiter_service(int fd, void *cookie) +{ + pid_t pid = (pid_t)cookie; + + D("entered. fd=%d of pid=%d\n", fd, pid); + for (;;) { + int status; + pid_t p = waitpid(pid, &status, 0); + if (p == pid) { + D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status); + if (WIFSIGNALED(status)) { + D("*** Killed by signal %d\n", WTERMSIG(status)); + break; + } else if (!WIFEXITED(status)) { + D("*** Didn't exit!!. status %d\n", status); + break; + } else if (WEXITSTATUS(status) >= 0) { + D("*** Exit code %d\n", WEXITSTATUS(status)); + break; + } + } + usleep(100000); // poll every 0.1 sec + } + D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno); + if (SHELL_EXIT_NOTIFY_FD >=0) { + int res; + res = writex(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)); + D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n", + SHELL_EXIT_NOTIFY_FD, pid, res, errno); + } +} + +static int create_subproc_thread(const char *name) +{ + stinfo *sti; + adb_thread_t t; + int ret_fd; + pid_t pid; + const char* shell_command; + struct stat filecheck; + if (stat(ALTERNATE_SHELL_COMMAND, &filecheck) == 0) { + shell_command = ALTERNATE_SHELL_COMMAND; + } + else { + shell_command = SHELL_COMMAND; + } + + if(name) { + ret_fd = create_subprocess(shell_command, "-c", name, &pid); + } else { + ret_fd = create_subprocess(shell_command, "-", 0, &pid); + } + D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = subproc_waiter_service; + sti->cookie = (void*)pid; + sti->fd = ret_fd; + + if(adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(ret_fd); + printf("cannot create service thread\n"); + return -1; + } + + D("service thread started, fd=%d pid=%d\n",ret_fd, pid); + return ret_fd; +} #endif int service_to_fd(const char *name) @@ -375,14 +485,12 @@ int service_to_fd(const char *name) ret = create_jdwp_connection_fd(atoi(name+5)); } else if (!strncmp(name, "log:", 4)) { ret = create_service_thread(log_service, get_log_file_path(name + 4)); -#endif } else if(!HOST && !strncmp(name, "shell:", 6)) { if(name[6]) { - ret = create_subprocess(SHELL_COMMAND, "-c", name + 6); + ret = create_subproc_thread(name + 6); } else { - ret = create_subprocess(SHELL_COMMAND, "-", 0); + ret = create_subproc_thread(0); } -#if !ADB_HOST } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread(file_sync_service, NULL); } else if(!strncmp(name, "remount:", 8)) { @@ -393,6 +501,12 @@ int service_to_fd(const char *name) ret = create_service_thread(reboot_service, arg); } else if(!strncmp(name, "root:", 5)) { ret = create_service_thread(restart_root_service, NULL); + } else if(!strncmp(name, "backup:", 7)) { + char* arg = strdup(name+7); + if (arg == NULL) return -1; + ret = backup_service(BACKUP, arg); + } else if(!strncmp(name, "restore:", 8)) { + ret = backup_service(RESTORE, NULL); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) == 0) { diff --git a/adb/shlist.c b/adb/shlist.c deleted file mode 100755 index 44919ef0460..00000000000 --- a/adb/shlist.c +++ /dev/null @@ -1,185 +0,0 @@ -/*-------------------------------------------------------------------*/ -/* List Functionality */ -/*-------------------------------------------------------------------*/ -/* #define SH_LIST_DEBUG */ -/*-------------------------------------------------------------------*/ -#include -#include -#include "shlist.h" -/*-------------------------------------------------------------------*/ -void shListInitList( SHLIST *listPtr ) -{ - listPtr->data = (void *)0L; - listPtr->next = listPtr; - listPtr->prev = listPtr; -} - -SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func ) -{ - SHLIST *item; - - for(item=head->next;( item != head );item=item->next) - if( func ) { - if( func( val, item->data ) ) { - return( item ); - } - } - else { - if( item->data == val ) { - return( item ); - } - } - return( NULL ); -} - -SHLIST *shListGetLastItem( SHLIST *head ) -{ - if( head->prev != head ) - return( head->prev ); - return( NULL ); -} - -SHLIST *shListGetFirstItem( SHLIST *head ) -{ - if( head->next != head ) - return( head->next ); - return( NULL ); -} - -SHLIST *shListGetNItem( SHLIST *head, unsigned long num ) -{ - SHLIST *item; - unsigned long i; - - for(i=0,item=head->next;( (i < num) && (item != head) );i++,item=item->next); - if( item != head ) - return( item ); - return( NULL ); -} - -SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item ) -{ - if( item == NULL ) - return( NULL ); - if( item->next != head ) - return( item->next ); - return( NULL ); -} - -SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item ) -{ - if( item == NULL ) - return( NULL ); - if( item->prev != head ) - return( item->prev ); - return( NULL ); -} - -void shListDelItem( SHLIST *head, SHLIST *item, shListFree func ) -{ - if( item == NULL ) - return; -#ifdef SH_LIST_DEBUG - fprintf(stderr, "Del %lx\n", (unsigned long)(item->data)); -#endif - (item->prev)->next = item->next; - (item->next)->prev = item->prev; - if( func && item->data ) { - func( (void *)(item->data) ); - } - free( item ); - head->data = (void *)((unsigned long)(head->data) - 1); -} - -void shListInsFirstItem( SHLIST *head, void *val ) -{ /* Insert to the beginning of the list */ - SHLIST *item; - - item = (SHLIST *)malloc( sizeof(SHLIST) ); - if( item == NULL ) - return; - item->data = val; - item->next = head->next; - item->prev = head; - (head->next)->prev = item; - head->next = item; -#ifdef SH_LIST_DEBUG - fprintf(stderr, "Ins First %lx\n", (unsigned long)(item->data)); -#endif - head->data = (void *)((unsigned long)(head->data) + 1); -} - -void shListInsLastItem( SHLIST *head, void *val ) -{ /* Insert to the end of the list */ - SHLIST *item; - - item = (SHLIST *)malloc( sizeof(SHLIST) ); - if( item == NULL ) - return; - item->data = val; - item->next = head; - item->prev = head->prev; - (head->prev)->next = item; - head->prev = item; -#ifdef SH_LIST_DEBUG - fprintf(stderr, "Ins Last %lx\n", (unsigned long)(item->data)); -#endif - head->data = (void *)((unsigned long)(head->data) + 1); -} - -void shListInsBeforeItem( SHLIST *head, void *val, void *etal, - shListCmp func ) -{ - SHLIST *item, *iptr; - - if( func == NULL ) - shListInsFirstItem( head, val ); - else { - item = (SHLIST *)malloc( sizeof(SHLIST) ); - if( item == NULL ) - return; - item->data = val; - for(iptr=head->next;( iptr != head );iptr=iptr->next) - if( func( val, iptr->data, etal ) ) - break; - item->next = iptr; - item->prev = iptr->prev; - (iptr->prev)->next = item; - iptr->prev = item; -#ifdef SH_LIST_DEBUG - fprintf(stderr, "Ins Before %lx\n", (unsigned long)(item->data)); -#endif - head->data = (void *)((unsigned long)(head->data) + 1); - } -} - -void shListDelAllItems( SHLIST *head, shListFree func ) -{ - SHLIST *item; - - for(item=head->next;( item != head );) { - shListDelItem( head, item, func ); - item = head->next; - } - head->data = (void *)0L; -} - -void shListPrintAllItems( SHLIST *head, shListPrint func ) -{ -#ifdef SH_LIST_DEBUG - SHLIST *item; - - for(item=head->next;( item != head );item=item->next) - if( func ) { - func(item->data); - } - else { - fprintf(stderr, "Item: %lx\n",(unsigned long)(item->data)); - } -#endif -} - -unsigned long shListGetCount( SHLIST *head ) -{ - return( (unsigned long)(head->data) ); -} diff --git a/adb/shlist.h b/adb/shlist.h deleted file mode 100755 index 0a9b07b2766..00000000000 --- a/adb/shlist.h +++ /dev/null @@ -1,34 +0,0 @@ -/*-------------------------------------------------------------------*/ -/* List Functionality */ -/*-------------------------------------------------------------------*/ -#ifndef _SHLIST_H_ -#define _SHLIST_H_ - -typedef struct SHLIST_STRUC { - void *data; - struct SHLIST_STRUC *next; - struct SHLIST_STRUC *prev; -} SHLIST; - -typedef int (*shListCmp)( void *valo, void *valn, void *etalon ); -typedef int (*shListPrint)( void *val ); -typedef void (*shListFree)( void *val ); -typedef int (*shListEqual)( void *val, void *idata ); - -void shListInitList( SHLIST *listPtr ); -SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func ); -SHLIST *shListGetFirstItem( SHLIST *head ); -SHLIST *shListGetNItem( SHLIST *head, unsigned long num ); -SHLIST *shListGetLastItem( SHLIST *head ); -SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item ); -SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item ); -void shListDelItem( SHLIST *head, SHLIST *item, shListFree func ); -void shListInsFirstItem( SHLIST *head, void *val ); -void shListInsBeforeItem( SHLIST *head, void *val, void *etalon, - shListCmp func ); -void shListInsLastItem( SHLIST *head, void *val ); -void shListDelAllItems( SHLIST *head, shListFree func ); -void shListPrintAllItems( SHLIST *head, shListPrint func ); -unsigned long shListGetCount( SHLIST *head ); - -#endif diff --git a/adb/sockets.c b/adb/sockets.c index 9f1b5987060..df223b13396 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -65,8 +65,11 @@ asocket *find_local_socket(unsigned id) asocket *result = NULL; adb_mutex_lock(&socket_list_lock); - for(s = local_socket_list.next; s != &local_socket_list && !result; s = s->next) { - if(s->id == id) result = s; + for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { + if (s->id == id) { + result = s; + break; + } } adb_mutex_unlock(&socket_list_lock); @@ -196,6 +199,7 @@ static void local_socket_close(asocket *s) static void local_socket_destroy(asocket *s) { apacket *p, *n; + D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd); /* IMPORTANT: the remove closes the fd ** that belongs to this socket @@ -215,13 +219,18 @@ static void local_socket_destroy(asocket *s) static void local_socket_close_locked(asocket *s) { + D("entered. LS(%d) fd=%d\n", s->id, s->fd); if(s->peer) { + D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n", + s->id, s->peer->id, s->peer->fd); s->peer->peer = 0; // tweak to avoid deadlock - if (s->peer->close == local_socket_close) + if (s->peer->close == local_socket_close) { local_socket_close_locked(s->peer); - else + } else { s->peer->close(s->peer); + } + s->peer = 0; } /* If we are already closing, or if there are no @@ -240,6 +249,7 @@ static void local_socket_close_locked(asocket *s) s->closing = 1; fdevent_del(&s->fde, FDE_READ); remove_socket(s); + D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd); insert_local_socket(s, &local_socket_closing_list); } @@ -247,6 +257,8 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) { asocket *s = _s; + D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev); + /* put the FDE_WRITE processing before the FDE_READ ** in order to simplify the code. */ @@ -268,6 +280,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) if(errno == EAGAIN) return; if(errno == EINTR) continue; } + D(" closing after write because r=%d and errno is %d\n", r, errno); s->close(s); return; } @@ -283,6 +296,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) ** we can now destroy it. */ if (s->closing) { + D(" closing because 'closing' is set after write\n"); s->close(s); return; } @@ -305,6 +319,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) while(avail > 0) { r = adb_read(fd, x, avail); + D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); if(r > 0) { avail -= r; x += r; @@ -319,13 +334,15 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) is_eof = 1; break; } - + D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n", + s->id, s->fd, r, is_eof, s->fde.force_eof); if((avail == MAX_PAYLOAD) || (s->peer == 0)) { put_apacket(p); } else { p->len = MAX_PAYLOAD - avail; r = s->peer->enqueue(s->peer, p); + D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r); if(r < 0) { /* error return means they closed us as a side-effect @@ -347,8 +364,9 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) fdevent_del(&s->fde, FDE_READ); } } - - if(is_eof) { + /* Don't allow a forced eof if data is still there */ + if((s->fde.force_eof && !r) || is_eof) { + D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof); s->close(s); } } @@ -359,6 +377,8 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) ** bytes of readable data. */ // s->close(s); + D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); + return; } } @@ -366,12 +386,12 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) asocket *create_local_socket(int fd) { asocket *s = calloc(1, sizeof(asocket)); - if(s == 0) fatal("cannot allocate socket"); - install_local_socket(s); + if (s == NULL) fatal("cannot allocate socket"); s->fd = fd; s->enqueue = local_socket_enqueue; s->ready = local_socket_ready; s->close = local_socket_close; + install_local_socket(s); fdevent_install(&s->fde, fd, local_socket_event_func, s); /* fdevent_add(&s->fde, FDE_ERROR); */ @@ -397,7 +417,7 @@ asocket *create_local_service_socket(const char *name) if(fd < 0) return 0; s = create_local_socket(fd); - D("LS(%d): bound to '%s'\n", s->id, name); + D("LS(%d): bound to '%s' via %d\n", s->id, name, fd); return s; } @@ -427,7 +447,8 @@ typedef struct aremotesocket { static int remote_socket_enqueue(asocket *s, apacket *p) { - D("Calling remote_socket_enqueue\n"); + D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n", + s->id, s->fd, s->peer->fd); p->msg.command = A_WRTE; p->msg.arg0 = s->peer->id; p->msg.arg1 = s->id; @@ -438,7 +459,8 @@ static int remote_socket_enqueue(asocket *s, apacket *p) static void remote_socket_ready(asocket *s) { - D("Calling remote_socket_ready\n"); + D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n", + s->id, s->fd, s->peer->fd); apacket *p = get_apacket(); p->msg.command = A_OKAY; p->msg.arg0 = s->peer->id; @@ -448,12 +470,15 @@ static void remote_socket_ready(asocket *s) static void remote_socket_close(asocket *s) { - D("Calling remote_socket_close\n"); + D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n", + s->id, s->fd, s->peer?s->peer->fd:-1); apacket *p = get_apacket(); p->msg.command = A_CLSE; if(s->peer) { p->msg.arg0 = s->peer->id; s->peer->peer = 0; + D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n", + s->id, s->peer->id, s->peer->fd); s->peer->close(s->peer); } p->msg.arg1 = s->id; @@ -482,7 +507,7 @@ asocket *create_remote_socket(unsigned id, atransport *t) asocket *s = calloc(1, sizeof(aremotesocket)); adisconnect* dis = &((aremotesocket*)s)->disconnect; - if(s == 0) fatal("cannot allocate socket"); + if (s == NULL) fatal("cannot allocate socket"); s->id = id; s->enqueue = remote_socket_enqueue; s->ready = remote_socket_ready; @@ -498,7 +523,7 @@ asocket *create_remote_socket(unsigned id, atransport *t) void connect_to_remote(asocket *s, const char *destination) { - D("Connect_to_remote call \n"); + D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd); apacket *p = get_apacket(); int len = strlen(destination) + 1; @@ -566,6 +591,32 @@ unsigned unhex(unsigned char *s, int len) return n; } +/* skip_host_serial return the position in a string + skipping over the 'serial' parameter in the ADB protocol, + where parameter string may be a host:port string containing + the protocol delimiter (colon). */ +char *skip_host_serial(char *service) { + char *first_colon, *serial_end; + + first_colon = strchr(service, ':'); + if (!first_colon) { + /* No colon in service string. */ + return NULL; + } + serial_end = first_colon; + if (isdigit(serial_end[1])) { + serial_end++; + while ((*serial_end) && isdigit(*serial_end)) { + serial_end++; + } + if ((*serial_end) != ':') { + // Something other than numbers was found, reset the end. + serial_end = first_colon; + } + } + return serial_end; +} + static int smart_socket_enqueue(asocket *s, apacket *p) { unsigned len; @@ -621,8 +672,8 @@ static int smart_socket_enqueue(asocket *s, apacket *p) char* serial_end; service += strlen("host-serial:"); - // serial number should follow "host:" - serial_end = strchr(service, ':'); + // serial number should follow "host:" and could be a host:port string. + serial_end = skip_host_serial(service); if (serial_end) { *serial_end = 0; // terminate string serial = service; @@ -753,6 +804,7 @@ static void smart_socket_close(asocket *s) if(s->peer) { s->peer->peer = 0; s->peer->close(s->peer); + s->peer = 0; } free(s); } @@ -761,8 +813,7 @@ asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act)) { D("Creating smart socket \n"); asocket *s = calloc(1, sizeof(asocket)); - if(s == 0) fatal("cannot allocate socket"); - s->id = 0; + if (s == NULL) fatal("cannot allocate socket"); s->enqueue = smart_socket_enqueue; s->ready = smart_socket_ready; s->close = smart_socket_close; diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 63726491a76..b51807615d6 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -44,6 +44,7 @@ typedef CRITICAL_SECTION adb_mutex_t; #define ADB_MUTEX_DEFINE(x) adb_mutex_t x /* declare all mutexes */ +/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */ #define ADB_MUTEX(x) extern adb_mutex_t x; #include "mutex_list.h" @@ -195,6 +196,8 @@ struct fdevent { fdevent *prev; int fd; + int force_eof; + unsigned short state; unsigned short events; @@ -274,13 +277,14 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) #define OS_PATH_SEPARATOR_STR "/" typedef pthread_mutex_t adb_mutex_t; + #define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #define adb_mutex_init pthread_mutex_init #define adb_mutex_lock pthread_mutex_lock #define adb_mutex_unlock pthread_mutex_unlock #define adb_mutex_destroy pthread_mutex_destroy -#define ADB_MUTEX_DEFINE(m) static adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER +#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER #define adb_cond_t pthread_cond_t #define adb_cond_init pthread_cond_init @@ -289,6 +293,10 @@ typedef pthread_mutex_t adb_mutex_t; #define adb_cond_signal pthread_cond_signal #define adb_cond_destroy pthread_cond_destroy +/* declare all mutexes */ +#define ADB_MUTEX(x) extern adb_mutex_t x; +#include "mutex_list.h" + static __inline__ void close_on_exec(int fd) { fcntl( fd, F_SETFD, FD_CLOEXEC ); @@ -387,7 +395,13 @@ static __inline__ int adb_creat(const char* path, int mode) static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) { - return accept( serverfd, addr, addrlen ); + int fd; + + fd = accept(serverfd, addr, addrlen); + if (fd >= 0) + close_on_exec(fd); + + return fd; } #undef accept diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c index ced91e83862..c4267181175 100644 --- a/adb/sysdeps_win32.c +++ b/adb/sysdeps_win32.c @@ -352,7 +352,7 @@ int adb_open(const char* path, int options) return -1; } } - + snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path ); D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) ); return _fh_to_int(f); @@ -837,7 +837,7 @@ static void bip_dump_hex( const unsigned char* ptr, size_t len ) if (len2 > 8) len2 = 8; - for (nn = 0; nn < len2; nn++) + for (nn = 0; nn < len2; nn++) printf("%02x", ptr[nn]); printf(" "); @@ -994,7 +994,7 @@ bip_buffer_write( BipBuffer bip, const void* src, int len ) SetEvent( bip->evt_read ); } - BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", + BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read )); LeaveCriticalSection( &bip->lock ); @@ -1018,7 +1018,7 @@ bip_buffer_read( BipBuffer bip, void* dst, int len ) LeaveCriticalSection( &bip->lock ); errno = EAGAIN; return -1; -#else +#else int ret; LeaveCriticalSection( &bip->lock ); @@ -1087,14 +1087,14 @@ bip_buffer_read( BipBuffer bip, void* dst, int len ) } BIPDUMP( (const unsigned char*)dst - count, count ); - BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", + BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read )); LeaveCriticalSection( &bip->lock ); return count; } -typedef struct SocketPairRec_ +typedef struct SocketPairRec_ { BipBufferRec a2b_bip; BipBufferRec b2a_bip; @@ -1400,7 +1400,7 @@ event_looper_hook( EventLooper looper, int fd, int events ) f->clazz->_fh_hook( f, events & ~node->wanted, node ); node->wanted |= events; } else { - D("event_looper_hook: ignoring events %x for %d wanted=%x)\n", + D("event_looper_hook: ignoring events %x for %d wanted=%x)\n", events, fd, node->wanted); } } @@ -1426,6 +1426,180 @@ event_looper_unhook( EventLooper looper, int fd, int events ) } } +/* + * A fixer for WaitForMultipleObjects on condition that there are more than 64 + * handles to wait on. + * + * In cetain cases DDMS may establish more than 64 connections with ADB. For + * instance, this may happen if there are more than 64 processes running on a + * device, or there are multiple devices connected (including the emulator) with + * the combined number of running processes greater than 64. In this case using + * WaitForMultipleObjects to wait on connection events simply wouldn't cut, + * because of the API limitations (64 handles max). So, we need to provide a way + * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The + * easiest (and "Microsoft recommended") way to do that would be dividing the + * handle array into chunks with the chunk size less than 64, and fire up as many + * waiting threads as there are chunks. Then each thread would wait on a chunk of + * handles, and will report back to the caller which handle has been set. + * Here is the implementation of that algorithm. + */ + +/* Number of handles to wait on in each wating thread. */ +#define WAIT_ALL_CHUNK_SIZE 63 + +/* Descriptor for a wating thread */ +typedef struct WaitForAllParam { + /* A handle to an event to signal when waiting is over. This handle is shared + * accross all the waiting threads, so each waiting thread knows when any + * other thread has exited, so it can exit too. */ + HANDLE main_event; + /* Upon exit from a waiting thread contains the index of the handle that has + * been signaled. The index is an absolute index of the signaled handle in + * the original array. This pointer is shared accross all the waiting threads + * and it's not guaranteed (due to a race condition) that when all the + * waiting threads exit, the value contained here would indicate the first + * handle that was signaled. This is fine, because the caller cares only + * about any handle being signaled. It doesn't care about the order, nor + * about the whole list of handles that were signaled. */ + LONG volatile *signaled_index; + /* Array of handles to wait on in a waiting thread. */ + HANDLE* handles; + /* Number of handles in 'handles' array to wait on. */ + int handles_count; + /* Index inside the main array of the first handle in the 'handles' array. */ + int first_handle_index; + /* Waiting thread handle. */ + HANDLE thread; +} WaitForAllParam; + +/* Waiting thread routine. */ +static unsigned __stdcall +_in_waiter_thread(void* arg) +{ + HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1]; + int res; + WaitForAllParam* const param = (WaitForAllParam*)arg; + + /* We have to wait on the main_event in order to be notified when any of the + * sibling threads is exiting. */ + wait_on[0] = param->main_event; + /* The rest of the handles go behind the main event handle. */ + memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE)); + + res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE); + if (res > 0 && res < (param->handles_count + 1)) { + /* One of the original handles got signaled. Save its absolute index into + * the output variable. */ + InterlockedCompareExchange(param->signaled_index, + res - 1L + param->first_handle_index, -1L); + } + + /* Notify the caller (and the siblings) that the wait is over. */ + SetEvent(param->main_event); + + _endthreadex(0); + return 0; +} + +/* WaitForMultipeObjects fixer routine. + * Param: + * handles Array of handles to wait on. + * handles_count Number of handles in the array. + * Return: + * (>= 0 && < handles_count) - Index of the signaled handle in the array, or + * WAIT_FAILED on an error. + */ +static int +_wait_for_all(HANDLE* handles, int handles_count) +{ + WaitForAllParam* threads; + HANDLE main_event; + int chunks, chunk, remains; + + /* This variable is going to be accessed by several threads at the same time, + * this is bound to fail randomly when the core is run on multi-core machines. + * To solve this, we need to do the following (1 _and_ 2): + * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize + * out the reads/writes in this function unexpectedly. + * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap + * all accesses inside a critical section. But we can also use + * InterlockedCompareExchange() which always provide a full memory barrier + * on Win32. + */ + volatile LONG sig_index = -1; + + /* Calculate number of chunks, and allocate thread param array. */ + chunks = handles_count / WAIT_ALL_CHUNK_SIZE; + remains = handles_count % WAIT_ALL_CHUNK_SIZE; + threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) * + sizeof(WaitForAllParam)); + if (threads == NULL) { + D("Unable to allocate thread array for %d handles.", handles_count); + return (int)WAIT_FAILED; + } + + /* Create main event to wait on for all waiting threads. This is a "manualy + * reset" event that will remain set once it was set. */ + main_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (main_event == NULL) { + D("Unable to create main event. Error: %d", GetLastError()); + free(threads); + return (int)WAIT_FAILED; + } + + /* + * Initialize waiting thread parameters. + */ + + for (chunk = 0; chunk < chunks; chunk++) { + threads[chunk].main_event = main_event; + threads[chunk].signaled_index = &sig_index; + threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk; + threads[chunk].handles = handles + threads[chunk].first_handle_index; + threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE; + } + if (remains) { + threads[chunk].main_event = main_event; + threads[chunk].signaled_index = &sig_index; + threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk; + threads[chunk].handles = handles + threads[chunk].first_handle_index; + threads[chunk].handles_count = remains; + chunks++; + } + + /* Start the waiting threads. */ + for (chunk = 0; chunk < chunks; chunk++) { + /* Note that using adb_thread_create is not appropriate here, since we + * need a handle to wait on for thread termination. */ + threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread, + &threads[chunk], 0, NULL); + if (threads[chunk].thread == NULL) { + /* Unable to create a waiter thread. Collapse. */ + D("Unable to create a waiting thread %d of %d. errno=%d", + chunk, chunks, errno); + chunks = chunk; + SetEvent(main_event); + break; + } + } + + /* Wait on any of the threads to get signaled. */ + WaitForSingleObject(main_event, INFINITE); + + /* Wait on all the waiting threads to exit. */ + for (chunk = 0; chunk < chunks; chunk++) { + WaitForSingleObject(threads[chunk].thread, INFINITE); + CloseHandle(threads[chunk].thread); + } + + CloseHandle(main_event); + free(threads); + + + const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1); + return (ret >= 0) ? ret : (int)WAIT_FAILED; +} + static EventLooperRec win32_looper; static void fdevent_init(void) @@ -1494,7 +1668,7 @@ static void fdevent_process() { looper->htab_count = 0; - for (hook = looper->hooks; hook; hook = hook->next) + for (hook = looper->hooks; hook; hook = hook->next) { if (hook->start && !hook->start(hook)) { D( "fdevent_process: error when starting a hook\n" ); @@ -1525,10 +1699,11 @@ static void fdevent_process() D( "adb_win32: waiting for %d events\n", looper->htab_count ); if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) { - D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count); - abort(); + D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count); + wait_ret = _wait_for_all(looper->htab, looper->htab_count); + } else { + wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE ); } - wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE ); if (wait_ret == (int)WAIT_FAILED) { D( "adb_win32: wait failed, error %ld\n", GetLastError() ); } else { @@ -1669,7 +1844,7 @@ void fdevent_destroy(fdevent *fde) fdevent_remove(fde); } -void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) { memset(fde, 0, sizeof(fdevent)); fde->state = FDE_ACTIVE; @@ -1691,7 +1866,7 @@ void fdevent_remove(fdevent *fde) if(fde->state & FDE_ACTIVE) { fdevent_disconnect(fde); - dump_fde(fde, "disconnect"); + dump_fde(fde, "disconnect"); fdevent_unregister(fde); } @@ -1917,7 +2092,7 @@ static void _event_socketpair_prepare( EventHook hook ) if (hook->wanted & FDE_READ && rbip->can_read) hook->ready |= FDE_READ; - if (hook->wanted & FDE_WRITE && wbip->can_write) + if (hook->wanted & FDE_WRITE && wbip->can_write) hook->ready |= FDE_WRITE; } @@ -1938,7 +2113,7 @@ static void _event_socketpair_prepare( EventHook hook ) D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" ); return 0; } - D( "_event_socketpair_start: hook %s for %x wanted=%x\n", + D( "_event_socketpair_start: hook %s for %x wanted=%x\n", hook->fh->name, _fh_to_int(fh), hook->wanted); return 1; } diff --git a/adb/transport.c b/adb/transport.c index c2877d23971..83a349a955b 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -35,24 +35,30 @@ static atransport transport_list = { ADB_MUTEX_DEFINE( transport_lock ); #if ADB_TRACE +#define MAX_DUMP_HEX_LEN 16 static void dump_hex( const unsigned char* ptr, size_t len ) { int nn, len2 = len; + // Build a string instead of logging each character. + // MAX chars in 2 digit hex, one space, MAX chars, one '\0'. + char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer; - if (len2 > 16) len2 = 16; + if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN; - for (nn = 0; nn < len2; nn++) - D("%02x", ptr[nn]); - D(" "); + for (nn = 0; nn < len2; nn++) { + sprintf(pb, "%02x", ptr[nn]); + pb += 2; + } + sprintf(pb++, " "); for (nn = 0; nn < len2; nn++) { int c = ptr[nn]; if (c < 32 || c > 127) c = '.'; - D("%c", c); + *pb++ = c; } - D("\n"); - fflush(stdout); + *pb++ = '\0'; + DR("%s\n", buffer); } #endif @@ -79,7 +85,7 @@ run_transport_disconnects(atransport* t) { adisconnect* dis = t->disconnects.next; - D("run_transport_disconnects: %p (%s)\n", t, t->serial ? t->serial : "unknown" ); + D("%s: run_transport_disconnects\n", t->serial); while (dis != &t->disconnects) { adisconnect* next = dis->next; dis->func( dis->opaque, t ); @@ -87,75 +93,91 @@ run_transport_disconnects(atransport* t) } } +#if ADB_TRACE +static void +dump_packet(const char* name, const char* func, apacket* p) +{ + unsigned command = p->msg.command; + int len = p->msg.data_length; + char cmd[9]; + char arg0[12], arg1[12]; + int n; + + for (n = 0; n < 4; n++) { + int b = (command >> (n*8)) & 255; + if (b < 32 || b >= 127) + break; + cmd[n] = (char)b; + } + if (n == 4) { + cmd[4] = 0; + } else { + /* There is some non-ASCII name in the command, so dump + * the hexadecimal value instead */ + snprintf(cmd, sizeof cmd, "%08x", command); + } + + if (p->msg.arg0 < 256U) + snprintf(arg0, sizeof arg0, "%d", p->msg.arg0); + else + snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0); + + if (p->msg.arg1 < 256U) + snprintf(arg1, sizeof arg1, "%d", p->msg.arg1); + else + snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1); + + D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", + name, func, cmd, arg0, arg1, len); + dump_hex(p->data, len); +} +#endif /* ADB_TRACE */ + static int -read_packet(int fd, apacket** ppacket) +read_packet(int fd, const char* name, apacket** ppacket) { char *p = (char*)ppacket; /* really read a packet address */ int r; int len = sizeof(*ppacket); + char buff[8]; + if (!name) { + snprintf(buff, sizeof buff, "fd=%d", fd); + name = buff; + } while(len > 0) { r = adb_read(fd, p, len); if(r > 0) { len -= r; p += r; } else { - D("read_packet: %d error %d %d\n", fd, r, errno); + D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno)); if((r < 0) && (errno == EINTR)) continue; return -1; } } #if ADB_TRACE - if (ADB_TRACING) - { - unsigned command = (*ppacket)->msg.command; - int len = (*ppacket)->msg.data_length; - char cmd[5]; - int n; - - for (n = 0; n < 4; n++) { - int b = (command >> (n*8)) & 255; - if (b >= 32 && b < 127) - cmd[n] = (char)b; - else - cmd[n] = '.'; - } - cmd[4] = 0; - - D("read_packet: %d ok: [%08x %s] %08x %08x (%d) ", - fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len); - dump_hex((*ppacket)->data, len); + if (ADB_TRACING) { + dump_packet(name, "from remote", *ppacket); } #endif return 0; } static int -write_packet(int fd, apacket** ppacket) +write_packet(int fd, const char* name, apacket** ppacket) { char *p = (char*) ppacket; /* we really write the packet address */ int r, len = sizeof(ppacket); + char buff[8]; + if (!name) { + snprintf(buff, sizeof buff, "fd=%d", fd); + name = buff; + } #if ADB_TRACE - if (ADB_TRACING) - { - unsigned command = (*ppacket)->msg.command; - int len = (*ppacket)->msg.data_length; - char cmd[5]; - int n; - - for (n = 0; n < 4; n++) { - int b = (command >> (n*8)) & 255; - if (b >= 32 && b < 127) - cmd[n] = (char)b; - else - cmd[n] = '.'; - } - cmd[4] = 0; - - D("write_packet: %d [%08x %s] %08x %08x (%d) ", - fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len); - dump_hex((*ppacket)->data, len); + if (ADB_TRACING) { + dump_packet(name, "to remote", *ppacket); } #endif len = sizeof(ppacket); @@ -165,7 +187,7 @@ write_packet(int fd, apacket** ppacket) len -= r; p += r; } else { - D("write_packet: %d error %d %d\n", fd, r, errno); + D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno)); if((r < 0) && (errno == EINTR)) continue; return -1; } @@ -175,10 +197,12 @@ write_packet(int fd, apacket** ppacket) static void transport_socket_events(int fd, unsigned events, void *_t) { + atransport *t = _t; + D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events); if(events & FDE_READ){ apacket *p = 0; - if(read_packet(fd, &p)){ - D("failed to read packet from transport socket on fd %d\n", fd); + if(read_packet(fd, t->serial, &p)){ + D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd); } else { handle_packet(p, (atransport *) _t); } @@ -204,11 +228,13 @@ void send_packet(apacket *p, atransport *t) print_packet("send", p); if (t == NULL) { - fatal_errno("Transport is null"); D("Transport is null \n"); + // Zap errno because print_packet() and other stuff have errno effect. + errno = 0; + fatal_errno("Transport is null"); } - if(write_packet(t->transport_socket, &p)){ + if(write_packet(t->transport_socket, t->serial, &p)){ fatal_errno("cannot enqueue packet on transport socket"); } } @@ -231,52 +257,51 @@ static void *output_thread(void *_t) atransport *t = _t; apacket *p; - D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd ); - - D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1); + D("%s: starting transport output thread on fd %d, SYNC online (%d)\n", + t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; - if(write_packet(t->fd, &p)) { + if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); - D("from_remote: failed to write SYNC apacket to transport %p", t); + D("%s: failed to write SYNC packet\n", t->serial); goto oops; } - D("from_remote: data pump for transport %p\n", t); + D("%s: data pump started\n", t->serial); for(;;) { p = get_apacket(); if(t->read_from_remote(p, t) == 0){ - D("from_remote: received remote packet, sending to transport %p\n", - t); - if(write_packet(t->fd, &p)){ + D("%s: received remote packet, sending to transport\n", + t->serial); + if(write_packet(t->fd, t->serial, &p)){ put_apacket(p); - D("from_remote: failed to write apacket to transport %p", t); + D("%s: failed to write apacket to transport\n", t->serial); goto oops; } } else { - D("from_remote: remote read failed for transport %p\n", p); + D("%s: remote read failed for transport\n", t->serial); put_apacket(p); break; } } - D("from_remote: SYNC offline for transport %p\n", t); + D("%s: SYNC offline for transport\n", t->serial); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; - if(write_packet(t->fd, &p)) { + if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); - D("from_remote: failed to write SYNC apacket to transport %p", t); + D("%s: failed to write SYNC apacket to transport", t->serial); } oops: - D("from_remote: thread is exiting for transport %p\n", t); + D("%s: transport output thread is exiting\n", t->serial); kick_transport(t); transport_unref(t); return 0; @@ -288,35 +313,35 @@ static void *input_thread(void *_t) apacket *p; int active = 0; - D("to_remote: starting input_thread for %p, reading from fd %d\n", - t, t->fd); + D("%s: starting transport input thread, reading from fd %d\n", + t->serial, t->fd); for(;;){ - if(read_packet(t->fd, &p)) { - D("to_remote: failed to read apacket from transport %p on fd %d\n", - t, t->fd ); + if(read_packet(t->fd, t->serial, &p)) { + D("%s: failed to read apacket from transport on fd %d\n", + t->serial, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { - D("to_remote: transport %p SYNC offline\n", t); + D("%s: transport SYNC offline\n", t->serial); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { - D("to_remote: transport %p SYNC online\n", t); + D("%s: transport SYNC online\n", t->serial); active = 1; } else { - D("to_remote: trandport %p ignoring SYNC %d != %d\n", - t, p->msg.arg1, t->sync_token); + D("%s: transport ignoring SYNC %d != %d\n", + t->serial, p->msg.arg1, t->sync_token); } } } else { if(active) { - D("to_remote: transport %p got packet, sending to remote\n", t); + D("%s: transport got packet, sending to remote\n", t->serial); t->write_to_remote(p, t); } else { - D("to_remote: transport %p ignoring packet while offline\n", t); + D("%s: transport ignoring packet while offline\n", t->serial); } } @@ -327,7 +352,7 @@ static void *input_thread(void *_t) // while a client socket is still active. close_all_sockets(t); - D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd); + D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd); kick_transport(t); transport_unref(t); return 0; @@ -508,7 +533,7 @@ transport_read_action(int fd, struct tmsg* m) p += r; } else { if((r < 0) && (errno == EINTR)) continue; - D("transport_read_action: on fd %d, error %d: %s\n", + D("transport_read_action: on fd %d, error %d: %s\n", fd, errno, strerror(errno)); return -1; } @@ -530,7 +555,7 @@ transport_write_action(int fd, struct tmsg* m) p += r; } else { if((r < 0) && (errno == EINTR)) continue; - D("transport_write_action: on fd %d, error %d: %s\n", + D("transport_write_action: on fd %d, error %d: %s\n", fd, errno, strerror(errno)); return -1; } @@ -557,7 +582,7 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) t = m.transport; if(m.action == 0){ - D("transport: %p removing and free'ing %d\n", t, t->transport_socket); + D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket); /* IMPORTANT: the remove closes one half of the ** socket pair. The close closes the other half. @@ -593,12 +618,11 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) fatal_errno("cannot open transport socketpair"); } - D("transport: %p (%d,%d) starting\n", t, s[0], s[1]); + D("transport: %s (%d,%d) starting\n", t->serial, s[0], s[1]); t->transport_socket = s[0]; t->fd = s[1]; - D("transport: %p install %d\n", t, t->transport_socket ); fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, @@ -653,7 +677,7 @@ static void register_transport(atransport *transport) tmsg m; m.transport = transport; m.action = 1; - D("transport: %p registered\n", transport); + D("transport: %s registered\n", transport->serial); if(transport_write_action(transport_registration_send, &m)) { fatal_errno("cannot write transport registration socket\n"); } @@ -664,28 +688,34 @@ static void remove_transport(atransport *transport) tmsg m; m.transport = transport; m.action = 0; - D("transport: %p removed\n", transport); + D("transport: %s removed\n", transport->serial); if(transport_write_action(transport_registration_send, &m)) { fatal_errno("cannot write transport registration socket\n"); } } +static void transport_unref_locked(atransport *t) +{ + t->ref_count--; + if (t->ref_count == 0) { + D("transport: %s unref (kicking and closing)\n", t->serial); + if (!t->kicked) { + t->kicked = 1; + t->kick(t); + } + t->close(t); + remove_transport(t); + } else { + D("transport: %s unref (count=%d)\n", t->serial, t->ref_count); + } +} + static void transport_unref(atransport *t) { if (t) { adb_mutex_lock(&transport_lock); - t->ref_count--; - D("transport: %p R- (ref=%d)\n", t, t->ref_count); - if (t->ref_count == 0) { - D("transport: %p kicking and closing\n", t); - if (!t->kicked) { - t->kicked = 1; - t->kick(t); - } - t->close(t); - remove_transport(t); - } + transport_unref_locked(t); adb_mutex_unlock(&transport_lock); } } @@ -852,7 +882,13 @@ void close_usb_devices() void register_socket_transport(int s, const char *serial, int port, int local) { atransport *t = calloc(1, sizeof(atransport)); - D("transport: %p init'ing for socket %d, on port %d\n", t, s, port); + char buff[32]; + + if (!serial) { + snprintf(buff, sizeof buff, "T-%p", t); + serial = buff; + } + D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port); if ( init_socket_transport(t, s, port, local) < 0 ) { adb_close(s); free(t); @@ -894,6 +930,29 @@ void unregister_transport(atransport *t) transport_unref(t); } +// unregisters all non-emulator TCP transports +void unregister_all_tcp_transports() +{ + atransport *t, *next; + adb_mutex_lock(&transport_lock); + for (t = transport_list.next; t != &transport_list; t = next) { + next = t->next; + if (t->type == kTransportLocal && t->adb_port == 0) { + t->next->prev = t->prev; + t->prev->next = next; + // we cannot call kick_transport when holding transport_lock + if (!t->kicked) + { + t->kicked = 1; + t->kick(t); + } + transport_unref_locked(t); + } + } + + adb_mutex_unlock(&transport_lock); +} + #endif void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) @@ -933,21 +992,26 @@ int readx(int fd, void *ptr, size_t len) #if ADB_TRACE int len0 = len; #endif - D("readx: %d %p %d\n", fd, ptr, (int)len); + D("readx: fd=%d wanted=%d\n", fd, (int)len); while(len > 0) { r = adb_read(fd, p, len); if(r > 0) { len -= r; p += r; } else { - D("readx: %d %d %s\n", fd, r, strerror(errno)); - if((r < 0) && (errno == EINTR)) continue; + if (r < 0) { + D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno)); + if (errno == EINTR) + continue; + } else { + D("readx: fd=%d disconnected\n", fd); + } return -1; } } #if ADB_TRACE - D("readx: %d ok: ", fd); + D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len); dump_hex( ptr, len0 ); #endif return 0; @@ -959,7 +1023,7 @@ int writex(int fd, const void *ptr, size_t len) int r; #if ADB_TRACE - D("writex: %d %p %d: ", fd, ptr, (int)len); + D("writex: fd=%d len=%d: ", fd, (int)len); dump_hex( ptr, len ); #endif while(len > 0) { @@ -968,13 +1032,16 @@ int writex(int fd, const void *ptr, size_t len) len -= r; p += r; } else { - D("writex: %d %d %s\n", fd, r, strerror(errno)); - if((r < 0) && (errno == EINTR)) continue; + if (r < 0) { + D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno)); + if (errno == EINTR) + continue; + } else { + D("writex: fd=%d disconnected\n", fd); + } return -1; } } - - D("writex: %d ok\n", fd); return 0; } @@ -1011,4 +1078,3 @@ int check_data(apacket *p) return 0; } } - diff --git a/vold/logwrapper.h b/adb/transport.h similarity index 61% rename from vold/logwrapper.h rename to adb/transport.h index bf28aae34e4..992e05285f9 100644 --- a/vold/logwrapper.h +++ b/adb/transport.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +14,13 @@ * limitations under the License. */ -#ifndef _LOGWRAPPER_H -#define _LOGWRAPPER_H +#ifndef __TRANSPORT_H +#define __TRANSPORT_H -#include -int logwrap(int argc, char* argv[], int background); -#endif +/* convenience wrappers around read/write that will retry on +** EINTR and/or short read/write. Returns 0 on success, -1 +** on error or EOF. +*/ +int readx(int fd, void *ptr, size_t len); +int writex(int fd, const void *ptr, size_t len); +#endif /* __TRANSPORT_H */ diff --git a/adb/transport_local.c b/adb/transport_local.c index 81d120ef119..d985ee3a64c 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -25,7 +25,7 @@ #define TRACE_TAG TRACE_TRANSPORT #include "adb.h" -#ifdef __ppc__ +#ifdef HAVE_BIG_ENDIAN #define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) static inline void fix_endians(apacket *p) { @@ -41,9 +41,9 @@ static inline void fix_endians(apacket *p) #endif #if ADB_HOST -/* we keep a list of opened transports, transport 0 is bound to 5555, - * transport 1 to 5557, .. transport n to 5555 + n*2. the list is used - * to detect when we're trying to connect twice to a given local transport +/* we keep a list of opened transports. The atransport struct knows to which + * local transport it is connected. The list is used to detect when we're + * trying to connect twice to a given local transport. */ #define ADB_LOCAL_TRANSPORT_MAX 16 @@ -61,7 +61,7 @@ static int remote_read(apacket *p, atransport *t) fix_endians(p); -#if 0 && defined __ppc__ +#if 0 && defined HAVE_BIG_ENDIAN D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n", p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic); #endif @@ -89,7 +89,7 @@ static int remote_write(apacket *p, atransport *t) fix_endians(p); -#if 0 && defined __ppc__ +#if 0 && defined HAVE_BIG_ENDIAN D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n", p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic); #endif @@ -102,7 +102,11 @@ static int remote_write(apacket *p, atransport *t) } -int local_connect(int port) +int local_connect(int port) { + return local_connect_arbitrary_ports(port-1, port); +} + +int local_connect_arbitrary_ports(int console_port, int adb_port) { char buf[64]; int fd = -1; @@ -110,19 +114,19 @@ int local_connect(int port) #if ADB_HOST const char *host = getenv("ADBHOST"); if (host) { - fd = socket_network_client(host, port, SOCK_STREAM); + fd = socket_network_client(host, adb_port, SOCK_STREAM); } #endif if (fd < 0) { - fd = socket_loopback_client(port, SOCK_STREAM); + fd = socket_loopback_client(adb_port, SOCK_STREAM); } if (fd >= 0) { D("client: connected on remote on fd %d\n", fd); close_on_exec(fd); disable_tcp_nagle(fd); - snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, port - 1); - register_socket_transport(fd, buf, port, 1); + snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port); + register_socket_transport(fd, buf, adb_port, 1); return 0; } return -1; @@ -132,7 +136,7 @@ int local_connect(int port) static void *client_socket_thread(void *x) { #if ADB_HOST - int port = ADB_LOCAL_TRANSPORT_PORT; + int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; int count = ADB_LOCAL_TRANSPORT_MAX; D("transport: client_socket_thread() starting\n"); @@ -181,6 +185,117 @@ static void *server_socket_thread(void * arg) return 0; } +/* This is relevant only for ADB daemon running inside the emulator. */ +#if !ADB_HOST +/* + * Redefine open and write for qemu_pipe.h that contains inlined references + * to those routines. We will redifine them back after qemu_pipe.h inclusion. + */ +#undef open +#undef write +#define open adb_open +#define write adb_write +#include +#undef open +#undef write +#define open ___xxx_open +#define write ___xxx_write + +/* A worker thread that monitors host connections, and registers a transport for + * every new host connection. This thread replaces server_socket_thread on + * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD + * pipe to communicate with adbd daemon inside the guest. This is done in order + * to provide more robust communication channel between ADB host and guest. The + * main issue with server_socket_thread approach is that it runs on top of TCP, + * and thus is sensitive to network disruptions. For instance, the + * ConnectionManager may decide to reset all network connections, in which case + * the connection between ADB host and guest will be lost. To make ADB traffic + * independent from the network, we use here 'adb' QEMUD service to transfer data + * between the host, and the guest. See external/qemu/android/adb-*.* that + * implements the emulator's side of the protocol. Another advantage of using + * QEMUD approach is that ADB will be up much sooner, since it doesn't depend + * anymore on network being set up. + * The guest side of the protocol contains the following phases: + * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service + * is opened, and it becomes clear whether or not emulator supports that + * protocol. + * - Wait for the ADB host to create connection with the guest. This is done by + * sending an 'accept' request to the adb QEMUD service, and waiting on + * response. + * - When new ADB host connection is accepted, the connection with adb QEMUD + * service is registered as the transport, and a 'start' request is sent to the + * adb QEMUD service, indicating that the guest is ready to receive messages. + * Note that the guest will ignore messages sent down from the emulator before + * the transport registration is completed. That's why we need to send the + * 'start' request after the transport is registered. + */ +static void *qemu_socket_thread(void * arg) +{ +/* 'accept' request to the adb QEMUD service. */ +static const char _accept_req[] = "accept"; +/* 'start' request to the adb QEMUD service. */ +static const char _start_req[] = "start"; +/* 'ok' reply from the adb QEMUD service. */ +static const char _ok_resp[] = "ok"; + + const int port = (int)arg; + int res, fd; + char tmp[256]; + char con_name[32]; + + D("transport: qemu_socket_thread() starting\n"); + + /* adb QEMUD service connection request. */ + snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port); + + /* Connect to the adb QEMUD service. */ + fd = qemu_pipe_open(con_name); + if (fd < 0) { + /* This could be an older version of the emulator, that doesn't + * implement adb QEMUD service. Fall back to the old TCP way. */ + adb_thread_t thr; + D("adb service is not available. Falling back to TCP socket.\n"); + adb_thread_create(&thr, server_socket_thread, arg); + return 0; + } + + for(;;) { + /* + * Wait till the host creates a new connection. + */ + + /* Send the 'accept' request. */ + res = adb_write(fd, _accept_req, strlen(_accept_req)); + if (res == strlen(_accept_req)) { + /* Wait for the response. In the response we expect 'ok' on success, + * or 'ko' on failure. */ + res = adb_read(fd, tmp, sizeof(tmp)); + if (res != 2 || memcmp(tmp, _ok_resp, 2)) { + D("Accepting ADB host connection has failed.\n"); + adb_close(fd); + } else { + /* Host is connected. Register the transport, and start the + * exchange. */ + register_socket_transport(fd, "host", port, 1); + adb_write(fd, _start_req, strlen(_start_req)); + } + + /* Prepare for accepting of the next ADB host connection. */ + fd = qemu_pipe_open(con_name); + if (fd < 0) { + D("adb service become unavailable.\n"); + return 0; + } + } else { + D("Unable to send the '%s' request to ADB service.\n", _accept_req); + return 0; + } + } + D("transport: qemu_socket_thread() exiting\n"); + return 0; +} +#endif // !ADB_HOST + void local_init(int port) { adb_thread_t thr; @@ -189,7 +304,21 @@ void local_init(int port) if(HOST) { func = client_socket_thread; } else { +#if ADB_HOST func = server_socket_thread; +#else + /* For the adbd daemon in the system image we need to distinguish + * between the device, and the emulator. */ + char is_qemu[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", is_qemu, ""); + if (!strcmp(is_qemu, "1")) { + /* Running inside the emulator: use QEMUD pipe as the transport. */ + func = qemu_socket_thread; + } else { + /* Running inside the device: use TCP socket as the transport. */ + func = server_socket_thread; + } +#endif !ADB_HOST } D("transport: local %s init\n", HOST ? "client" : "server"); @@ -227,7 +356,50 @@ static void remote_close(atransport *t) adb_close(t->fd); } -int init_socket_transport(atransport *t, int s, int port, int local) + +#if ADB_HOST +/* Only call this function if you already hold local_transports_lock. */ +atransport* find_emulator_transport_by_adb_port_locked(int adb_port) +{ + int i; + for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) { + if (local_transports[i] && local_transports[i]->adb_port == adb_port) { + return local_transports[i]; + } + } + return NULL; +} + +atransport* find_emulator_transport_by_adb_port(int adb_port) +{ + adb_mutex_lock( &local_transports_lock ); + atransport* result = find_emulator_transport_by_adb_port_locked(adb_port); + adb_mutex_unlock( &local_transports_lock ); + return result; +} + +/* Only call this function if you already hold local_transports_lock. */ +int get_available_local_transport_index_locked() +{ + int i; + for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) { + if (local_transports[i] == NULL) { + return i; + } + } + return -1; +} + +int get_available_local_transport_index() +{ + adb_mutex_lock( &local_transports_lock ); + int result = get_available_local_transport_index_locked(); + adb_mutex_unlock( &local_transports_lock ); + return result; +} +#endif + +int init_socket_transport(atransport *t, int s, int adb_port, int local) { int fail = 0; @@ -239,26 +411,30 @@ int init_socket_transport(atransport *t, int s, int port, int local) t->sync_token = 1; t->connection_state = CS_OFFLINE; t->type = kTransportLocal; + t->adb_port = 0; #if ADB_HOST if (HOST && local) { adb_mutex_lock( &local_transports_lock ); { - int index = (port - ADB_LOCAL_TRANSPORT_PORT)/2; - - if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) { - D("bad local transport port number: %d\n", port); - fail = -1; - } - else if (local_transports[index] != NULL) { + t->adb_port = adb_port; + atransport* existing_transport = + find_emulator_transport_by_adb_port_locked(adb_port); + int index = get_available_local_transport_index_locked(); + if (existing_transport != NULL) { D("local transport for port %d already registered (%p)?\n", - port, local_transports[index]); + adb_port, existing_transport); fail = -1; - } - else + } else if (index < 0) { + // Too many emulators. + D("cannot register more emulators. Maximum is %d\n", + ADB_LOCAL_TRANSPORT_MAX); + fail = -1; + } else { local_transports[index] = t; - } - adb_mutex_unlock( &local_transports_lock ); + } + } + adb_mutex_unlock( &local_transports_lock ); } #endif return fail; diff --git a/adb/transport_usb.c b/adb/transport_usb.c index 258416353b5..ee6b637b556 100644 --- a/adb/transport_usb.c +++ b/adb/transport_usb.c @@ -27,8 +27,7 @@ #include "usb_vendors.h" #endif -/* XXX better define? */ -#ifdef __ppc__ +#ifdef HAVE_BIG_ENDIAN #define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) static inline void fix_endians(apacket *p) { diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c new file mode 100644 index 00000000000..8c752662692 --- /dev/null +++ b/adb/usb_libusb.c @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2009 bsdroid project + * Alexey Tarasov + * + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + +static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER; +static libusb_context *ctx = NULL; + +struct usb_handle +{ + usb_handle *prev; + usb_handle *next; + + libusb_device *dev; + libusb_device_handle *devh; + int interface; + uint8_t dev_bus; + uint8_t dev_addr; + + int zero_mask; + unsigned char end_point_address[2]; + char serial[128]; + + adb_cond_t notify; + adb_mutex_t lock; +}; + +static struct usb_handle handle_list = { + .prev = &handle_list, + .next = &handle_list, +}; + +void +usb_cleanup() +{ + libusb_exit(ctx); +} + +void +report_bulk_libusb_error(int r) +{ + switch (r) { + case LIBUSB_ERROR_TIMEOUT: + D("Transfer timeout\n"); + break; + + case LIBUSB_ERROR_PIPE: + D("Control request is not supported\n"); + break; + + case LIBUSB_ERROR_OVERFLOW: + D("Device offered more data\n"); + break; + + case LIBUSB_ERROR_NO_DEVICE : + D("Device was disconnected\n"); + break; + + default: + D("Error %d during transfer\n", r); + break; + }; +} + +static int +usb_bulk_write(usb_handle *uh, const void *data, int len) +{ + int r = 0; + int transferred = 0; + + r = libusb_bulk_transfer(uh->devh, uh->end_point_address[1], (void *)data, len, + &transferred, 0); + + if (r != 0) { + D("usb_bulk_write(): "); + report_bulk_libusb_error(r); + return r; + } + + return (transferred); +} + +static int +usb_bulk_read(usb_handle *uh, void *data, int len) +{ + int r = 0; + int transferred = 0; + + r = libusb_bulk_transfer(uh->devh, uh->end_point_address[0], data, len, + &transferred, 0); + + if (r != 0) { + D("usb_bulk_read(): "); + report_bulk_libusb_error(r); + return r; + } + + return (transferred); +} + +int +usb_write(struct usb_handle *uh, const void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + int need_zero = 0; + + if (uh->zero_mask == 1) { + if (!(len & uh->zero_mask)) { + need_zero = 1; + } + } + + D("usb_write(): %p:%d -> transport %p\n", _data, len, uh); + + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + n = usb_bulk_write(uh, data, xfer); + + if (n != xfer) { + D("usb_write(): failed for transport %p (%d bytes left)\n", uh, len); + return -1; + } + + len -= xfer; + data += xfer; + } + + if (need_zero){ + n = usb_bulk_write(uh, _data, 0); + + if (n < 0) { + D("usb_write(): failed to finish operation for transport %p\n", uh); + } + return n; + } + + return 0; +} + +int +usb_read(struct usb_handle *uh, void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + + D("usb_read(): %p:%d <- transport %p\n", _data, len, uh); + + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + n = usb_bulk_read(uh, data, xfer); + + if (n != xfer) { + if (n > 0) { + data += n; + len -= n; + continue; + } + + D("usb_read(): failed for transport %p (%d bytes left)\n", uh, len); + return -1; + } + + len -= xfer; + data += xfer; + } + + return 0; + } + +int +usb_close(struct usb_handle *h) +{ + D("usb_close(): closing transport %p\n", h); + adb_mutex_lock(&usb_lock); + + h->next->prev = h->prev; + h->prev->next = h->next; + h->prev = NULL; + h->next = NULL; + + libusb_release_interface(h->devh, h->interface); + libusb_close(h->devh); + libusb_unref_device(h->dev); + + adb_mutex_unlock(&usb_lock); + + free(h); + + return (0); +} + +void usb_kick(struct usb_handle *h) +{ + D("usb_cick(): kicking transport %p\n", h); + + adb_mutex_lock(&h->lock); + unregister_usb_transport(h); + adb_mutex_unlock(&h->lock); + + h->next->prev = h->prev; + h->prev->next = h->next; + h->prev = NULL; + h->next = NULL; + + libusb_release_interface(h->devh, h->interface); + libusb_close(h->devh); + libusb_unref_device(h->dev); + free(h); +} + +int +check_usb_interface(libusb_interface *interface, + libusb_device_descriptor *desc, + struct usb_handle *uh) +{ + int e; + + if (interface->num_altsetting == 0) { + D("check_usb_interface(): No interface settings\n"); + return -1; + } + + libusb_interface_descriptor *idesc = &interface->altsetting[0]; + + if (idesc->bNumEndpoints != 2) { + D("check_usb_interface(): Interface have not 2 endpoints, ignoring\n"); + return -1; + } + + for (e = 0; e < idesc->bNumEndpoints; e++) { + libusb_endpoint_descriptor *edesc = &idesc->endpoint[e]; + + if (edesc->bmAttributes != LIBUSB_TRANSFER_TYPE_BULK) { + D("check_usb_interface(): Endpoint (%u) is not bulk (%u), ignoring\n", + edesc->bmAttributes, LIBUSB_TRANSFER_TYPE_BULK); + return -1; + } + + if (edesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) + uh->end_point_address[0] = edesc->bEndpointAddress; + else + uh->end_point_address[1] = edesc->bEndpointAddress; + + /* aproto 01 needs 0 termination */ + if (idesc->bInterfaceProtocol == 0x01) { + uh->zero_mask = edesc->wMaxPacketSize - 1; + D("check_usb_interface(): Forced Android interface protocol v.1\n"); + } + } + + D("check_usb_interface(): Device: %04x:%04x " + "iclass: %x, isclass: %x, iproto: %x ep: %x/%x-> ", + desc->idVendor, desc->idProduct, idesc->bInterfaceClass, + idesc->bInterfaceSubClass, idesc->bInterfaceProtocol, + uh->end_point_address[0], uh->end_point_address[1]); + + if (!is_adb_interface(desc->idVendor, desc->idProduct, + idesc->bInterfaceClass, idesc->bInterfaceSubClass, + idesc->bInterfaceProtocol)) + { + D("not matches\n"); + return -1; + } + + D("matches\n"); + return 1; +} + +int +check_usb_interfaces(libusb_config_descriptor *config, + libusb_device_descriptor *desc, struct usb_handle *uh) +{ + int i; + + for (i = 0; i < config->bNumInterfaces; ++i) { + if (check_usb_interface(&config->interface[i], desc, uh) != -1) { + /* found some interface and saved information about it */ + D("check_usb_interfaces(): Interface %d of %04x:%04x " + "matches Android device\n", i, desc->idVendor, + desc->idProduct); + + return i; + } + } + + return -1; +} + +int +register_device(struct usb_handle *uh, const char *serial) +{ + D("register_device(): Registering %p [%s] as USB transport\n", + uh, serial); + + struct usb_handle *usb= NULL; + + usb = calloc(1, sizeof(struct usb_handle)); + memcpy(usb, uh, sizeof(struct usb_handle)); + strcpy(usb->serial, uh->serial); + + adb_cond_init(&usb->notify, 0); + adb_mutex_init(&usb->lock, 0); + + adb_mutex_lock(&usb_lock); + + usb->next = &handle_list; + usb->prev = handle_list.prev; + usb->prev->next = usb; + usb->next->prev = usb; + + adb_mutex_unlock(&usb_lock); + + register_usb_transport(usb, serial, 1); + + return (1); +} + +int +already_registered(usb_handle *uh) +{ + struct usb_handle *usb= NULL; + int exists = 0; + + adb_mutex_lock(&usb_lock); + + for (usb = handle_list.next; usb != &handle_list; usb = usb->next) { + if ((usb->dev_bus == uh->dev_bus) && + (usb->dev_addr == uh->dev_addr)) + { + exists = 1; + break; + } + } + + adb_mutex_unlock(&usb_lock); + + return exists; +} + +void +check_device(libusb_device *dev) +{ + struct usb_handle uh; + int i = 0; + int found = -1; + char serial[256] = {0}; + + libusb_device_descriptor desc; + libusb_config_descriptor *config = NULL; + + int r = libusb_get_device_descriptor(dev, &desc); + + if (r != LIBUSB_SUCCESS) { + D("check_device(): Failed to get device descriptor\n"); + return; + } + + if ((desc.idVendor == 0) && (desc.idProduct == 0)) + return; + + D("check_device(): Probing usb device %04x:%04x\n", + desc.idVendor, desc.idProduct); + + if (!is_adb_interface (desc.idVendor, desc.idProduct, + ADB_CLASS, ADB_SUBCLASS, ADB_PROTOCOL)) + { + D("check_device(): Ignored due unknown vendor id\n"); + return; + } + + uh.dev_bus = libusb_get_bus_number(dev); + uh.dev_addr = libusb_get_device_address(dev); + + if (already_registered(&uh)) { + D("check_device(): Device (bus: %d, address: %d) " + "is already registered\n", uh.dev_bus, uh.dev_addr); + return; + } + + D("check_device(): Device bus: %d, address: %d\n", + uh.dev_bus, uh.dev_addr); + + r = libusb_get_active_config_descriptor(dev, &config); + + if (r != 0) { + if (r == LIBUSB_ERROR_NOT_FOUND) { + D("check_device(): Device %4x:%4x is unconfigured\n", + desc.idVendor, desc.idProduct); + return; + } + + D("check_device(): Failed to get configuration for %4x:%4x\n", + desc.idVendor, desc.idProduct); + return; + } + + if (config == NULL) { + D("check_device(): Sanity check failed after " + "getting active config\n"); + return; + } + + if (config->interface != NULL) { + found = check_usb_interfaces(config, &desc, &uh); + } + + /* not needed anymore */ + libusb_free_config_descriptor(config); + + r = libusb_open(dev, &uh.devh); + uh.dev = dev; + + if (r != 0) { + switch (r) { + case LIBUSB_ERROR_NO_MEM: + D("check_device(): Memory allocation problem\n"); + break; + + case LIBUSB_ERROR_ACCESS: + D("check_device(): Permissions problem, " + "current user priveleges are messed up?\n"); + break; + + case LIBUSB_ERROR_NO_DEVICE: + D("check_device(): Device disconected, bad cable?\n"); + break; + + default: + D("check_device(): libusb triggered error %d\n", r); + } + // skip rest + found = -1; + } + + if (found >= 0) { + D("check_device(): Device matches Android interface\n"); + // read the device's serial number + memset(serial, 0, sizeof(serial)); + uh.interface = found; + + r = libusb_claim_interface(uh.devh, uh.interface); + + if (r < 0) { + D("check_device(): Failed to claim interface %d\n", + uh.interface); + + goto fail; + } + + if (desc.iSerialNumber) { + // reading serial + uint16_t buffer[128] = {0}; + uint16_t languages[128] = {0}; + int languageCount = 0; + + memset(languages, 0, sizeof(languages)); + r = libusb_control_transfer(uh.devh, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8, + 0, (uint8_t *)languages, sizeof(languages), 0); + + if (r <= 0) { + D("check_device(): Failed to get languages count\n"); + goto fail; + } + + languageCount = (r - 2) / 2; + + for (i = 1; i <= languageCount; ++i) { + memset(buffer, 0, sizeof(buffer)); + + r = libusb_control_transfer(uh.devh, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber, + languages[i], (uint8_t *)buffer, sizeof(buffer), 0); + + if (r > 0) { /* converting serial */ + int j = 0; + r /= 2; + + for (j = 1; j < r; ++j) + serial[j - 1] = buffer[j]; + + serial[j - 1] = '\0'; + break; /* languagesCount cycle */ + } + } + + if (register_device(&uh, serial) == 0) { + D("check_device(): Failed to register device\n"); + goto fail_interface; + } + + libusb_ref_device(dev); + } + } + + return; + +fail_interface: + libusb_release_interface(uh.devh, uh.interface); + +fail: + libusb_close(uh.devh); + uh.devh = NULL; +} + +int +check_device_connected(struct usb_handle *uh) +{ + int r = libusb_kernel_driver_active(uh->devh, uh->interface); + + if (r == LIBUSB_ERROR_NO_DEVICE) + return 0; + + if (r < 0) + return -1; + + return 1; +} + +void +kick_disconnected() +{ + struct usb_handle *usb= NULL; + + adb_mutex_lock(&usb_lock); + + for (usb = handle_list.next; usb != &handle_list; usb = usb->next) { + + if (check_device_connected(usb) == 0) { + D("kick_disconnected(): Transport %p is not online anymore\n", + usb); + + usb_kick(usb); + } + } + + adb_mutex_unlock(&usb_lock); +} + +void +scan_usb_devices() +{ + D("scan_usb_devices(): started\n"); + + libusb_device **devs= NULL; + libusb_device *dev= NULL; + ssize_t cnt = libusb_get_device_list(ctx, &devs); + + if (cnt < 0) { + D("scan_usb_devices(): Failed to get device list (error: %d)\n", + cnt); + + return; + } + + int i = 0; + + while ((dev = devs[i++]) != NULL) { + check_device(dev); + } + + libusb_free_device_list(devs, 1); +} + +void * +device_poll_thread(void* unused) +{ + D("device_poll_thread(): Created USB scan thread\n"); + + for (;;) { + sleep(5); + kick_disconnected(); + scan_usb_devices(); + } + + /* never reaching this point */ + return (NULL); +} + +static void +sigalrm_handler(int signo) +{ + /* nothing */ +} + +void +usb_init() +{ + D("usb_init(): started\n"); + adb_thread_t tid; + struct sigaction actions; + + int r = libusb_init(&ctx); + + if (r != LIBUSB_SUCCESS) { + err(EX_IOERR, "Failed to init libusb\n"); + } + + memset(&actions, 0, sizeof(actions)); + + sigemptyset(&actions.sa_mask); + + actions.sa_flags = 0; + actions.sa_handler = sigalrm_handler; + + sigaction(SIGALRM, &actions, NULL); + + /* initial device scan */ + scan_usb_devices(); + + /* starting USB event polling thread */ + if (adb_thread_create(&tid, device_poll_thread, NULL)) { + err(EX_IOERR, "cannot create USB scan thread\n"); + } + + D("usb_init(): finished\n"); +} + diff --git a/adb/usb_linux.c b/adb/usb_linux.c index 863af1dfc48..4d55b74ea87 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,7 @@ /* usb scan debugging is waaaay too verbose */ #define DBGX(x...) -static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER; +ADB_MUTEX_DEFINE( usb_lock ); struct usb_handle { @@ -148,15 +149,15 @@ static void find_usb_device(const char *base, // DBGX("[ scanning %s ]\n", busname); while((de = readdir(devdir))) { - unsigned char devdesc[256]; + unsigned char devdesc[4096]; unsigned char* bufptr = devdesc; + unsigned char* bufend; struct usb_device_descriptor* device; struct usb_config_descriptor* config; struct usb_interface_descriptor* interface; struct usb_endpoint_descriptor *ep1, *ep2; unsigned zero_mask = 0; unsigned vid, pid; - int i, interfaces; size_t desclength; if(badname(de->d_name)) continue; @@ -173,6 +174,7 @@ static void find_usb_device(const char *base, } desclength = adb_read(fd, devdesc, sizeof(devdesc)); + bufend = bufptr + desclength; // should have device and configuration descriptors, and atleast two endpoints if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) { @@ -189,9 +191,8 @@ static void find_usb_device(const char *base, continue; } - vid = __le16_to_cpu(device->idVendor); - pid = __le16_to_cpu(device->idProduct); - pid = devdesc[10] | (devdesc[11] << 8); + vid = device->idVendor; + pid = device->idProduct; DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid); // should have config descriptor next @@ -203,75 +204,73 @@ static void find_usb_device(const char *base, continue; } - // loop through all the interfaces and look for the ADB interface - interfaces = config->bNumInterfaces; - for (i = 0; i < interfaces; i++) { - if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength) - break; - - interface = (struct usb_interface_descriptor *)bufptr; - bufptr += USB_DT_INTERFACE_SIZE; - if (interface->bLength != USB_DT_INTERFACE_SIZE || - interface->bDescriptorType != USB_DT_INTERFACE) { - D("usb_interface_descriptor not found\n"); - break; - } - - DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d," - "bInterfaceProtocol: %d, bNumEndpoints: %d\n", - interface->bInterfaceClass, interface->bInterfaceSubClass, - interface->bInterfaceProtocol, interface->bNumEndpoints); - - if (interface->bNumEndpoints == 2 && - is_adb_interface(vid, pid, interface->bInterfaceClass, - interface->bInterfaceSubClass, interface->bInterfaceProtocol)) { - - DBGX("looking for bulk endpoints\n"); - // looks like ADB... - ep1 = (struct usb_endpoint_descriptor *)bufptr; - bufptr += USB_DT_ENDPOINT_SIZE; - ep2 = (struct usb_endpoint_descriptor *)bufptr; - bufptr += USB_DT_ENDPOINT_SIZE; - - if (bufptr > devdesc + desclength || - ep1->bLength != USB_DT_ENDPOINT_SIZE || - ep1->bDescriptorType != USB_DT_ENDPOINT || - ep2->bLength != USB_DT_ENDPOINT_SIZE || - ep2->bDescriptorType != USB_DT_ENDPOINT) { - D("endpoints not found\n"); - break; - } + // loop through all the descriptors and look for the ADB interface + while (bufptr < bufend) { + unsigned char length = bufptr[0]; + unsigned char type = bufptr[1]; - // both endpoints should be bulk - if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK || - ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) { - D("bulk endpoints not found\n"); - continue; - } + if (type == USB_DT_INTERFACE) { + interface = (struct usb_interface_descriptor *)bufptr; + bufptr += length; - /* aproto 01 needs 0 termination */ - if(interface->bInterfaceProtocol == 0x01) { - zero_mask = ep1->wMaxPacketSize - 1; + if (length != USB_DT_INTERFACE_SIZE) { + D("interface descriptor has wrong size\n"); + break; } - // we have a match. now we just need to figure out which is in and which is out. - if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { - local_ep_in = ep1->bEndpointAddress; - local_ep_out = ep2->bEndpointAddress; - } else { - local_ep_in = ep2->bEndpointAddress; - local_ep_out = ep1->bEndpointAddress; + DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d," + "bInterfaceProtocol: %d, bNumEndpoints: %d\n", + interface->bInterfaceClass, interface->bInterfaceSubClass, + interface->bInterfaceProtocol, interface->bNumEndpoints); + + if (interface->bNumEndpoints == 2 && + is_adb_interface(vid, pid, interface->bInterfaceClass, + interface->bInterfaceSubClass, interface->bInterfaceProtocol)) { + + DBGX("looking for bulk endpoints\n"); + // looks like ADB... + ep1 = (struct usb_endpoint_descriptor *)bufptr; + bufptr += USB_DT_ENDPOINT_SIZE; + ep2 = (struct usb_endpoint_descriptor *)bufptr; + bufptr += USB_DT_ENDPOINT_SIZE; + + if (bufptr > devdesc + desclength || + ep1->bLength != USB_DT_ENDPOINT_SIZE || + ep1->bDescriptorType != USB_DT_ENDPOINT || + ep2->bLength != USB_DT_ENDPOINT_SIZE || + ep2->bDescriptorType != USB_DT_ENDPOINT) { + D("endpoints not found\n"); + break; + } + + // both endpoints should be bulk + if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK || + ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) { + D("bulk endpoints not found\n"); + continue; + } + /* aproto 01 needs 0 termination */ + if(interface->bInterfaceProtocol == 0x01) { + zero_mask = ep1->wMaxPacketSize - 1; + } + + // we have a match. now we just need to figure out which is in and which is out. + if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + local_ep_in = ep1->bEndpointAddress; + local_ep_out = ep2->bEndpointAddress; + } else { + local_ep_in = ep2->bEndpointAddress; + local_ep_out = ep1->bEndpointAddress; + } + + register_device_callback(devname, local_ep_in, local_ep_out, + interface->bInterfaceNumber, device->iSerialNumber, zero_mask); + break; } - - register_device_callback(devname, local_ep_in, local_ep_out, - interface->bInterfaceNumber, device->iSerialNumber, zero_mask); - - break; } else { - // seek next interface descriptor - bufptr += (USB_DT_ENDPOINT_SIZE * interface->bNumEndpoints); - } - } // end of for + bufptr += length; + } + } // end of while adb_close(fd); } // end of devdir while @@ -288,6 +287,8 @@ static int usb_bulk_write(usb_handle *h, const void *data, int len) { struct usbdevfs_urb *urb = &h->urb_out; int res; + struct timeval tv; + struct timespec ts; memset(urb, 0, sizeof(*urb)); urb->type = USBDEVFS_URB_TYPE_BULK; @@ -314,8 +315,12 @@ static int usb_bulk_write(usb_handle *h, const void *data, int len) res = -1; h->urb_out_busy = 1; for(;;) { - adb_cond_wait(&h->notify, &h->lock); - if(h->dead) { + /* time out after five seconds */ + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec + 5; + ts.tv_nsec = tv.tv_usec * 1000L; + res = pthread_cond_timedwait(&h->notify, &h->lock, &ts); + if(res < 0 || h->dead) { break; } if(h->urb_out_busy == 0) { @@ -364,6 +369,7 @@ static int usb_bulk_read(usb_handle *h, void *data, int len) h->reaper_thread = pthread_self(); adb_mutex_unlock(&h->lock); res = ioctl(h->desc, USBDEVFS_REAPURB, &out); + int saved_errno = errno; adb_mutex_lock(&h->lock); h->reaper_thread = 0; if(h->dead) { @@ -371,7 +377,7 @@ static int usb_bulk_read(usb_handle *h, void *data, int len) break; } if(res < 0) { - if(errno == EINTR) { + if(saved_errno == EINTR) { continue; } D("[ reap urb - error ]\n"); @@ -599,6 +605,7 @@ static void register_device(const char *dev_name, ctrl.wIndex = 0; ctrl.wLength = sizeof(languages); ctrl.data = languages; + ctrl.timeout = 1000; result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); if (result > 0) @@ -611,9 +618,10 @@ static void register_device(const char *dev_name, ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; ctrl.wValue = (USB_DT_STRING << 8) | serial_index; - ctrl.wIndex = languages[i]; + ctrl.wIndex = __le16_to_cpu(languages[i]); ctrl.wLength = sizeof(buffer); ctrl.data = buffer; + ctrl.timeout = 1000; result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); if (result > 0) { @@ -621,7 +629,7 @@ static void register_device(const char *dev_name, // skip first word, and copy the rest to the serial string, changing shorts to bytes. result /= 2; for (i = 1; i < result; i++) - serial[i - 1] = buffer[i]; + serial[i - 1] = __le16_to_cpu(buffer[i]); serial[i - 1] = 0; break; } @@ -680,4 +688,3 @@ void usb_init() fatal_errno("cannot create input thread"); } } - diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index 0a21c6f2ca3..635fa4bbb61 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -83,14 +83,14 @@ int usb_write(usb_handle *h, const void *data, int len) { int n; - D("[ write %d ]\n", len); + D("about to write (fd=%d, len=%d)\n", h->fd, len); n = adb_write(h->fd, data, len); if(n != len) { - D("ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->fd, n, errno, strerror(errno)); return -1; } - D("[ done ]\n"); + D("[ done fd=%d ]\n", h->fd); return 0; } @@ -98,13 +98,14 @@ int usb_read(usb_handle *h, void *data, int len) { int n; - D("[ read %d ]\n", len); + D("about to read (fd=%d, len=%d)\n", h->fd, len); n = adb_read(h->fd, data, len); if(n != len) { - D("ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->fd, n, errno, strerror(errno)); return -1; } + D("[ done fd=%d ]\n", h->fd); return 0; } diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index ce7d5aa9a54..1fd31d8f011 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -51,10 +51,80 @@ #define VENDOR_ID_ACER 0x0502 // Sony Ericsson's USB Vendor ID #define VENDOR_ID_SONY_ERICSSON 0x0FCE +// Foxconn's USB Vendor ID +#define VENDOR_ID_FOXCONN 0x0489 // Dell's USB Vendor ID #define VENDOR_ID_DELL 0x413c +// Nvidia's USB Vendor ID +#define VENDOR_ID_NVIDIA 0x0955 // Garmin-Asus's USB Vendor ID #define VENDOR_ID_GARMIN_ASUS 0x091E +// Sharp's USB Vendor ID +#define VENDOR_ID_SHARP 0x04dd +// ZTE's USB Vendor ID +#define VENDOR_ID_ZTE 0x19D2 +// Kyocera's USB Vendor ID +#define VENDOR_ID_KYOCERA 0x0482 +// Pantech's USB Vendor ID +#define VENDOR_ID_PANTECH 0x10A9 +// Qualcomm's USB Vendor ID +#define VENDOR_ID_QUALCOMM 0x05c6 +// On-The-Go-Video's USB Vendor ID +#define VENDOR_ID_OTGV 0x2257 +// NEC's USB Vendor ID +#define VENDOR_ID_NEC 0x0409 +// Panasonic Mobile Communication's USB Vendor ID +#define VENDOR_ID_PMC 0x04DA +// Toshiba's USB Vendor ID +#define VENDOR_ID_TOSHIBA 0x0930 +// SK Telesys's USB Vendor ID +#define VENDOR_ID_SK_TELESYS 0x1F53 +// KT Tech's USB Vendor ID +#define VENDOR_ID_KT_TECH 0x2116 +// Asus's USB Vendor ID +#define VENDOR_ID_ASUS 0x0b05 +// Philips's USB Vendor ID +#define VENDOR_ID_PHILIPS 0x0471 +// Texas Instruments's USB Vendor ID +#define VENDOR_ID_TI 0x0451 +// Funai's USB Vendor ID +#define VENDOR_ID_FUNAI 0x0F1C +// Gigabyte's USB Vendor ID +#define VENDOR_ID_GIGABYTE 0x0414 +// IRiver's USB Vendor ID +#define VENDOR_ID_IRIVER 0x2420 +// Compal's USB Vendor ID +#define VENDOR_ID_COMPAL 0x1219 +// T & A Mobile Phones' USB Vendor ID +#define VENDOR_ID_T_AND_A 0x1BBB +// LenovoMobile's USB Vendor ID +#define VENDOR_ID_LENOVOMOBILE 0x2006 +// Lenovo's USB Vendor ID +#define VENDOR_ID_LENOVO 0x17EF +// Vizio's USB Vendor ID +#define VENDOR_ID_VIZIO 0xE040 +// K-Touch's USB Vendor ID +#define VENDOR_ID_K_TOUCH 0x24E3 +// Pegatron's USB Vendor ID +#define VENDOR_ID_PEGATRON 0x1D4D +// Archos's USB Vendor ID +#define VENDOR_ID_ARCHOS 0x0E79 +// Positivo's USB Vendor ID +#define VENDOR_ID_POSITIVO 0x1662 +// Fujitsu's USB Vendor ID +#define VENDOR_ID_FUJITSU 0x04C5 +// Lumigon's USB Vendor ID +#define VENDOR_ID_LUMIGON 0x25E3 +//Intel's USB Vendor ID +#define VENDOR_ID_INTEL 0x8087 +// Quanta's USB Vendor ID +#define VENDOR_ID_QUANTA 0x0408 +// INQ Mobile's USB Vendor ID +#define VENDOR_ID_INQ_MOBILE 0x2314 +// Sony's USB Vendor ID +#define VENDOR_ID_SONY 0x054C +// Lab126's USB Vendor ID +#define VENDOR_ID_LAB126 0x1949 /** built-in vendor list */ int builtInVendorIds[] = { @@ -66,8 +136,43 @@ int builtInVendorIds[] = { VENDOR_ID_HUAWEI, VENDOR_ID_ACER, VENDOR_ID_SONY_ERICSSON, + VENDOR_ID_FOXCONN, VENDOR_ID_DELL, + VENDOR_ID_NVIDIA, VENDOR_ID_GARMIN_ASUS, + VENDOR_ID_SHARP, + VENDOR_ID_ZTE, + VENDOR_ID_KYOCERA, + VENDOR_ID_PANTECH, + VENDOR_ID_QUALCOMM, + VENDOR_ID_OTGV, + VENDOR_ID_NEC, + VENDOR_ID_PMC, + VENDOR_ID_TOSHIBA, + VENDOR_ID_SK_TELESYS, + VENDOR_ID_KT_TECH, + VENDOR_ID_ASUS, + VENDOR_ID_PHILIPS, + VENDOR_ID_TI, + VENDOR_ID_FUNAI, + VENDOR_ID_GIGABYTE, + VENDOR_ID_IRIVER, + VENDOR_ID_COMPAL, + VENDOR_ID_T_AND_A, + VENDOR_ID_LENOVOMOBILE, + VENDOR_ID_LENOVO, + VENDOR_ID_VIZIO, + VENDOR_ID_K_TOUCH, + VENDOR_ID_PEGATRON, + VENDOR_ID_ARCHOS, + VENDOR_ID_POSITIVO, + VENDOR_ID_FUJITSU, + VENDOR_ID_LUMIGON, + VENDOR_ID_INTEL, + VENDOR_ID_QUANTA, + VENDOR_ID_INQ_MOBILE, + VENDOR_ID_SONY, + VENDOR_ID_LAB126, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) @@ -129,7 +234,7 @@ void usb_vendors_init(void) /* builds the path to the adb vendor id file. returns 0 if success */ int build_path(char* buff, size_t len, const char* format, const char* home) { - if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_ADB_INI) >= len) { + if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_ADB_INI) >= (signed)len) { return 1; } diff --git a/adb/usb_vendors.h b/adb/usb_vendors.h index 43790b904fd..cee23a15e34 100644 --- a/adb/usb_vendors.h +++ b/adb/usb_vendors.h @@ -22,4 +22,4 @@ extern unsigned vendorIdCount; void usb_vendors_init(void); -#endif \ No newline at end of file +#endif diff --git a/adb/usb_windows.c b/adb/usb_windows.c index 38c4cf484f2..b216999bb29 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -246,10 +246,10 @@ usb_handle* do_usb_open(const wchar_t* interface_name) { } // Something went wrong. - errno = GetLastError(); + int saved_errno = GetLastError(); usb_cleanup_handle(ret); free(ret); - SetLastError(errno); + SetLastError(saved_errno); return NULL; } @@ -267,7 +267,7 @@ int usb_write(usb_handle* handle, const void* data, int len) { (unsigned long)len, &written, time_out); - errno = GetLastError(); + int saved_errno = GetLastError(); if (ret) { // Make sure that we've written what we were asked to write @@ -285,9 +285,10 @@ int usb_write(usb_handle* handle, const void* data, int len) { } } else { // assume ERROR_INVALID_HANDLE indicates we are disconnected - if (errno == ERROR_INVALID_HANDLE) + if (saved_errno == ERROR_INVALID_HANDLE) usb_kick(handle); } + errno = saved_errno; } else { D("usb_write NULL handle\n"); SetLastError(ERROR_INVALID_HANDLE); @@ -313,20 +314,21 @@ int usb_read(usb_handle *handle, void* data, int len) { (unsigned long)xfer, &read, time_out); - errno = GetLastError(); - D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno); + int saved_errno = GetLastError(); + D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno); if (ret) { data += read; len -= read; if (len == 0) return 0; - } else if (errno != ERROR_SEM_TIMEOUT) { + } else if (saved_errno != ERROR_SEM_TIMEOUT) { // assume ERROR_INVALID_HANDLE indicates we are disconnected - if (errno == ERROR_INVALID_HANDLE) + if (saved_errno == ERROR_INVALID_HANDLE) usb_kick(handle); break; } + errno = saved_errno; } } else { D("usb_read NULL handle\n"); diff --git a/charger/Android.mk b/charger/Android.mk new file mode 100644 index 00000000000..77116fb6e10 --- /dev/null +++ b/charger/Android.mk @@ -0,0 +1,59 @@ +# Copyright 2011 The Android Open Source Project + +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + charger.c + +LOCAL_MODULE := charger +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) + +LOCAL_C_INCLUDES := bootable/recovery + +LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng +LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libc + +ifneq ($(BOARD_BATTERY_DEVICE_NAME),) +LOCAL_CFLAGS += -DBATTERY_DEVICE_NAME=\"$(BOARD_BATTERY_DEVICE_NAME)\" +endif + +include $(BUILD_EXECUTABLE) + +define _add-charger-image +include $$(CLEAR_VARS) +LOCAL_MODULE := system_core_charger_$(notdir $(1)) +LOCAL_MODULE_STEM := $(notdir $(1)) +_img_modules += $$(LOCAL_MODULE) +LOCAL_SRC_FILES := $1 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger +include $$(BUILD_PREBUILT) +endef + +_img_modules := +_images := +ifneq ($(BOARD_CHARGER_RES),) +$(foreach _img, $(call find-subdir-subdir-files, ../../../$(BOARD_CHARGER_RES), "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) +else +$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := charger_res_images +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := $(_img_modules) +include $(BUILD_PHONY_PACKAGE) + +_add-charger-image := +_img_modules := + +endif diff --git a/charger/charger.c b/charger/charger.c new file mode 100644 index 00000000000..589a6bf16a1 --- /dev/null +++ b/charger/charger.c @@ -0,0 +1,1002 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define DEBUG_UEVENTS +#define CHARGER_KLOG_LEVEL 6 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "minui/minui.h" + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#define MSEC_PER_SEC (1000LL) +#define NSEC_PER_MSEC (1000000LL) + +#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC) +#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) +#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) + +#define BATTERY_FULL_THRESH 95 + +#define LAST_KMSG_PATH "/proc/last_kmsg" +#define LAST_KMSG_MAX_SZ (32 * 1024) + +#if 1 +#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) +#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0) +#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) +#else +#define LOG_NDEBUG 0 +#define LOG_TAG "charger" +#include +#endif + +struct key_state { + bool pending; + bool down; + int64_t timestamp; +}; + +struct power_supply { + struct listnode list; + char name[256]; + char type[32]; + bool online; + bool valid; + char cap_path[PATH_MAX]; +}; + +struct frame { + const char *name; + int disp_time; + int min_capacity; + bool level_only; + + gr_surface surface; +}; + +struct animation { + bool run; + + struct frame *frames; + int cur_frame; + int num_frames; + + int cur_cycle; + int num_cycles; + + /* current capacity being animated */ + int capacity; +}; + +struct charger { + int64_t next_screen_transition; + int64_t next_key_check; + int64_t next_pwr_check; + + struct key_state keys[KEY_MAX + 1]; + int uevent_fd; + + struct listnode supplies; + int num_supplies; + int num_supplies_online; + + struct animation *batt_anim; + gr_surface surf_unknown; + + struct power_supply *battery; +}; + +struct uevent { + const char *action; + const char *path; + const char *subsystem; + const char *ps_name; + const char *ps_type; + const char *ps_online; +}; + +static struct frame batt_anim_frames[] = { + { + .name = "charger/battery_0", + .disp_time = 750, + .min_capacity = 0, + }, + { + .name = "charger/battery_1", + .disp_time = 750, + .min_capacity = 20, + }, + { + .name = "charger/battery_2", + .disp_time = 750, + .min_capacity = 40, + }, + { + .name = "charger/battery_3", + .disp_time = 750, + .min_capacity = 60, + }, + { + .name = "charger/battery_4", + .disp_time = 750, + .min_capacity = 80, + .level_only = true, + }, + { + .name = "charger/battery_5", + .disp_time = 750, + .min_capacity = BATTERY_FULL_THRESH, + }, +}; + +static struct animation battery_animation = { + .frames = batt_anim_frames, + .num_frames = ARRAY_SIZE(batt_anim_frames), + .num_cycles = 3, +}; + +static struct charger charger_state = { + .batt_anim = &battery_animation, +}; + +static int char_width; +static int char_height; + +/* current time in milliseconds */ +static int64_t curr_time_ms(void) +{ + struct timespec tm; + clock_gettime(CLOCK_MONOTONIC, &tm); + return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC); +} + +static void clear_screen(void) +{ + gr_color(0, 0, 0, 255); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); +}; + +#define MAX_KLOG_WRITE_BUF_SZ 256 + +static void dump_last_kmsg(void) +{ + char *buf; + char *ptr; + unsigned sz = 0; + int len; + + LOGI("\n"); + LOGI("*************** LAST KMSG ***************\n"); + LOGI("\n"); + buf = load_file(LAST_KMSG_PATH, &sz); + if (!buf || !sz) { + LOGI("last_kmsg not found. Cold reset?\n"); + goto out; + } + + len = min(sz, LAST_KMSG_MAX_SZ); + ptr = buf + (sz - len); + + while (len > 0) { + int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ); + char yoink; + char *nl; + + nl = memrchr(ptr, '\n', cnt - 1); + if (nl) + cnt = nl - ptr + 1; + + yoink = ptr[cnt]; + ptr[cnt] = '\0'; + klog_write(6, "<6>%s", ptr); + ptr[cnt] = yoink; + + len -= cnt; + ptr += cnt; + } + + free(buf); + +out: + LOGI("\n"); + LOGI("************* END LAST KMSG *************\n"); + LOGI("\n"); +} + +static int read_file(const char *path, char *buf, size_t sz) +{ + int fd; + size_t cnt; + + fd = open(path, O_RDONLY, 0); + if (fd < 0) + goto err; + + cnt = read(fd, buf, sz - 1); + if (cnt <= 0) + goto err; + buf[cnt] = '\0'; + if (buf[cnt - 1] == '\n') { + cnt--; + buf[cnt] = '\0'; + } + + close(fd); + return cnt; + +err: + if (fd >= 0) + close(fd); + return -1; +} + +static int read_file_int(const char *path, int *val) +{ + char buf[32]; + int ret; + int tmp; + char *end; + + ret = read_file(path, buf, sizeof(buf)); + if (ret < 0) + return -1; + + tmp = strtol(buf, &end, 0); + if (end == buf || + ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0'))) + goto err; + + *val = tmp; + return 0; + +err: + return -1; +} + +static int get_battery_capacity(struct charger *charger) +{ + int ret; + int batt_cap = -1; + + if (!charger->battery) + return -1; + + ret = read_file_int(charger->battery->cap_path, &batt_cap); + if (ret < 0 || batt_cap > 100) { + batt_cap = -1; + } + + return batt_cap; +} + +static struct power_supply *find_supply(struct charger *charger, + const char *name) +{ + struct listnode *node; + struct power_supply *supply; + + list_for_each(node, &charger->supplies) { + supply = node_to_item(node, struct power_supply, list); + if (!strncmp(name, supply->name, sizeof(supply->name))) + return supply; + } + return NULL; +} + +static struct power_supply *add_supply(struct charger *charger, + const char *name, const char *type, + const char *path, bool online) +{ + struct power_supply *supply; + + supply = calloc(1, sizeof(struct power_supply)); + if (!supply) + return NULL; + + strlcpy(supply->name, name, sizeof(supply->name)); + strlcpy(supply->type, type, sizeof(supply->type)); + snprintf(supply->cap_path, sizeof(supply->cap_path), + "/sys/%s/capacity", path); + supply->online = online; + list_add_tail(&charger->supplies, &supply->list); + charger->num_supplies++; + LOGV("... added %s %s %d\n", supply->name, supply->type, online); + return supply; +} + +static void remove_supply(struct charger *charger, struct power_supply *supply) +{ + if (!supply) + return; + list_remove(&supply->list); + charger->num_supplies--; + free(supply); +} + +static void parse_uevent(const char *msg, struct uevent *uevent) +{ + uevent->action = ""; + uevent->path = ""; + uevent->subsystem = ""; + uevent->ps_name = ""; + uevent->ps_online = ""; + uevent->ps_type = ""; + + /* currently ignoring SEQNUM */ + while (*msg) { +#ifdef DEBUG_UEVENTS + LOGV("uevent str: %s\n", msg); +#endif + if (!strncmp(msg, "ACTION=", 7)) { + msg += 7; + uevent->action = msg; + } else if (!strncmp(msg, "DEVPATH=", 8)) { + msg += 8; + uevent->path = msg; + } else if (!strncmp(msg, "SUBSYSTEM=", 10)) { + msg += 10; + uevent->subsystem = msg; + } else if (!strncmp(msg, "POWER_SUPPLY_NAME=", 18)) { + msg += 18; + uevent->ps_name = msg; + } else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) { + msg += 20; + uevent->ps_online = msg; + } else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) { + msg += 18; + uevent->ps_type = msg; + } + + /* advance to after the next \0 */ + while (*msg++) + ; + } + + LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n", + uevent->action, uevent->path, uevent->subsystem, + uevent->ps_name, uevent->ps_type, uevent->ps_online); +} + +static void process_ps_uevent(struct charger *charger, struct uevent *uevent) +{ + int online; + char ps_type[32]; + struct power_supply *supply = NULL; + int i; + bool was_online = false; + bool battery = false; + + if (uevent->ps_type[0] == '\0') { + char *path; + int ret; + + if (uevent->path[0] == '\0') + return; + ret = asprintf(&path, "/sys/%s/type", uevent->path); + if (ret <= 0) + return; + ret = read_file(path, ps_type, sizeof(ps_type)); + free(path); + if (ret < 0) + return; + } else { + strlcpy(ps_type, uevent->ps_type, sizeof(ps_type)); + } + +#ifdef BATTERY_DEVICE_NAME + // We only want to look at one device + if (strcmp(BATTERY_DEVICE_NAME, uevent->ps_name) != 0) + return; +#endif + + if (!strncmp(ps_type, "Battery", 7)) + battery = true; + + online = atoi(uevent->ps_online); + supply = find_supply(charger, uevent->ps_name); + if (supply) { + was_online = supply->online; + supply->online = online; + } + + if (!strcmp(uevent->action, "add")) { + if (!supply) { + supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path, + online); + if (!supply) { + LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name, + uevent->ps_type, online); + return; + } + /* only pick up the first battery for now */ + if (battery && !charger->battery) + charger->battery = supply; + } else { + LOGE("supply '%s' already exists..\n", uevent->ps_name); + } + } else if (!strcmp(uevent->action, "remove")) { + if (supply) { + if (charger->battery == supply) + charger->battery = NULL; + remove_supply(charger, supply); + supply = NULL; + } + } else if (!strcmp(uevent->action, "change")) { + if (!supply) { + LOGE("power supply '%s' not found ('%s' %d)\n", + uevent->ps_name, ps_type, online); + } + } else { + return; + } + + /* allow battery to be managed in the supply list but make it not + * contribute to online power supplies. */ +#ifndef BATTERY_DEVICE_NAME + if (!battery) { +#endif + if (was_online && !online) + charger->num_supplies_online--; + else if (supply && !was_online && online) + charger->num_supplies_online++; +#ifndef BATTERY_DEVICE_NAME + } +#endif + LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n", + uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline", + uevent->action, charger->num_supplies_online, charger->num_supplies); +} + +static void process_uevent(struct charger *charger, struct uevent *uevent) +{ + if (!strcmp(uevent->subsystem, "power_supply")) + process_ps_uevent(charger, uevent); +} + +#define UEVENT_MSG_LEN 1024 +static int handle_uevent_fd(struct charger *charger, int fd) +{ + char msg[UEVENT_MSG_LEN+2]; + int n; + + if (fd < 0) + return -1; + + while (true) { + struct uevent uevent; + + n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN); + if (n <= 0) + break; + if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ + continue; + + msg[n] = '\0'; + msg[n+1] = '\0'; + + parse_uevent(msg, &uevent); + process_uevent(charger, &uevent); + } + + return 0; +} + +static int uevent_callback(int fd, short revents, void *data) +{ + struct charger *charger = data; + + if (!(revents & POLLIN)) + return -1; + return handle_uevent_fd(charger, fd); +} + +/* force the kernel to regenerate the change events for the existing + * devices, if valid */ +static void do_coldboot(struct charger *charger, DIR *d, const char *event, + bool follow_links, int max_depth) +{ + struct dirent *de; + int dfd, fd; + + dfd = dirfd(d); + + fd = openat(dfd, "uevent", O_WRONLY); + if (fd >= 0) { + write(fd, event, strlen(event)); + close(fd); + handle_uevent_fd(charger, charger->uevent_fd); + } + + while ((de = readdir(d)) && max_depth > 0) { + DIR *d2; + + LOGV("looking at '%s'\n", de->d_name); + + if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) || + de->d_name[0] == '.') { + LOGV("skipping '%s' type %d (depth=%d follow=%d)\n", + de->d_name, de->d_type, max_depth, follow_links); + continue; + } + LOGV("can descend into '%s'\n", de->d_name); + + fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); + if (fd < 0) { + LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name, + errno, strerror(errno)); + continue; + } + + d2 = fdopendir(fd); + if (d2 == 0) + close(fd); + else { + LOGV("opened '%s'\n", de->d_name); + do_coldboot(charger, d2, event, follow_links, max_depth - 1); + closedir(d2); + } + } +} + +static void coldboot(struct charger *charger, const char *path, + const char *event) +{ + char str[256]; + + LOGV("doing coldboot '%s' in '%s'\n", event, path); + DIR *d = opendir(path); + if (d) { + snprintf(str, sizeof(str), "%s\n", event); + do_coldboot(charger, d, str, true, 1); + closedir(d); + } +} + +static int draw_text(const char *str, int x, int y) +{ + int str_len_px = gr_measure(str); + + if (x < 0) + x = (gr_fb_width() - str_len_px) / 2; + if (y < 0) + y = (gr_fb_height() - char_height) / 2; + gr_text(x, y, str); + + return y + char_height; +} + +static void android_green(void) +{ + gr_color(0xa4, 0xc6, 0x39, 255); +} + +/* returns the last y-offset of where the surface ends */ +static int draw_surface_centered(struct charger *charger, gr_surface surface) +{ + int w; + int h; + int x; + int y; + + w = gr_get_width(surface); + h = gr_get_height(surface); + x = (gr_fb_width() - w) / 2 ; + y = (gr_fb_height() - h) / 2 ; + + LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y); + gr_blit(surface, 0, 0, w, h, x, y); + return y + h; +} + +static void draw_unknown(struct charger *charger) +{ + int y; + if (charger->surf_unknown) { + draw_surface_centered(charger, charger->surf_unknown); + } else { + android_green(); + y = draw_text("Charging!", -1, -1); + draw_text("?\?/100", -1, y + 25); + } +} + +static void draw_battery(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + struct frame *frame = &batt_anim->frames[batt_anim->cur_frame]; + + if (batt_anim->num_frames != 0) { + draw_surface_centered(charger, frame->surface); + LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n", + batt_anim->cur_frame, frame->name, frame->min_capacity, + frame->disp_time); + } +} + +static void redraw_screen(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + + clear_screen(); + + /* try to display *something* */ + if (batt_anim->capacity < 0 || batt_anim->num_frames == 0) + draw_unknown(charger); + else + draw_battery(charger); + gr_flip(); +} + +static void kick_animation(struct animation *anim) +{ + anim->run = true; +} + +static void reset_animation(struct animation *anim) +{ + anim->cur_cycle = 0; + anim->cur_frame = 0; + anim->run = false; +} + +static void update_screen_state(struct charger *charger, int64_t now) +{ + struct animation *batt_anim = charger->batt_anim; + int cur_frame; + int disp_time; + + if (!batt_anim->run || now < charger->next_screen_transition) + return; + + /* animation is over, blank screen and leave */ + if (batt_anim->cur_cycle == batt_anim->num_cycles) { + reset_animation(batt_anim); + charger->next_screen_transition = -1; + gr_fb_blank(true); + LOGV("[%lld] animation done\n", now); + return; + } + + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time; + + /* animation starting, set up the animation */ + if (batt_anim->cur_frame == 0) { + int batt_cap; + int ret; + + LOGV("[%lld] animation starting\n", now); + batt_cap = get_battery_capacity(charger); + if (batt_cap >= 0 && batt_anim->num_frames != 0) { + int i; + + /* find first frame given current capacity */ + for (i = 1; i < batt_anim->num_frames; i++) { + if (batt_cap < batt_anim->frames[i].min_capacity) + break; + } + batt_anim->cur_frame = i - 1; + + /* show the first frame for twice as long */ + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2; + } + + batt_anim->capacity = batt_cap; + } + + /* unblank the screen on first cycle */ + if (batt_anim->cur_cycle == 0) + gr_fb_blank(false); + + /* draw the new frame (@ cur_frame) */ + redraw_screen(charger); + + /* if we don't have anim frames, we only have one image, so just bump + * the cycle counter and exit + */ + if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) { + LOGV("[%lld] animation missing or unknown battery status\n", now); + charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; + batt_anim->cur_cycle++; + return; + } + + /* schedule next screen transition */ + charger->next_screen_transition = now + disp_time; + + /* advance frame cntr to the next valid frame + * if necessary, advance cycle cntr, and reset frame cntr + */ + batt_anim->cur_frame++; + + /* if the frame is used for level-only, that is only show it when it's + * the current level, skip it during the animation. + */ + while (batt_anim->cur_frame < batt_anim->num_frames && + batt_anim->frames[batt_anim->cur_frame].level_only) + batt_anim->cur_frame++; + if (batt_anim->cur_frame >= batt_anim->num_frames) { + batt_anim->cur_cycle++; + batt_anim->cur_frame = 0; + + /* don't reset the cycle counter, since we use that as a signal + * in a test above to check if animation is over + */ + } +} + +static int set_key_callback(int code, int value, void *data) +{ + struct charger *charger = data; + int64_t now = curr_time_ms(); + int down = !!value; + + if (code > KEY_MAX) + return -1; + + /* ignore events that don't modify our state */ + if (charger->keys[code].down == down) + return 0; + + /* only record the down even timestamp, as the amount + * of time the key spent not being pressed is not useful */ + if (down) + charger->keys[code].timestamp = now; + charger->keys[code].down = down; + charger->keys[code].pending = true; + if (down) { + LOGV("[%lld] key[%d] down\n", now, code); + } else { + int64_t duration = now - charger->keys[code].timestamp; + int64_t secs = duration / 1000; + int64_t msecs = duration - secs * 1000; + LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now, + code, secs, msecs); + } + + return 0; +} + +static void update_input_state(struct charger *charger, + struct input_event *ev) +{ + if (ev->type != EV_KEY) + return; + set_key_callback(ev->code, ev->value, charger); +} + +static void set_next_key_check(struct charger *charger, + struct key_state *key, + int64_t timeout) +{ + int64_t then = key->timestamp + timeout; + + if (charger->next_key_check == -1 || then < charger->next_key_check) + charger->next_key_check = then; +} + +static void process_key(struct charger *charger, int code, int64_t now) +{ + struct key_state *key = &charger->keys[code]; + int64_t next_key_check; + + if (code == KEY_POWER) { + if (key->down) { + int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; + if (now >= reboot_timeout) { + LOGI("[%lld] rebooting\n", now); + android_reboot(ANDROID_RB_RESTART, 0, 0); + } else { + /* if the key is pressed but timeout hasn't expired, + * make sure we wake up at the right-ish time to check + */ + set_next_key_check(charger, key, POWER_ON_KEY_TIME); + } + } else { + /* if the power key got released, force screen state cycle */ + if (key->pending) + kick_animation(charger->batt_anim); + } + } + + key->pending = false; +} + +static void handle_input_state(struct charger *charger, int64_t now) +{ + process_key(charger, KEY_POWER, now); + + if (charger->next_key_check != -1 && now > charger->next_key_check) + charger->next_key_check = -1; +} + +static void handle_power_supply_state(struct charger *charger, int64_t now) +{ + if (charger->num_supplies_online == 0) { + if (charger->next_pwr_check == -1) { + charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; + LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n", + now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check); + } else if (now >= charger->next_pwr_check) { + LOGI("[%lld] shutting down\n", now); + android_reboot(ANDROID_RB_POWEROFF, 0, 0); + } else { + /* otherwise we already have a shutdown timer scheduled */ + } + } else { + /* online supply present, reset shutdown timer if set */ + if (charger->next_pwr_check != -1) { + LOGI("[%lld] device plugged in: shutdown cancelled\n", now); + kick_animation(charger->batt_anim); + } + charger->next_pwr_check = -1; + } +} + +static void wait_next_event(struct charger *charger, int64_t now) +{ + int64_t next_event = INT64_MAX; + int64_t timeout; + struct input_event ev; + int ret; + + LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now, + charger->next_screen_transition, charger->next_key_check, + charger->next_pwr_check); + + if (charger->next_screen_transition != -1) + next_event = charger->next_screen_transition; + if (charger->next_key_check != -1 && charger->next_key_check < next_event) + next_event = charger->next_key_check; + if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event) + next_event = charger->next_pwr_check; + + if (next_event != -1 && next_event != INT64_MAX) + timeout = max(0, next_event - now); + else + timeout = -1; + LOGV("[%lld] blocking (%lld)\n", now, timeout); + ret = ev_wait((int)timeout); + if (!ret) + ev_dispatch(); +} + +static int input_callback(int fd, short revents, void *data) +{ + struct charger *charger = data; + struct input_event ev; + int ret; + + ret = ev_get_input(fd, revents, &ev); + if (ret) + return -1; + update_input_state(charger, &ev); + return 0; +} + +static void event_loop(struct charger *charger) +{ + int ret; + + while (true) { + int64_t now = curr_time_ms(); + + LOGV("[%lld] event_loop()\n", now); + handle_input_state(charger, now); + handle_power_supply_state(charger, now); + + /* do screen update last in case any of the above want to start + * screen transitions (animations, etc) + */ + update_screen_state(charger, now); + + wait_next_event(charger, now); + } +} + +int main(int argc, char **argv) +{ + int ret; + struct charger *charger = &charger_state; + int64_t now = curr_time_ms() - 1; + int fd; + int i; + + list_init(&charger->supplies); + + klog_init(); + klog_set_level(CHARGER_KLOG_LEVEL); + + dump_last_kmsg(); + + LOGI("--------------- STARTING CHARGER MODE ---------------\n"); + + gr_init(); + gr_font_size(&char_width, &char_height); + + ev_init(input_callback, charger); + + fd = uevent_open_socket(64*1024, true); + if (fd >= 0) { + fcntl(fd, F_SETFL, O_NONBLOCK); + ev_add_fd(fd, uevent_callback, charger); + } + charger->uevent_fd = fd; + coldboot(charger, "/sys/class/power_supply", "add"); + + ret = res_create_surface("charger/battery_fail", &charger->surf_unknown); + if (ret < 0) { + LOGE("Cannot load image\n"); + charger->surf_unknown = NULL; + } + + for (i = 0; i < charger->batt_anim->num_frames; i++) { + struct frame *frame = &charger->batt_anim->frames[i]; + + ret = res_create_surface(frame->name, &frame->surface); + if (ret < 0) { + LOGE("Cannot load image %s\n", frame->name); + /* TODO: free the already allocated surfaces... */ + charger->batt_anim->num_frames = 0; + charger->batt_anim->num_cycles = 1; + break; + } + } + + ev_sync_key_state(set_key_callback, charger); + + gr_fb_blank(true); + + charger->next_screen_transition = now - 1; + charger->next_key_check = -1; + charger->next_pwr_check = -1; + reset_animation(charger->batt_anim); + kick_animation(charger->batt_anim); + + event_loop(charger); + + return 0; +} diff --git a/charger/images/battery_0.png b/charger/images/battery_0.png new file mode 100644 index 00000000000..2347074b2af Binary files /dev/null and b/charger/images/battery_0.png differ diff --git a/charger/images/battery_1.png b/charger/images/battery_1.png new file mode 100644 index 00000000000..cd34620efab Binary files /dev/null and b/charger/images/battery_1.png differ diff --git a/charger/images/battery_2.png b/charger/images/battery_2.png new file mode 100644 index 00000000000..3e4095e8fd8 Binary files /dev/null and b/charger/images/battery_2.png differ diff --git a/charger/images/battery_3.png b/charger/images/battery_3.png new file mode 100644 index 00000000000..08c155125de Binary files /dev/null and b/charger/images/battery_3.png differ diff --git a/charger/images/battery_4.png b/charger/images/battery_4.png new file mode 100644 index 00000000000..3a678da09f2 Binary files /dev/null and b/charger/images/battery_4.png differ diff --git a/charger/images/battery_5.png b/charger/images/battery_5.png new file mode 100644 index 00000000000..d8dc40e8766 Binary files /dev/null and b/charger/images/battery_5.png differ diff --git a/charger/images/battery_charge.png b/charger/images/battery_charge.png new file mode 100644 index 00000000000..b501933a08e Binary files /dev/null and b/charger/images/battery_charge.png differ diff --git a/charger/images/battery_fail.png b/charger/images/battery_fail.png new file mode 100644 index 00000000000..36fc254d054 Binary files /dev/null and b/charger/images/battery_fail.png differ diff --git a/cpio/Android.mk b/cpio/Android.mk index 8d01852b0c9..5184463bccf 100644 --- a/cpio/Android.mk +++ b/cpio/Android.mk @@ -10,4 +10,4 @@ LOCAL_MODULE := mkbootfs include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index b22e1a81d75..6cfe79b1428 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -1,26 +1,53 @@ # Copyright 2005 The Android Open Source Project -ifeq ($(TARGET_ARCH),arm) +ifneq ($(filter arm x86,$(TARGET_ARCH)),) LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c +LOCAL_SRC_FILES:= debuggerd.c utility.c getevent.c $(TARGET_ARCH)/machine.c $(TARGET_ARCH)/unwind.c symbol_table.c +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += $(TARGET_ARCH)/pr-support.c +endif + LOCAL_CFLAGS := -Wall LOCAL_MODULE := debuggerd +ifeq ($(ARCH_ARM_HAVE_VFP),true) +LOCAL_CFLAGS += -DWITH_VFP +endif # ARCH_ARM_HAVE_VFP +ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) +LOCAL_CFLAGS += -DWITH_VFP_D32 +endif # ARCH_ARM_HAVE_VFP_D32 + LOCAL_STATIC_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := crasher.c -LOCAL_SRC_FILES += crashglue.S -LOCAL_MODULE := crasher +LOCAL_SRC_FILES := crasher.c +LOCAL_SRC_FILES += $(TARGET_ARCH)/crashglue.S +LOCAL_MODULE := crasher LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) -endif # TARGET_ARCH == arm +ifeq ($(ARCH_ARM_HAVE_VFP),true) +include $(CLEAR_VARS) + +LOCAL_CFLAGS += -DWITH_VFP +ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) +LOCAL_CFLAGS += -DWITH_VFP_D32 +endif # ARCH_ARM_HAVE_VFP_D32 + +LOCAL_SRC_FILES := vfp-crasher.c vfp.S +LOCAL_MODULE := vfp-crasher +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) +endif # ARCH_ARM_HAVE_VFP == true + +endif # arm or x86 in TARGET_ARCH diff --git a/debuggerd/crashglue.S b/debuggerd/arm/crashglue.S similarity index 87% rename from debuggerd/crashglue.S rename to debuggerd/arm/crashglue.S index 888951b9c47..eb9d0e38ba6 100644 --- a/debuggerd/crashglue.S +++ b/debuggerd/arm/crashglue.S @@ -1,6 +1,8 @@ .globl crash1 +.type crash1, %function .globl crashnostack - +.type crashnostack, %function + crash1: ldr r0, =0xa5a50000 ldr r1, =0xa5a50001 @@ -25,4 +27,4 @@ crashnostack: mov sp, #0 mov r0, #0 ldr r0, [r0] - b . \ No newline at end of file + b . diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c new file mode 100644 index 00000000000..d5efb79ffa7 --- /dev/null +++ b/debuggerd/arm/machine.c @@ -0,0 +1,373 @@ +/* system/debuggerd/debuggerd.c +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "utility.h" + +/* enable to dump memory pointed to by every register */ +#define DUMP_MEM_FOR_ALL_REGS 0 + +#ifdef WITH_VFP +#ifdef WITH_VFP_D32 +#define NUM_VFP_REGS 32 +#else +#define NUM_VFP_REGS 16 +#endif +#endif + +/* Main entry point to get the backtrace from the crashing process */ +extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], + int *frame0_pc_sane, + bool at_fault); + +/* + * If this isn't clearly a null pointer dereference, dump the + * /proc/maps entries near the fault address. + * + * This only makes sense to do on the thread that crashed. + */ +static void show_nearby_maps(int tfd, int pid, mapinfo *map) +{ + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) { + _LOG(tfd, false, "cannot get siginfo for %d: %s\n", + pid, strerror(errno)); + return; + } + if (!signal_has_address(si.si_signo)) + return; + + uintptr_t addr = (uintptr_t) si.si_addr; + addr &= ~0xfff; /* round to 4K page boundary */ + if (addr == 0) /* null-pointer deref */ + return; + + _LOG(tfd, false, "\nmemory map around addr %08x:\n", si.si_addr); + + /* + * Search for a match, or for a hole where the match would be. The list + * is backward from the file content, so it starts at high addresses. + */ + bool found = false; + mapinfo *next = NULL; + mapinfo *prev = NULL; + while (map != NULL) { + if (addr >= map->start && addr < map->end) { + found = true; + next = map->next; + break; + } else if (addr >= map->end) { + /* map would be between "prev" and this entry */ + next = map; + map = NULL; + break; + } + + prev = map; + map = map->next; + } + + /* + * Show "next" then "match" then "prev" so that the addresses appear in + * ascending order (like /proc/pid/maps). + */ + if (next != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", next->start, next->end, next->name); + } else { + _LOG(tfd, false, "(no map below)\n"); + } + if (map != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", map->start, map->end, map->name); + } else { + _LOG(tfd, false, "(no map for address)\n"); + } + if (prev != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", prev->start, prev->end, prev->name); + } else { + _LOG(tfd, false, "(no map above)\n"); + } +} + +/* + * Dumps a few bytes of memory, starting a bit before and ending a bit + * after the specified address. + */ +static void dump_memory(int tfd, int pid, uintptr_t addr, + bool only_in_tombstone) +{ + char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ + char ascii_buffer[32]; /* actual 16 + 1 == 17 */ + uintptr_t p, end; + + p = addr & ~3; + p -= 32; + if (p > addr) { + /* catch underflow */ + p = 0; + } + end = p + 80; + /* catch overflow; 'end - p' has to be multiples of 16 */ + while (end < p) + end -= 16; + + /* Dump the code around PC as: + * addr contents ascii + * 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q + * 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... + */ + while (p < end) { + char* asc_out = ascii_buffer; + + sprintf(code_buffer, "%08x ", p); + + int i; + for (i = 0; i < 4; i++) { + /* + * If we see (data == -1 && errno != 0), we know that the ptrace + * call failed, probably because we're dumping memory in an + * unmapped or inaccessible page. I don't know if there's + * value in making that explicit in the output -- it likely + * just complicates parsing and clarifies nothing for the + * enlightened reader. + */ + long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); + + int j; + for (j = 0; j < 4; j++) { + /* + * Our isprint() allows high-ASCII characters that display + * differently (often badly) in different viewers, so we + * just use a simpler test. + */ + char val = (data >> (j*8)) & 0xff; + if (val >= 0x20 && val < 0x7f) { + *asc_out++ = val; + } else { + *asc_out++ = '.'; + } + } + p += 4; + } + *asc_out = '\0'; + _LOG(tfd, only_in_tombstone, "%s %s\n", code_buffer, ascii_buffer); + } + +} + +void dump_stack_and_code(int tfd, int pid, mapinfo *map, + int unwind_depth, unsigned int sp_list[], + bool at_fault) +{ + struct pt_regs r; + int sp_depth; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; + + if (DUMP_MEM_FOR_ALL_REGS && at_fault) { + /* + * If configured to do so, dump memory around *all* registers + * for the crashing thread. + * + * TODO: remove duplicates. + */ + static const char REG_NAMES[] = "R0R1R2R3R4R5R6R7R8R9SLFPIPSPLRPC"; + + int reg; + for (reg = 0; reg < 16; reg++) { + /* this may not be a valid way to access, but it'll do for now */ + uintptr_t addr = r.uregs[reg]; + + /* + * Don't bother if it looks like a small int or ~= null, or if + * it's in the kernel area. + */ + if (addr < 4096 || addr >= 0xc0000000) { + continue; + } + + _LOG(tfd, only_in_tombstone, "\nmem near %.2s:\n", + ®_NAMES[reg*2]); + dump_memory(tfd, pid, addr, false); + } + } else { + unsigned int pc, lr; + pc = r.ARM_pc; + lr = r.ARM_lr; + + _LOG(tfd, only_in_tombstone, "\ncode around pc:\n"); + dump_memory(tfd, pid, (uintptr_t) pc, only_in_tombstone); + + if (lr != pc) { + _LOG(tfd, only_in_tombstone, "\ncode around lr:\n"); + dump_memory(tfd, pid, (uintptr_t) lr, only_in_tombstone); + } + } + + if (at_fault) { + show_nearby_maps(tfd, pid, map); + } + + unsigned int p, end; + unsigned int sp = r.ARM_sp; + + p = sp - 64; + if (p > sp) + p = 0; + p &= ~3; + if (unwind_depth != 0) { + if (unwind_depth < STACK_CONTENT_DEPTH) { + end = sp_list[unwind_depth-1]; + } + else { + end = sp_list[STACK_CONTENT_DEPTH-1]; + } + } + else { + end = p + 256; + /* 'end - p' has to be multiples of 4 */ + if (end < p) + end = ~7; + } + + _LOG(tfd, only_in_tombstone, "\nstack:\n"); + + /* If the crash is due to PC == 0, there will be two frames that + * have identical SP value. + */ + if (sp_list[0] == sp_list[1]) { + sp_depth = 1; + } + else { + sp_depth = 0; + } + + while (p <= end) { + char *prompt; + char level[16]; + long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + if (p == sp_list[sp_depth]) { + sprintf(level, "#%02d", sp_depth++); + prompt = level; + } + else { + prompt = " "; + } + + /* Print the stack content in the log for the first 3 frames. For the + * rest only print them in the tombstone file. + */ + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + "%s %08x %08x %s\n", prompt, p, data, + map_to_name(map, data, "")); + p += 4; + } + /* print another 64-byte of stack data after the last frame */ + + end = p+64; + /* 'end - p' has to be multiples of 4 */ + if (end < p) + end = ~7; + + while (p <= end) { + long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + " %08x %08x %s\n", p, data, + map_to_name(map, data, "")); + p += 4; + } +} + +void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, + bool at_fault) +{ + struct pt_regs r; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, !at_fault, "tid %d not responding!\n", pid); + return; + } + + if (unwound_level == 0) { + _LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc, + map_to_name(map, r.ARM_pc, "")); + } + _LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr, + map_to_name(map, r.ARM_lr, "")); +} + +void dump_registers(int tfd, int pid, bool at_fault) +{ + struct pt_regs r; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3); + _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7); + _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n", + r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp); + _LOG(tfd, only_in_tombstone, + " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr); + +#ifdef WITH_VFP + struct user_vfp vfp_regs; + int i; + + if(ptrace(PTRACE_GETVFPREGS, pid, 0, &vfp_regs)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } + + for (i = 0; i < NUM_VFP_REGS; i += 2) { + _LOG(tfd, only_in_tombstone, + " d%-2d %016llx d%-2d %016llx\n", + i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); + } + _LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr); +#endif +} diff --git a/debuggerd/pr-support.c b/debuggerd/arm/pr-support.c similarity index 100% rename from debuggerd/pr-support.c rename to debuggerd/arm/pr-support.c diff --git a/debuggerd/unwind-arm.c b/debuggerd/arm/unwind.c similarity index 96% rename from debuggerd/unwind-arm.c rename to debuggerd/arm/unwind.c index 9642d2e4aee..d9600b7730c 100644 --- a/debuggerd/unwind-arm.c +++ b/debuggerd/arm/unwind.c @@ -37,6 +37,8 @@ #include #include "utility.h" +#include "symbol_table.h" + typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); @@ -393,6 +395,7 @@ static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, phase2_vrs *vrs = (phase2_vrs*) context; const mapinfo *mi; bool only_in_tombstone = !at_fault; + const struct symbol* sym = 0; if (stack_level < STACK_CONTENT_DEPTH) { sp_list[stack_level] = vrs->core.r[R_SP]; @@ -427,7 +430,7 @@ static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, if (pc & 1) { _uw prev_word; pc = (pc & ~1); - prev_word = get_remote_word(pid, (void *) pc-4); + prev_word = get_remote_word(pid, (char *) pc-4); // Long offset if ((prev_word & 0xf0000000) == 0xf0000000 && (prev_word & 0x0000e000) == 0x0000e000) { @@ -448,12 +451,22 @@ static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, * 1MB boundaries, and the library may be larger than 1MB. So for .so * addresses we print the relative offset in back trace. */ - rel_pc = pc; mi = pc_to_mapinfo(map, pc, &rel_pc); - _LOG(tfd, only_in_tombstone, - " #%02d pc %08x %s\n", stack_level, rel_pc, - mi ? mi->name : ""); + /* See if we can determine what symbol this stack frame resides in */ + if (mi != 0 && mi->symbols != 0) { + sym = symbol_table_lookup(mi->symbols, rel_pc); + } + + if (sym) { + _LOG(tfd, only_in_tombstone, + " #%02d pc %08x %s (%s)\n", stack_level, rel_pc, + mi ? mi->name : "", sym->name); + } else { + _LOG(tfd, only_in_tombstone, + " #%02d pc %08x %s\n", stack_level, rel_pc, + mi ? mi->name : ""); + } return _URC_NO_REASON; } diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c index f4a5a62bd84..00652e975d9 100644 --- a/debuggerd/crasher.c +++ b/debuggerd/crasher.c @@ -19,6 +19,7 @@ void crash1(void); void crashnostack(void); +void maybeabort(void); static void debuggerd_connect() { diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 996e6c2044f..2acf26d195e 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -16,8 +16,6 @@ */ #include -#include -#include #include #include #include @@ -33,29 +31,25 @@ #include #include -#include +#include #include #include #include +#include "debuggerd.h" #include "utility.h" -/* Main entry point to get the backtrace from the crashing process */ -extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, - unsigned int sp_list[], - int *frame0_pc_sane, - bool at_fault); - -static int logsocket = -1; - #define ANDROID_LOG_INFO 4 +void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + /* Log information onto the tombstone */ void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) { - char buf[128]; + char buf[512]; va_list ap; va_start(ap, fmt); @@ -69,15 +63,9 @@ void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) if (!in_tombstone_only) __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); + va_end(ap); } -#define LOG(fmt...) _LOG(-1, 0, fmt) -#if 0 -#define XLOG(fmt...) _LOG(-1, 0, fmt) -#else -#define XLOG(fmt...) do {} while(0) -#endif - // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so // 012345678901234567890123456789012345678901234567890123456789 // 0 1 2 3 4 5 @@ -87,23 +75,31 @@ mapinfo *parse_maps_line(char *line) mapinfo *mi; int len = strlen(line); - if(len < 1) return 0; + if (len < 1) return 0; /* not expected */ line[--len] = 0; - if(len < 50) return 0; - if(line[20] != 'x') return 0; + if (len < 50) { + mi = malloc(sizeof(mapinfo) + 1); + } else { + mi = malloc(sizeof(mapinfo) + (len - 47)); + } + if (mi == 0) return 0; - mi = malloc(sizeof(mapinfo) + (len - 47)); - if(mi == 0) return 0; + mi->isExecutable = (line[20] == 'x'); mi->start = strtoul(line, 0, 16); mi->end = strtoul(line + 9, 0, 16); - /* To be filled in parse_exidx_info if the mapped section starts with + /* To be filled in parse_elf_info if the mapped section starts with * elf_header */ mi->exidx_start = mi->exidx_end = 0; + mi->symbols = 0; mi->next = 0; - strcpy(mi->name, line + 49); + if (len < 50) { + mi->name[0] = '\0'; + } else { + strcpy(mi->name, line + 49); + } return mi; } @@ -117,176 +113,6 @@ void dump_build_info(int tfd) _LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint); } - -void dump_stack_and_code(int tfd, int pid, mapinfo *map, - int unwind_depth, unsigned int sp_list[], - int frame0_pc_sane, bool at_fault) -{ - unsigned int sp, pc, p, end, data; - struct pt_regs r; - int sp_depth; - bool only_in_tombstone = !at_fault; - char code_buffer[80]; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; - sp = r.ARM_sp; - pc = r.ARM_pc; - - /* Died because calling the weeds - dump - * the code around the PC in the next frame instead. - */ - if (frame0_pc_sane == 0) { - pc = r.ARM_lr; - } - - _LOG(tfd, only_in_tombstone, - "\ncode around %s:\n", frame0_pc_sane ? "pc" : "lr"); - - end = p = pc & ~3; - p -= 16; - end += 16; - - /* Dump the code around PC as: - * addr contents - * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c - * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 - */ - while (p <= end) { - int i; - - sprintf(code_buffer, "%08x ", p); - for (i = 0; i < 4; i++) { - data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - sprintf(code_buffer + strlen(code_buffer), "%08x ", data); - p += 4; - } - _LOG(tfd, only_in_tombstone, "%s\n", code_buffer); - } - - if (frame0_pc_sane) { - _LOG(tfd, only_in_tombstone, "\ncode around lr:\n"); - - end = p = r.ARM_lr & ~3; - p -= 16; - end += 16; - - /* Dump the code around LR as: - * addr contents - * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c - * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 - */ - while (p <= end) { - int i; - - sprintf(code_buffer, "%08x ", p); - for (i = 0; i < 4; i++) { - data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - sprintf(code_buffer + strlen(code_buffer), "%08x ", data); - p += 4; - } - _LOG(tfd, only_in_tombstone, "%s\n", code_buffer); - } - } - - p = sp - 64; - p &= ~3; - if (unwind_depth != 0) { - if (unwind_depth < STACK_CONTENT_DEPTH) { - end = sp_list[unwind_depth-1]; - } - else { - end = sp_list[STACK_CONTENT_DEPTH-1]; - } - } - else { - end = sp | 0x000000ff; - end += 0xff; - } - - _LOG(tfd, only_in_tombstone, "\nstack:\n"); - - /* If the crash is due to PC == 0, there will be two frames that - * have identical SP value. - */ - if (sp_list[0] == sp_list[1]) { - sp_depth = 1; - } - else { - sp_depth = 0; - } - - while (p <= end) { - char *prompt; - char level[16]; - data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - if (p == sp_list[sp_depth]) { - sprintf(level, "#%02d", sp_depth++); - prompt = level; - } - else { - prompt = " "; - } - - /* Print the stack content in the log for the first 3 frames. For the - * rest only print them in the tombstone file. - */ - _LOG(tfd, (sp_depth > 2) || only_in_tombstone, - "%s %08x %08x %s\n", prompt, p, data, - map_to_name(map, data, "")); - p += 4; - } - /* print another 64-byte of stack data after the last frame */ - - end = p+64; - while (p <= end) { - data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - _LOG(tfd, (sp_depth > 2) || only_in_tombstone, - " %08x %08x %s\n", p, data, - map_to_name(map, data, "")); - p += 4; - } -} - -void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, - bool at_fault) -{ - struct pt_regs r; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { - _LOG(tfd, !at_fault, "tid %d not responding!\n", pid); - return; - } - - if (unwound_level == 0) { - _LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc, - map_to_name(map, r.ARM_pc, "")); - } - _LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr, - map_to_name(map, r.ARM_lr, "")); -} - -void dump_registers(int tfd, int pid, bool at_fault) -{ - struct pt_regs r; - bool only_in_tombstone = !at_fault; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { - _LOG(tfd, only_in_tombstone, - "cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", - r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3); - _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", - r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7); - _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n", - r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp); - _LOG(tfd, only_in_tombstone, - " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", - r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr); -} - const char *get_signame(int sig) { switch(sig) { @@ -300,6 +126,50 @@ const char *get_signame(int sig) } } +const char *get_sigcode(int signo, int code) +{ + switch (signo) { + case SIGILL: + switch (code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + break; + case SIGBUS: + switch (code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + } + break; + case SIGFPE: + switch (code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + break; + case SIGSEGV: + switch (code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + case SEGV_ACCERR: return "SEGV_ACCERR"; + } + break; + } + return "?"; +} + void dump_fault_addr(int tfd, int pid, int sig) { siginfo_t si; @@ -307,9 +177,14 @@ void dump_fault_addr(int tfd, int pid, int sig) memset(&si, 0, sizeof(si)); if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); + } else if (signal_has_address(sig)) { + _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n", + sig, get_signame(sig), + si.si_code, get_sigcode(sig, si.si_code), + (uintptr_t) si.si_addr); } else { - _LOG(tfd, false, "signal %d (%s), fault addr %08x\n", - sig, get_signame(sig), si.si_addr); + _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); } } @@ -335,10 +210,13 @@ void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig) if(sig) dump_fault_addr(tfd, tid, sig); } -static void parse_exidx_info(mapinfo *milist, pid_t pid) +static void parse_elf_info(mapinfo *milist, pid_t pid) { mapinfo *mi; for (mi = milist; mi != NULL; mi = mi->next) { + if (!mi->isExecutable) + continue; + Elf32_Ehdr ehdr; memset(&ehdr, 0, sizeof(Elf32_Ehdr)); @@ -356,15 +234,20 @@ static void parse_exidx_info(mapinfo *milist, pid_t pid) ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff); for (i = 0; i < ehdr.e_phnum; i++) { /* Parse the program header */ - get_remote_struct(pid, (char *) ptr+i, &phdr, + get_remote_struct(pid, (char *) (ptr+i), &phdr, sizeof(Elf32_Phdr)); +#ifdef __arm__ /* Found a EXIDX segment? */ if (phdr.p_type == PT_ARM_EXIDX) { mi->exidx_start = mi->start + phdr.p_offset; mi->exidx_end = mi->exidx_start + phdr.p_filesz; break; } +#endif } + + /* Try to load symbols from this file */ + mi->symbols = symbol_table_create(mi->name); } } } @@ -376,7 +259,9 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) mapinfo *milist = 0; unsigned int sp_list[STACK_CONTENT_DEPTH]; int stack_depth; +#ifdef __arm__ int frame0_pc_sane = 1; +#endif if (!at_fault) { _LOG(tfd, true, @@ -402,8 +287,9 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) fclose(fp); } - parse_exidx_info(milist, tid); + parse_elf_info(milist, tid); +#if __arm__ /* If stack unwinder fails, use the default solution to dump the stack * content. */ @@ -417,11 +303,19 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault); } - dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, frame0_pc_sane, - at_fault); + dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, at_fault); +#elif __i386__ + /* If stack unwinder fails, use the default solution to dump the stack + * content. + */ + stack_depth = unwind_backtrace_with_ptrace_x86(tfd, tid, milist,at_fault); +#else +#error "Unsupported architecture" +#endif while(milist) { mapinfo *next = milist->next; + symbol_table_free(milist->symbols); free(milist); milist = next; } @@ -518,12 +412,161 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) continue; dump_crash_report(tfd, pid, new_tid, false); - need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0); + + if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { + XLOG("detach of tid %d failed: %s\n", new_tid, strerror(errno)); + need_cleanup = 1; + } } closedir(d); + return need_cleanup != 0; } +/* + * Reads the contents of the specified log device, filters out the entries + * that don't match the specified pid, and writes them to the tombstone file. + * + * If "tailOnly" is set, we only print the last few lines. + */ +static void dump_log_file(int tfd, unsigned pid, const char* filename, + bool tailOnly) +{ + bool first = true; + + /* circular buffer, for "tailOnly" mode */ + const int kShortLogMaxLines = 5; + const int kShortLogLineLen = 256; + char shortLog[kShortLogMaxLines][kShortLogLineLen]; + int shortLogCount = 0; + int shortLogNext = 0; + + int logfd = open(filename, O_RDONLY | O_NONBLOCK); + if (logfd < 0) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + } log_entry; + + while (true) { + ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + if (actual < 0) { + if (errno == EINTR) { + /* interrupted by signal, retry */ + continue; + } else if (errno == EAGAIN) { + /* non-blocking EOF; we're done */ + break; + } else { + _LOG(tfd, true, "Error while reading log: %s\n", + strerror(errno)); + break; + } + } else if (actual == 0) { + _LOG(tfd, true, "Got zero bytes while reading log: %s\n", + strerror(errno)); + break; + } + + /* + * NOTE: if you XLOG something here, this will spin forever, + * because you will be writing as fast as you're reading. Any + * high-frequency debug diagnostics should just be written to + * the tombstone file. + */ + + struct logger_entry* entry = &log_entry.entry; + + if (entry->pid != (int32_t) pid) { + /* wrong pid, ignore */ + continue; + } + + if (first) { + _LOG(tfd, true, "--------- %slog %s\n", + tailOnly ? "tail end of " : "", filename); + first = false; + } + + /* + * Msg format is: \0\0 + * + * We want to display it in the same format as "logcat -v threadtime" + * (although in this case the pid is redundant). + * + * TODO: scan for line breaks ('\n') and display each text line + * on a separate line, prefixed with the header, like logcat does. + */ + static const char* kPrioChars = "!.VDIWEFS"; + unsigned char prio = entry->msg[0]; + char* tag = entry->msg + 1; + char* msg = tag + strlen(tag) + 1; + + /* consume any trailing newlines */ + char* eatnl = msg + strlen(msg) - 1; + while (eatnl >= msg && *eatnl == '\n') { + *eatnl-- = '\0'; + } + + char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); + + char timeBuf[32]; + time_t sec = (time_t) entry->sec; + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + if (tailOnly) { + snprintf(shortLog[shortLogNext], kShortLogLineLen, + "%s.%03d %5d %5d %c %-8s: %s", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); + shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; + shortLogCount++; + } else { + _LOG(tfd, true, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); + } + } + + if (tailOnly) { + int i; + + /* + * If we filled the buffer, we want to start at "next", which has + * the oldest entry. If we didn't, we want to start at zero. + */ + if (shortLogCount < kShortLogMaxLines) { + shortLogNext = 0; + } else { + shortLogCount = kShortLogMaxLines; /* cap at window size */ + } + + for (i = 0; i < shortLogCount; i++) { + _LOG(tfd, true, "%s\n", shortLog[shortLogNext]); + shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; + } + } + + close(logfd); +} + +/* + * Dumps the logs generated by the specified pid to the tombstone, from both + * "system" and "main" log devices. Ideally we'd interleave the output. + */ +static void dump_logs(int tfd, unsigned pid, bool tailOnly) +{ + dump_log_file(tfd, pid, "/dev/log/system", tailOnly); + dump_log_file(tfd, pid, "/dev/log/main", tailOnly); +} + /* Return true if some thread is not detached cleanly */ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, int signal) @@ -531,6 +574,11 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, int fd; bool need_cleanup = false; + /* don't copy log messages to tombstone unless this is a dev device */ + char value[PROPERTY_VALUE_MAX]; + property_get("ro.debuggable", value, "0"); + bool wantLogs = (value[0] == '1'); + mkdir(TOMBSTONE_DIR, 0755); chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); @@ -540,6 +588,11 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, dump_crash_banner(fd, pid, tid, signal); dump_crash_report(fd, pid, tid, true); + + if (wantLogs) { + dump_logs(fd, pid, true); + } + /* * If the user has requested to attach gdb, don't collect the per-thread * information as it increases the chance to lose track of the process. @@ -548,6 +601,10 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, need_cleanup = dump_sibling_thread_report(fd, pid, tid); } + if (wantLogs) { + dump_logs(fd, pid, false); + } + close(fd); return need_cleanup; } @@ -606,15 +663,16 @@ static void wait_for_user_action(unsigned tid, struct ucred* cr) (void)tid; /* First log a helpful message */ LOG( "********************************************************\n" - "* process %d crashed. debuggerd waiting for gdbserver \n" - "* \n" - "* adb shell gdbserver :port --attach %d & \n" - "* \n" - "* and press the HOME key. \n" + "* Process %d has been suspended while crashing. To\n" + "* attach gdbserver for a gdb connection on port 5039:\n" + "*\n" + "* adb shell gdbserver :5039 --attach %d &\n" + "*\n" + "* Press HOME key to let the process continue crashing.\n" "********************************************************\n", cr->pid, cr->pid); - /* wait for HOME key */ + /* wait for HOME key (TODO: something useful for devices w/o HOME key) */ if (init_getevent() == 0) { int ms = 1200 / 10; int dit = 1; @@ -696,9 +754,9 @@ static void handle_crashing_process(int fd) goto done; } - sprintf(buf,"/proc/%d/task/%d", cr.pid, tid); + snprintf(buf, sizeof buf, "/proc/%d/task/%d", cr.pid, tid); if(stat(buf, &s)) { - LOG("tid %d does not exist in pid %d. ignorning debug request\n", + LOG("tid %d does not exist in pid %d. ignoring debug request\n", tid, cr.pid); close(fd); return; @@ -706,17 +764,51 @@ static void handle_crashing_process(int fd) XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); + /* Note that at this point, the target thread's signal handler + * is blocked in a read() call. This gives us the time to PTRACE_ATTACH + * to it before it has a chance to really fault. + * + * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it + * won't necessarily have stopped by the time ptrace() returns. (We + * currently assume it does.) We write to the file descriptor to + * ensure that it can run as soon as we call PTRACE_CONT below. + * See details in bionic/libc/linker/debugger.c, in function + * debugger_signal_handler(). + */ tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); + int ptrace_error = errno; + + if (TEMP_FAILURE_RETRY(write(fd, &tid, 1)) != 1) { + XLOG("failed responding to client: %s\n", + strerror(errno)); + goto done; + } + if(tid_attach_status < 0) { - LOG("ptrace attach failed: %s\n", strerror(errno)); + LOG("ptrace attach failed: %s\n", strerror(ptrace_error)); goto done; } close(fd); fd = -1; + const int sleep_time_usec = 200000; /* 0.2 seconds */ + const int max_total_sleep_usec = 3000000; /* 3 seconds */ + int loop_limit = max_total_sleep_usec / sleep_time_usec; for(;;) { - n = waitpid(tid, &status, __WALL); + if (loop_limit-- == 0) { + LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n", + cr.pid, tid, cr.uid); + goto done; + } + n = waitpid(tid, &status, __WALL | WNOHANG); + + if (n == 0) { + /* not ready yet */ + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + continue; + } if(n < 0) { if(errno == EAGAIN) continue; @@ -788,7 +880,12 @@ static void handle_crashing_process(int fd) wait_for_user_action(tid, &cr); } - /* resume stopped process (so it can crash in peace) */ + /* + * Resume stopped process (so it can crash in peace). If we didn't + * successfully detach, we're still the parent, and the actual parent + * won't receive a death notification via wait(2). At this point + * there's not much we can do about that. + */ kill(cr.pid, SIGCONT); if (need_cleanup) { @@ -799,10 +896,24 @@ static void handle_crashing_process(int fd) if(fd != -1) close(fd); } + int main() { int s; struct sigaction act; + int logsocket = -1; + + /* + * debuggerd crashes can't be reported to debuggerd. Reset all of the + * crash handlers. + */ + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGSTKFLT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); @@ -831,8 +942,12 @@ int main() int fd; alen = sizeof(addr); + XLOG("waiting for connection\n"); fd = accept(s, &addr, &alen); - if(fd < 0) continue; + if(fd < 0) { + XLOG("accept failed: %s\n", strerror(errno)); + continue; + } fcntl(fd, F_SETFD, FD_CLOEXEC); diff --git a/debuggerd/debuggerd.h b/debuggerd/debuggerd.h new file mode 100644 index 00000000000..e3cdc7c5cc9 --- /dev/null +++ b/debuggerd/debuggerd.h @@ -0,0 +1,39 @@ +/* system/debuggerd/debuggerd.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include "utility.h" +#include "symbol_table.h" + + +/* Main entry point to get the backtrace from the crashing process */ +extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], + int *frame0_pc_sane, + bool at_fault); + +extern void dump_registers(int tfd, int pid, bool at_fault); + +extern int unwind_backtrace_with_ptrace_x86(int tfd, pid_t pid, mapinfo *map, bool at_fault); + +void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, bool at_fault); + +void dump_stack_and_code(int tfd, int pid, mapinfo *map, + int unwind_depth, unsigned int sp_list[], + bool at_fault); diff --git a/debuggerd/symbol_table.c b/debuggerd/symbol_table.c new file mode 100644 index 00000000000..23572a38bd7 --- /dev/null +++ b/debuggerd/symbol_table.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include + +#include "symbol_table.h" +#include "utility.h" + +#include + +// Compare func for qsort +static int qcompar(const void *a, const void *b) +{ + return ((struct symbol*)a)->addr - ((struct symbol*)b)->addr; +} + +// Compare func for bsearch +static int bcompar(const void *addr, const void *element) +{ + struct symbol *symbol = (struct symbol*)element; + + if((unsigned int)addr < symbol->addr) { + return -1; + } + + if((unsigned int)addr - symbol->addr >= symbol->size) { + return 1; + } + + return 0; +} + +/* + * Create a symbol table from a given file + * + * Parameters: + * filename - Filename to process + * + * Returns: + * A newly-allocated SymbolTable structure, or NULL if error. + * Free symbol table with symbol_table_free() + */ +struct symbol_table *symbol_table_create(const char *filename) +{ + struct symbol_table *table = NULL; + + // Open the file, and map it into memory + struct stat sb; + int length; + char *base; + + XLOG2("Creating symbol table for %s\n", filename); + int fd = open(filename, O_RDONLY); + + if(fd < 0) { + goto out; + } + + fstat(fd, &sb); + length = sb.st_size; + + base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); + + if(!base) { + goto out_close; + } + + // Parse the file header + Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; + Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); + + // Search for the dynamic symbols section + int sym_idx = -1; + int dynsym_idx = -1; + int i; + + for(i = 0; i < hdr->e_shnum; i++) { + if(shdr[i].sh_type == SHT_SYMTAB ) { + sym_idx = i; + } + if(shdr[i].sh_type == SHT_DYNSYM ) { + dynsym_idx = i; + } + } + if ((dynsym_idx == -1) && (sym_idx == -1)) { + goto out_unmap; + } + + table = malloc(sizeof(struct symbol_table)); + if(!table) { + goto out_unmap; + } + table->name = strdup(filename); + table->num_symbols = 0; + + Elf32_Sym *dynsyms = NULL; + Elf32_Sym *syms = NULL; + int dynnumsyms = 0; + int numsyms = 0; + char *dynstr = NULL; + char *str = NULL; + + if (dynsym_idx != -1) { + dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset); + dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize; + int dynstr_idx = shdr[dynsym_idx].sh_link; + dynstr = base + shdr[dynstr_idx].sh_offset; + } + + if (sym_idx != -1) { + syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset); + numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize; + int str_idx = shdr[sym_idx].sh_link; + str = base + shdr[str_idx].sh_offset; + } + + int symbol_count = 0; + int dynsymbol_count = 0; + + if (dynsym_idx != -1) { + // Iterate through the dynamic symbol table, and count how many symbols + // are actually defined + for(i = 0; i < dynnumsyms; i++) { + if(dynsyms[i].st_shndx != SHN_UNDEF) { + dynsymbol_count++; + } + } + XLOG2("Dynamic Symbol count: %d\n", dynsymbol_count); + } + + if (sym_idx != -1) { + // Iterate through the symbol table, and count how many symbols + // are actually defined + for(i = 0; i < numsyms; i++) { + if((syms[i].st_shndx != SHN_UNDEF) && + (strlen(str+syms[i].st_name)) && + (syms[i].st_value != 0) && (syms[i].st_size != 0)) { + symbol_count++; + } + } + XLOG2("Symbol count: %d\n", symbol_count); + } + + // Now, create an entry in our symbol table structure for each symbol... + table->num_symbols += symbol_count + dynsymbol_count; + table->symbols = malloc(table->num_symbols * sizeof(struct symbol)); + if(!table->symbols) { + free(table); + table = NULL; + goto out_unmap; + } + + + int j = 0; + if (dynsym_idx != -1) { + // ...and populate them + for(i = 0; i < dynnumsyms; i++) { + if(dynsyms[i].st_shndx != SHN_UNDEF) { + table->symbols[j].name = strdup(dynstr + dynsyms[i].st_name); + table->symbols[j].addr = dynsyms[i].st_value; + table->symbols[j].size = dynsyms[i].st_size; + XLOG2("name: %s, addr: %x, size: %x\n", + table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size); + j++; + } + } + } + + if (sym_idx != -1) { + // ...and populate them + for(i = 0; i < numsyms; i++) { + if((syms[i].st_shndx != SHN_UNDEF) && + (strlen(str+syms[i].st_name)) && + (syms[i].st_value != 0) && (syms[i].st_size != 0)) { + table->symbols[j].name = strdup(str + syms[i].st_name); + table->symbols[j].addr = syms[i].st_value; + table->symbols[j].size = syms[i].st_size; + XLOG2("name: %s, addr: %x, size: %x\n", + table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size); + j++; + } + } + } + + // Sort the symbol table entries, so they can be bsearched later + qsort(table->symbols, table->num_symbols, sizeof(struct symbol), qcompar); + +out_unmap: + munmap(base, length); + +out_close: + close(fd); + +out: + return table; +} + +/* + * Free a symbol table + * + * Parameters: + * table - Table to free + */ +void symbol_table_free(struct symbol_table *table) +{ + int i; + + if(!table) { + return; + } + + for(i=0; inum_symbols; i++) { + free(table->symbols[i].name); + } + + free(table->symbols); + free(table); +} + +/* + * Search for an address in the symbol table + * + * Parameters: + * table - Table to search in + * addr - Address to search for. + * + * Returns: + * A pointer to the Symbol structure corresponding to the + * symbol which contains this address, or NULL if no symbol + * contains it. + */ +const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr) +{ + if(!table) { + return NULL; + } + + return bsearch((void*)addr, table->symbols, table->num_symbols, sizeof(struct symbol), bcompar); +} diff --git a/debuggerd/symbol_table.h b/debuggerd/symbol_table.h new file mode 100644 index 00000000000..7f41f915175 --- /dev/null +++ b/debuggerd/symbol_table.h @@ -0,0 +1,20 @@ +#ifndef SYMBOL_TABLE_H +#define SYMBOL_TABLE_H + +struct symbol { + unsigned int addr; + unsigned int size; + char *name; +}; + +struct symbol_table { + struct symbol *symbols; + int num_symbols; + char *name; +}; + +struct symbol_table *symbol_table_create(const char *filename); +void symbol_table_free(struct symbol_table *table); +const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr); + +#endif diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 8f3931c1fdc..409209c21da 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -38,14 +39,14 @@ void get_remote_struct(int pid, void *src, void *dst, size_t size) unsigned int i; for (i = 0; i+4 <= size; i+=4) { - *(int *)(dst+i) = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL); + *(int *)((char *)dst+i) = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL); } if (i < size) { int val; assert((size - i) < 4); - val = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL); + val = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL); while (i < size) { ((unsigned char *)dst)[i] = val & 0xff; i++; @@ -69,11 +70,12 @@ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def) /* Find the containing map info for the pc */ const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) { + *rel_pc = pc; while(mi) { if((pc >= mi->start) && (pc < mi->end)){ // Only calculate the relative offset for shared libraries if (strstr(mi->name, ".so")) { - *rel_pc = pc - mi->start; + *rel_pc -= mi->start; } return mi; } @@ -81,3 +83,20 @@ const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) } return NULL; } + +/* + * Returns true if the specified signal has an associated address (i.e. it + * sets siginfo_t.si_addr). + */ +bool signal_has_address(int sig) +{ + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } +} diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 49f59518ec7..4a935d2ae4d 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -21,6 +21,8 @@ #include #include +#include "symbol_table.h" + #ifndef PT_ARM_EXIDX #define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ #endif @@ -33,6 +35,8 @@ typedef struct mapinfo { unsigned end; unsigned exidx_start; unsigned exidx_end; + struct symbol_table *symbols; + bool isExecutable; char name[]; } mapinfo; @@ -53,4 +57,23 @@ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); /* Log information onto the tombstone */ extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); +/* Determine whether si_addr is valid for this signal */ +bool signal_has_address(int sig); + +#define LOG(fmt...) _LOG(-1, 0, fmt) + +/* Set to 1 for normal debug traces */ +#if 0 +#define XLOG(fmt...) _LOG(-1, 0, fmt) +#else +#define XLOG(fmt...) do {} while(0) +#endif + +/* Set to 1 for chatty debug traces. Includes all resolved dynamic symbols */ +#if 0 +#define XLOG2(fmt...) _LOG(-1, 0, fmt) +#else +#define XLOG2(fmt...) do {} while(0) +#endif + #endif diff --git a/debuggerd/vfp-crasher.c b/debuggerd/vfp-crasher.c new file mode 100644 index 00000000000..7a19cdd8e06 --- /dev/null +++ b/debuggerd/vfp-crasher.c @@ -0,0 +1,7 @@ +int main() +{ + extern void crash(void); + + crash(); + return 0; +} diff --git a/debuggerd/vfp.S b/debuggerd/vfp.S new file mode 100644 index 00000000000..5ae6711627b --- /dev/null +++ b/debuggerd/vfp.S @@ -0,0 +1,48 @@ + .text + .align 2 + .global crash + .type crash, %function +crash: +#if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_6__) + //VFPv3 was the first to have the fconstd instruction. +#else //// defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_6__) + + fconstd d0, #0 + fconstd d1, #1 + fconstd d2, #2 + fconstd d3, #3 + fconstd d4, #4 + fconstd d5, #5 + fconstd d6, #6 + fconstd d7, #7 + fconstd d8, #8 + fconstd d9, #9 + fconstd d10, #10 + fconstd d11, #11 + fconstd d12, #12 + fconstd d13, #13 + fconstd d14, #14 + fconstd d15, #15 +#ifdef WITH_VFP_D32 + fconstd d16, #16 + fconstd d17, #17 + fconstd d18, #18 + fconstd d19, #19 + fconstd d20, #20 + fconstd d21, #21 + fconstd d22, #22 + fconstd d23, #23 + fconstd d24, #24 + fconstd d25, #25 + fconstd d26, #26 + fconstd d27, #27 + fconstd d28, #28 + fconstd d29, #29 + fconstd d30, #30 + fconstd d31, #31 +#endif +#endif // defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_6__) + mov r0, #0 + str r0, [r0] + bx lr + diff --git a/debuggerd/x86/crashglue.S b/debuggerd/x86/crashglue.S new file mode 100644 index 00000000000..59df432507d --- /dev/null +++ b/debuggerd/x86/crashglue.S @@ -0,0 +1,15 @@ +.globl crash1 +.globl crashnostack + +crash1: + movl $0xa5a50000, %eax + movl $0xa5a50001, %ebx + movl $0xa5a50002, %ecx + + movl $0, %edx + jmp *%edx + + +crashnostack: + movl $0, %ebp + jmp *%ebp diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c new file mode 100644 index 00000000000..9d418cf61b3 --- /dev/null +++ b/debuggerd/x86/machine.c @@ -0,0 +1,61 @@ +/* system/debuggerd/debuggerd.c +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "../utility.h" +#include "x86_utility.h" + +void dump_registers(int tfd, int pid, bool at_fault) +{ + struct pt_regs_x86 r; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } +//if there is no stack, no print just like arm + if(!r.ebp) + return; + _LOG(tfd, only_in_tombstone, " eax %08x ebx %08x ecx %08x edx %08x\n", + r.eax, r.ebx, r.ecx, r.edx); + _LOG(tfd, only_in_tombstone, " esi %08x edi %08x\n", + r.esi, r.edi); + _LOG(tfd, only_in_tombstone, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", + r.xcs, r.xds, r.xes, r.xfs, r.xss); + _LOG(tfd, only_in_tombstone, + " eip %08x ebp %08x esp %08x flags %08x\n", + r.eip, r.ebp, r.esp, r.eflags); +} diff --git a/debuggerd/x86/unwind.c b/debuggerd/x86/unwind.c new file mode 100644 index 00000000000..0a7f04c88e1 --- /dev/null +++ b/debuggerd/x86/unwind.c @@ -0,0 +1,86 @@ +#include +#include +#include "../utility.h" +#include "x86_utility.h" + + +int unwind_backtrace_with_ptrace_x86(int tfd, pid_t pid, mapinfo *map, + bool at_fault) +{ + struct pt_regs_x86 r; + unsigned int stack_level = 0; + unsigned int stack_depth = 0; + unsigned int rel_pc; + unsigned int stack_ptr; + unsigned int stack_content; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0; + unsigned int eip = (unsigned int)r.eip; + unsigned int ebp = (unsigned int)r.ebp; + unsigned int cur_sp = (unsigned int)r.esp; + const mapinfo *mi; + const struct symbol* sym = 0; + + +//ebp==0, it indicates that the stack is poped to the bottom or there is no stack at all. + while (ebp) { + mi = pc_to_mapinfo(map, eip, &rel_pc); + + /* See if we can determine what symbol this stack frame resides in */ + if (mi != 0 && mi->symbols != 0) { + sym = symbol_table_lookup(mi->symbols, rel_pc); + } + if (sym) { + _LOG(tfd, !at_fault, " #%02d eip: %08x %s (%s)\n", + stack_level, eip, mi ? mi->name : "", sym->name); + } else { + _LOG(tfd, !at_fault, " #%02d eip: %08x %s\n", + stack_level, eip, mi ? mi->name : ""); + } + + stack_level++; + if (stack_level >= STACK_DEPTH || eip == 0) + break; + eip = ptrace(PTRACE_PEEKTEXT, pid, (void*)(ebp + 4), NULL); + ebp = ptrace(PTRACE_PEEKTEXT, pid, (void*)ebp, NULL); + } + ebp = (unsigned int)r.ebp; + stack_depth = stack_level; + stack_level = 0; + if (ebp) + _LOG(tfd, !at_fault, "stack: \n"); + while (ebp) { + stack_ptr = cur_sp; + while((int)(ebp - stack_ptr) >= 0) { + stack_content = ptrace(PTRACE_PEEKTEXT, pid, (void*)stack_ptr, NULL); + mi = pc_to_mapinfo(map, stack_content, &rel_pc); + + /* See if we can determine what symbol this stack frame resides in */ + if (mi != 0 && mi->symbols != 0) { + sym = symbol_table_lookup(mi->symbols, rel_pc); + } + if (sym) { + _LOG(tfd, !at_fault, " #%02d %08x %08x %s (%s)\n", + stack_level, stack_ptr, stack_content, mi ? mi->name : "", sym->name); + } else { + _LOG(tfd, !at_fault, " #%02d %08x %08x %s\n", + stack_level, stack_ptr, stack_content, mi ? mi->name : ""); + } + + stack_ptr = stack_ptr + 4; + //the stack frame may be very deep. + if((int)(stack_ptr - cur_sp) >= STACK_FRAME_DEPTH) { + _LOG(tfd, !at_fault, " ...... ...... \n"); + break; + } + } + cur_sp = ebp + 4; + stack_level++; + if (stack_level >= STACK_DEPTH || stack_level >= stack_depth) + break; + ebp = ptrace(PTRACE_PEEKTEXT, pid, (void*)ebp, NULL); + } + + return stack_depth; +} + diff --git a/debuggerd/x86/x86_utility.h b/debuggerd/x86/x86_utility.h new file mode 100644 index 00000000000..ac6a885c0fb --- /dev/null +++ b/debuggerd/x86/x86_utility.h @@ -0,0 +1,40 @@ +/* +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define STACK_DEPTH 8 +#define STACK_FRAME_DEPTH 64 + +typedef struct pt_regs_x86 { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + int xds; + int xes; + int xfs; + int xgs; + long orig_eax; + long eip; + int xcs; + long eflags; + long esp; + int xss; +}pt_regs_x86; + diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 7a9d35f2856..3c5538f4a18 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -33,17 +33,24 @@ endif ifeq ($(HOST_OS),windows) LOCAL_SRC_FILES += usb_windows.c util_windows.c EXTRA_STATIC_LIBS := AdbWinApi - LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api - ifeq ($(strip $(USE_CYGWIN)),) + ifneq ($(strip $(USE_CYGWIN)),) + # Pure cygwin case + LOCAL_LDLIBS += -lpthread + LOCAL_C_INCLUDES += /usr/include/w32api/ddk + endif + ifneq ($(strip $(USE_MINGW)),) + # MinGW under Linux case LOCAL_LDLIBS += -lws2_32 USE_SYSDEPS_WIN32 := 1 + LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk endif + LOCAL_C_INCLUDES += development/host/windows/usb/api endif LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) diff --git a/fastboot/engine.c b/fastboot/engine.c index 4c7e197fd82..6d9403551d9 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -30,9 +30,17 @@ #include #include #include +#include #include "fastboot.h" +double now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; +} + char *mkmsg(const char *fmt, ...) { char buf[256]; @@ -61,11 +69,14 @@ struct Action Action *next; char cmd[64]; + const char *prod; void *data; unsigned size; const char *msg; int (*func)(Action *a, int status, char *resp); + + double start; }; static Action *action_list = 0; @@ -76,7 +87,9 @@ static int cb_default(Action *a, int status, char *resp) if (status) { fprintf(stderr,"FAILED (%s)\n", resp); } else { - fprintf(stderr,"OKAY\n"); + double split = now(); + fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start)); + a->start = split; } return status; } @@ -85,14 +98,20 @@ static Action *queue_action(unsigned op, const char *fmt, ...) { Action *a; va_list ap; + size_t cmdsize; a = calloc(1, sizeof(Action)); if (a == 0) die("out of memory"); va_start(ap, fmt); - vsprintf(a->cmd, fmt, ap); + cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap); va_end(ap); + if (cmdsize >= sizeof(a->cmd)) { + free(a); + die("Command length (%d) exceeds maximum size (%d)", cmdsize, sizeof(a->cmd)); + } + if (action_last) { action_last->next = a; } else { @@ -101,6 +120,9 @@ static Action *queue_action(unsigned op, const char *fmt, ...) action_last = a; a->op = op; a->func = cb_default; + + a->start = -1; + return a; } @@ -162,11 +184,23 @@ static int cb_check(Action *a, int status, char *resp, int invert) return status; } + if (a->prod) { + if (strcmp(a->prod, cur_product) != 0) { + double split = now(); + fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n", + cur_product, a->prod, (split - a->start)); + a->start = split; + return 0; + } + } + yes = match(resp, value, count); if (invert) yes = !yes; if (yes) { - fprintf(stderr,"OKAY\n"); + double split = now(); + fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start)); + a->start = split; return 0; } @@ -191,10 +225,12 @@ static int cb_reject(Action *a, int status, char *resp) return cb_check(a, status, resp, 1); } -void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value) +void fb_queue_require(const char *prod, const char *var, + int invert, unsigned nvalues, const char **value) { Action *a; a = queue_action(OP_QUERY, "getvar:%s", var); + a->prod = prod; a->data = value; a->size = nvalues; a->msg = mkmsg("checking %s", var); @@ -221,6 +257,25 @@ void fb_queue_display(const char *var, const char *prettyname) a->func = cb_display; } +static int cb_save(Action *a, int status, char *resp) +{ + if (status) { + fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp); + return status; + } + strncpy(a->data, resp, a->size); + return 0; +} + +void fb_queue_query_save(const char *var, char *dest, unsigned dest_size) +{ + Action *a; + a = queue_action(OP_QUERY, "getvar:%s", var); + a->data = (void *)dest; + a->size = dest_size; + a->func = cb_save; +} + static int cb_do_nothing(Action *a, int status, char *resp) { fprintf(stderr,"\n"); @@ -254,18 +309,22 @@ void fb_queue_notice(const char *notice) a->data = (void*) notice; } -void fb_execute_queue(usb_handle *usb) +int fb_execute_queue(usb_handle *usb) { Action *a; char resp[FB_RESPONSE_SZ+1]; - int status; + int status = 0; a = action_list; resp[FB_RESPONSE_SZ] = 0; + double start = -1; for (a = action_list; a; a = a->next) { + a->start = now(); + if (start < 0) start = a->start; if (a->msg) { - fprintf(stderr,"%s... ",a->msg); + // fprintf(stderr,"%30s... ",a->msg); + fprintf(stderr,"%s...\n",a->msg); } if (a->op == OP_DOWNLOAD) { status = fb_download_data(usb, a->data, a->size); @@ -285,5 +344,7 @@ void fb_execute_queue(usb_handle *usb) die("bogus action"); } } -} + fprintf(stderr,"finished. total time: %.3fs\n", (now() - start)); + return status; +} diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 9fab38c47a8..a069d339d12 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -9,7 +9,7 @@ * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF @@ -42,6 +42,8 @@ #include "fastboot.h" +char cur_product[FB_RESPONSE_SZ + 1]; + void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline); boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, @@ -68,7 +70,7 @@ void die(const char *fmt, ...) fprintf(stderr,"\n"); va_end(ap); exit(1); -} +} void get_my_path(char *path); @@ -99,13 +101,13 @@ char *find_item(const char *item, const char *product) "../../../target/product/%s/%s", product, fn); return strdup(path); } - + dir = getenv("ANDROID_PRODUCT_OUT"); if((dir == 0) || (dir[0] == 0)) { die("neither -p product specified nor ANDROID_PRODUCT_OUT set"); return 0; } - + sprintf(path, "%s/%s", dir, fn); return strdup(path); } @@ -149,8 +151,13 @@ int match_fastboot(usb_ifc_info *info) if(!(vendor_id && (info->dev_vendor == vendor_id)) && (info->dev_vendor != 0x18d1) && // Google (info->dev_vendor != 0x0451) && + (info->dev_vendor != 0x0502) && + (info->dev_vendor != 0x0fce) && // Sony Ericsson + (info->dev_vendor != 0x05c6) && // Qualcomm (info->dev_vendor != 0x22b8) && // Motorola + (info->dev_vendor != 0x0955) && // Nvidia (info->dev_vendor != 0x413c) && // DELL + (info->dev_vendor != 0x2314) && // INQ Mobile (info->dev_vendor != 0x0bb4)) // HTC return -1; if(info->ifc_class != 0xff) return -1; @@ -166,6 +173,9 @@ int list_devices_callback(usb_ifc_info *info) { if (match_fastboot(info) == 0) { char* serial = info->serial_number; + if (!info->writable) { + serial = "no permissions"; // like "adb devices" + } if (!serial[0]) { serial = "????????????"; } @@ -182,12 +192,12 @@ usb_handle *open_device(void) int announce = 1; if(usb) return usb; - + for(;;) { usb = usb_open(match_fastboot); if(usb) return usb; if(announce) { - announce = 0; + announce = 0; fprintf(stderr,"< waiting for device >\n"); } sleep(1); @@ -216,8 +226,10 @@ void usage(void) " boot [ ] download and boot kernel\n" " flash:raw boot [ ] create bootimage and flash it\n" " devices list all connected devices\n" + " continue continue with autoboot\n" " reboot reboot device normally\n" " reboot-bootloader reboot device into bootloader\n" + " help show this help message\n" "\n" "options:\n" " -w erase userdata and cache\n" @@ -226,11 +238,11 @@ void usage(void) " -c override kernel commandline\n" " -i specify a custom USB vendor id\n" " -b specify a custom kernel base address\n" + " -n specify the nand page size. default: 2048\n" ); - exit(1); } -void *load_bootable_image(const char *kernel, const char *ramdisk, +void *load_bootable_image(unsigned page_size, const char *kernel, const char *ramdisk, unsigned *sz, const char *cmdline) { void *kdata = 0, *rdata = 0; @@ -248,16 +260,16 @@ void *load_bootable_image(const char *kernel, const char *ramdisk, fprintf(stderr, "cannot load '%s'\n", kernel); return 0; } - + /* is this actually a boot image? */ if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline); - + if(ramdisk) { fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n"); return 0; } - + *sz = ksize; return kdata; } @@ -271,7 +283,7 @@ void *load_bootable_image(const char *kernel, const char *ramdisk, } fprintf(stderr,"creating boot image...\n"); - bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, base_addr, &bsize); + bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, page_size, base_addr, &bsize); if(bdata == 0) { fprintf(stderr,"failed to create boot.img\n"); return 0; @@ -279,7 +291,7 @@ void *load_bootable_image(const char *kernel, const char *ramdisk, if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline); fprintf(stderr,"creating boot image - %d bytes\n", bsize); *sz = bsize; - + return bdata; } @@ -288,7 +300,7 @@ void *unzip_file(zipfile_t zip, const char *name, unsigned *sz) void *data; zipentry_t entry; unsigned datasz; - + entry = lookup_zipentry(zip, name); if (entry == NULL) { fprintf(stderr, "archive does not contain '%s'\n", name); @@ -331,16 +343,25 @@ static int setup_requirement_line(char *name) { char *val[MAX_OPTIONS]; const char **out; + char *prod = NULL; unsigned n, count; char *x; int invert = 0; - + if (!strncmp(name, "reject ", 7)) { name += 7; invert = 1; } else if (!strncmp(name, "require ", 8)) { name += 8; invert = 0; + } else if (!strncmp(name, "require-for-product:", 20)) { + // Get the product and point name past it + prod = name + 20; + name = strchr(name, ' '); + if (!name) return -1; + *name = 0; + name += 1; + invert = 0; } x = strchr(name, '='); @@ -354,10 +375,10 @@ static int setup_requirement_line(char *name) *x = 0; val[count] = x + 1; } - + name = strip(name); for(n = 0; n < count; n++) val[n] = strip(val[n]); - + name = strip(name); if (name == 0) return -1; @@ -372,7 +393,7 @@ static int setup_requirement_line(char *name) if (out[n] == 0) return -1; } - fb_queue_require(name, invert, n, out); + fb_queue_require(prod, name, invert, n, out); return 0; } @@ -423,6 +444,8 @@ void do_update(char *fn) queue_info_dump(); + fb_queue_query_save("product", cur_product, sizeof(cur_product)); + zdata = load_file(fn, &zsize); if (zdata == 0) die("failed to load '%s'", fn); @@ -468,11 +491,11 @@ void do_send_signature(char *fn) void *data; unsigned sz; char *xtn; - + xtn = strrchr(fn, '.'); if (!xtn) return; if (strcmp(xtn, ".img")) return; - + strcpy(xtn,".sig"); data = load_file(fn, &sz); strcpy(xtn,".img"); @@ -489,6 +512,8 @@ void do_flashall(void) queue_info_dump(); + fb_queue_query_save("product", cur_product, sizeof(cur_product)); + fname = find_item("info", product); if (fname == 0) die("cannot find android-info.txt"); data = load_file(fname, &sz); @@ -512,18 +537,18 @@ void do_flashall(void) data = load_file(fname, &sz); if (data == 0) die("could not load system.img"); do_send_signature(fname); - fb_queue_flash("system", data, sz); + fb_queue_flash("system", data, sz); } #define skip(n) do { argc -= (n); argv += (n); } while (0) -#define require(n) do { if (argc < (n)) usage(); } while (0) +#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0) int do_oem_command(int argc, char **argv) { int i; char command[256]; if (argc <= 1) return 0; - + command[0] = 0; while(1) { strcat(command,*argv); @@ -532,7 +557,7 @@ int do_oem_command(int argc, char **argv) strcat(command," "); } - fb_queue_command(command,""); + fb_queue_command(command,""); return 0; } @@ -543,11 +568,13 @@ int main(int argc, char **argv) int wants_reboot_bootloader = 0; void *data; unsigned sz; + unsigned page_size = 2048; + int status; skip(1); if (argc == 0) { usage(); - return 0; + return 1; } if (!strcmp(*argv, "devices")) { @@ -555,6 +582,14 @@ int main(int argc, char **argv) return 0; } + if (!strcmp(*argv, "help")) { + usage(); + return 0; + } + + + serial = getenv("ANDROID_SERIAL"); + while (argc > 0) { if(!strcmp(*argv, "-w")) { wants_wipe = 1; @@ -563,6 +598,11 @@ int main(int argc, char **argv) require(2); base_addr = strtoul(argv[1], 0, 16); skip(2); + } else if(!strcmp(*argv, "-n")) { + require(2); + page_size = (unsigned)strtoul(argv[1], NULL, 0); + if (!page_size) die("invalid page size"); + skip(2); } else if(!strcmp(*argv, "-s")) { require(2); serial = argv[1]; @@ -622,7 +662,7 @@ int main(int argc, char **argv) rname = argv[0]; skip(1); } - data = load_bootable_image(kname, rname, &sz, cmdline); + data = load_bootable_image(page_size, kname, rname, &sz, cmdline); if (data == 0) return 1; fb_queue_download("boot.img", data, sz); fb_queue_command("boot", "booting"); @@ -652,7 +692,7 @@ int main(int argc, char **argv) } else { skip(3); } - data = load_bootable_image(kname, rname, &sz, cmdline); + data = load_bootable_image(page_size, kname, rname, &sz, cmdline); if (data == 0) die("cannot load bootable image"); fb_queue_flash(pname, data, sz); } else if(!strcmp(*argv, "flashall")) { @@ -672,6 +712,7 @@ int main(int argc, char **argv) argc = do_oem_command(argc, argv); } else { usage(); + return 1; } } @@ -687,6 +728,6 @@ int main(int argc, char **argv) usb = open_device(); - fb_execute_queue(usb); - return 0; + status = fb_execute_queue(usb); + return (status) ? 1 : 0; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index a36c569e159..9e043fe6ffc 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -43,15 +43,20 @@ char *fb_get_error(void); /* engine.c - high level command queue engine */ void fb_queue_flash(const char *ptn, void *data, unsigned sz);; void fb_queue_erase(const char *ptn); -void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value); +void fb_queue_require(const char *prod, const char *var, int invert, + unsigned nvalues, const char **value); void fb_queue_display(const char *var, const char *prettyname); +void fb_queue_query_save(const char *var, char *dest, unsigned dest_size); void fb_queue_reboot(void); void fb_queue_command(const char *cmd, const char *msg); void fb_queue_download(const char *name, void *data, unsigned size); void fb_queue_notice(const char *notice); -void fb_execute_queue(usb_handle *usb); +int fb_execute_queue(usb_handle *usb); /* util stuff */ void die(const char *fmt, ...); +/* Current product */ +extern char cur_product[FB_RESPONSE_SZ + 1]; + #endif diff --git a/fastboot/protocol.c b/fastboot/protocol.c index c788a12ff0e..3948363d98d 100644 --- a/fastboot/protocol.c +++ b/fastboot/protocol.c @@ -62,7 +62,7 @@ static int check_response(usb_handle *usb, unsigned size, } if(!memcmp(status, "INFO", 4)) { - fprintf(stderr,"%s\n", status); + fprintf(stderr,"(bootloader) %s\n", status + 4); continue; } diff --git a/fastboot/usb.h b/fastboot/usb.h index f3ec5bfdad4..cc157d56ee9 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -50,6 +50,8 @@ struct usb_ifc_info unsigned char has_bulk_in; unsigned char has_bulk_out; + unsigned char writable; + char serial_number[256]; }; diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index 3b40ba7f9ef..1ba87e62a23 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -61,6 +61,11 @@ #define DBG1(x...) #endif +/* The max bulk size for linux is 16384 which is defined + * in drivers/usb/core/devio.c. + */ +#define MAX_USBFS_BULK_SIZE (16 * 1024) + struct usb_handle { char fname[64]; @@ -89,7 +94,8 @@ static int check(void *_desc, int len, unsigned type, int size) return 0; } -static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback, +static int filter_usb_device(int fd, char *ptr, int len, int writable, + ifc_match_func callback, int *ept_in_id, int *ept_out_id, int *ifc_id) { struct usb_device_descriptor *dev; @@ -119,7 +125,8 @@ static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback info.dev_class = dev->bDeviceClass; info.dev_subclass = dev->bDeviceSubClass; info.dev_protocol = dev->bDeviceProtocol; - + info.writable = writable; + // read device serial number (if there is one) info.serial_number[0] = 0; if (dev->iSerialNumber) { @@ -135,6 +142,7 @@ static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback ctrl.wIndex = 0; ctrl.wLength = sizeof(buffer); ctrl.data = buffer; + ctrl.timeout = 50; result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); if (result > 0) { @@ -201,6 +209,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) DIR *busdir, *devdir; struct dirent *de; int fd; + int writable; busdir = opendir(base); if(busdir == 0) return 0; @@ -219,13 +228,20 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) sprintf(devname, "%s/%s", busname, de->d_name); // DBG("[ scanning %s ]\n", devname); + writable = 1; if((fd = open(devname, O_RDWR)) < 0) { - continue; + // Check if we have read-only access, so we can give a helpful + // diagnostic like "adb devices" does. + writable = 0; + if((fd = open(devname, O_RDONLY)) < 0) { + continue; + } } n = read(fd, desc, sizeof(desc)); - if(filter_usb_device(fd, desc, n, callback, &in, &out, &ifc) == 0){ + if(filter_usb_device(fd, desc, n, writable, callback, + &in, &out, &ifc) == 0) { usb = calloc(1, sizeof(usb_handle)); strcpy(usb->fname, devname); usb->ep_in = in; @@ -278,7 +294,7 @@ int usb_write(usb_handle *h, const void *_data, int len) while(len > 0) { int xfer; - xfer = (len > 4096) ? 4096 : len; + xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; bulk.ep = h->ep_out; bulk.len = xfer; @@ -312,7 +328,7 @@ int usb_read(usb_handle *h, void *_data, int len) } while(len > 0) { - int xfer = (len > 4096) ? 4096 : len; + int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; bulk.ep = h->ep_in; bulk.len = xfer; @@ -375,5 +391,3 @@ usb_handle *usb_open(ifc_match_func callback) { return find_usb_device("/dev/bus/usb", callback); } - - diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index d6a826094b8..570a456ac1b 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -64,7 +64,7 @@ struct usb_handle /** Try out all the interfaces and see if there's a match. Returns 0 on * success, -1 on failure. */ -static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) { +static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { IOReturn kr; IOUSBFindInterfaceRequest request; io_iterator_t iterator; @@ -231,8 +231,7 @@ static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) { kr = (*interface)->ClearPipeStallBothEnds(interface, handle->bulkIn); if (kr != 0) { - ERR("could not clear input pipe; result %d", kr); - return -1; + ERR("could not clear input pipe; result %x, ignoring...\n", kr); } } @@ -240,8 +239,7 @@ static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) { kr = (*interface)->ClearPipeStallBothEnds(interface, handle->bulkOut); if (kr != 0) { - ERR("could not clear output pipe; result %d", kr); - return -1; + ERR("could not clear output pipe; result %x, ignoring....\n", kr); } } @@ -351,6 +349,7 @@ static int try_device(io_service_t device, usb_handle *handle) { // device has no serial number handle->info.serial_number[0] = 0; } + handle->info.writable = 1; if (try_interfaces(dev, handle)) { goto error; @@ -416,8 +415,6 @@ static int init_usb(ifc_match_func callback, usb_handle **handle) { break; } - usb_ifc_info info; - if (try_device(device, &h) != 0) { IOObjectRelease(device); ret = -1; @@ -516,8 +513,29 @@ int usb_write(usb_handle *h, const void *data, int len) { return -1; } +#if 0 result = (*h->interface)->WritePipe( h->interface, h->bulkOut, (void *)data, len); +#else + /* Attempt to work around crashes in the USB driver that may be caused + * by trying to write too much data at once. The kernel IOCopyMapper + * panics if a single iovmAlloc needs more than half of its mapper pages. + */ + const int maxLenToSend = 1048576; // 1 MiB + int lenRemaining = len; + result = 0; + while (lenRemaining > 0) { + int lenToSend = lenRemaining > maxLenToSend + ? maxLenToSend : lenRemaining; + + result = (*h->interface)->WritePipe( + h->interface, h->bulkOut, (void *)data, lenToSend); + if (result != 0) break; + + lenRemaining -= lenToSend; + data = (const char*)data + lenToSend; + } +#endif #if 0 if ((result == 0) && (h->zero_mask)) { diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 9c0a9cbf134..1050293d72f 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -42,6 +42,7 @@ #define DBG(x...) #endif +#define MAX_USBFS_BULK_SIZE (1024 * 1024) /** Structure usb_handle describes our connection to the usb device via AdbWinApi.dll. This structure is returned from usb_open() routine and @@ -160,7 +161,7 @@ int usb_write(usb_handle* handle, const void* data, int len) { if (NULL != handle) { // Perform write while(len > 0) { - int xfer = (len > 4096) ? 4096 : len; + int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; ret = AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, (unsigned long)xfer, @@ -200,7 +201,7 @@ int usb_read(usb_handle *handle, void* data, int len) { DBG("usb_read %d\n", len); if (NULL != handle) { while (1) { - int xfer = (len > 4096) ? 4096 : len; + int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; ret = AdbReadEndpointSync(handle->adb_read_pipe, (void*)data, @@ -301,6 +302,7 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) { info.ifc_class = interf_desc.bInterfaceClass; info.ifc_subclass = interf_desc.bInterfaceSubClass; info.ifc_protocol = interf_desc.bInterfaceProtocol; + info.writable = 1; // read serial number (if there is one) unsigned long serial_number_len = sizeof(info.serial_number); diff --git a/gpttool/Android.mk b/gpttool/Android.mk new file mode 100644 index 00000000000..a9fffe90166 --- /dev/null +++ b/gpttool/Android.mk @@ -0,0 +1,14 @@ +ifeq ($(HOST_OS),linux) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := gpttool.c +LOCAL_STATIC_LIBRARIES := libz + +LOCAL_MODULE := gpttool +LOCAL_MODULE_TAGS := eng + +include $(BUILD_HOST_EXECUTABLE) + +endif diff --git a/gpttool/gpttool.c b/gpttool/gpttool.c new file mode 100644 index 00000000000..05d51779cf7 --- /dev/null +++ b/gpttool/gpttool.c @@ -0,0 +1,376 @@ +/* system/core/gpttool/gpttool.c +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +const u8 partition_type_uuid[16] = { + 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, + 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, +}; + + +#define EFI_VERSION 0x00010000 +#define EFI_MAGIC "EFI PART" +#define EFI_ENTRIES 128 +#define EFI_NAMELEN 36 + +struct efi_header { + u8 magic[8]; + + u32 version; + u32 header_sz; + + u32 crc32; + u32 reserved; + + u64 header_lba; + u64 backup_lba; + u64 first_lba; + u64 last_lba; + + u8 volume_uuid[16]; + + u64 entries_lba; + + u32 entries_count; + u32 entries_size; + u32 entries_crc32; +} __attribute__((packed)); + +struct efi_entry { + u8 type_uuid[16]; + u8 uniq_uuid[16]; + u64 first_lba; + u64 last_lba; + u64 attr; + u16 name[EFI_NAMELEN]; +}; + +struct ptable { + u8 mbr[512]; + union { + struct efi_header header; + u8 block[512]; + }; + struct efi_entry entry[EFI_ENTRIES]; +}; + +void get_uuid(u8 *uuid) +{ + int fd; + fd = open("/dev/urandom", O_RDONLY); + read(fd, uuid, 16); + close(fd); +} + +void init_mbr(u8 *mbr, u32 blocks) +{ + mbr[0x1be] = 0x00; // nonbootable + mbr[0x1bf] = 0xFF; // bogus CHS + mbr[0x1c0] = 0xFF; + mbr[0x1c1] = 0xFF; + + mbr[0x1c2] = 0xEE; // GPT partition + mbr[0x1c3] = 0xFF; // bogus CHS + mbr[0x1c4] = 0xFF; + mbr[0x1c5] = 0xFF; + + mbr[0x1c6] = 0x01; // start + mbr[0x1c7] = 0x00; + mbr[0x1c8] = 0x00; + mbr[0x1c9] = 0x00; + + memcpy(mbr + 0x1ca, &blocks, sizeof(u32)); + + mbr[0x1fe] = 0x55; + mbr[0x1ff] = 0xaa; +} + +int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name) +{ + struct efi_header *hdr = &ptbl->header; + struct efi_entry *entry = ptbl->entry; + unsigned n; + + if (first < 34) { + fprintf(stderr,"partition '%s' overlaps partition table\n", name); + return -1; + } + + if (last > hdr->last_lba) { + fprintf(stderr,"partition '%s' does not fit on disk\n", name); + return -1; + } + for (n = 0; n < EFI_ENTRIES; n++, entry++) { + if (entry->type_uuid[0]) + continue; + memcpy(entry->type_uuid, partition_type_uuid, 16); + get_uuid(entry->uniq_uuid); + entry->first_lba = first; + entry->last_lba = last; + for (n = 0; (n < EFI_NAMELEN) && *name; n++) + entry->name[n] = *name++; + return 0; + } + fprintf(stderr,"out of partition table entries\n"); + return -1; +} + +int usage(void) +{ + fprintf(stderr, + "usage: gpttool write [ ]*\n" + " gpttool read \n" + " gpttool test [ ]*\n" + "\n" + "partition: []:[kmg] | @\n" + ); + return 0; +} + +void show(struct ptable *ptbl) +{ + struct efi_entry *entry = ptbl->entry; + unsigned n, m; + char name[EFI_NAMELEN]; + + fprintf(stderr,"ptn start block end block name\n"); + fprintf(stderr,"---- ------------- ------------- --------------------\n"); + + for (n = 0; n < EFI_ENTRIES; n++, entry++) { + if (entry->type_uuid[0] == 0) + break; + for (m = 0; m < EFI_NAMELEN; m++) { + name[m] = entry->name[m] & 127; + } + name[m] = 0; + fprintf(stderr,"#%03d %13lld %13lld %s\n", + n + 1, entry->first_lba, entry->last_lba, name); + } +} + +u64 find_next_lba(struct ptable *ptbl) +{ + struct efi_entry *entry = ptbl->entry; + unsigned n; + u64 a = 0; + for (n = 0; n < EFI_ENTRIES; n++, entry++) { + if ((entry->last_lba + 1) > a) + a = entry->last_lba + 1; + } + return a; +} + +u64 next_lba = 0; + +u64 parse_size(char *sz) +{ + int l = strlen(sz); + u64 n = strtoull(sz, 0, 10); + if (l) { + switch(sz[l-1]){ + case 'k': + case 'K': + n *= 1024; + break; + case 'm': + case 'M': + n *= (1024 * 1024); + break; + case 'g': + case 'G': + n *= (1024 * 1024 * 1024); + break; + } + } + return n; +} + +int parse_ptn(struct ptable *ptbl, char *x) +{ + char *y = strchr(x, ':'); + u64 sz; + + if (!y) { + fprintf(stderr,"invalid partition entry: %s\n", x); + return -1; + } + *y++ = 0; + + if (*y == 0) { + sz = ptbl->header.last_lba - next_lba; + } else { + sz = parse_size(y); + if (sz & 511) { + fprintf(stderr,"partition size must be multiple of 512\n"); + return -1; + } + sz /= 512; + } + + if (sz == 0) { + fprintf(stderr,"zero size partitions not allowed\n"); + return -1; + } + + if (x[0] && add_ptn(ptbl, next_lba, next_lba + sz - 1, x)) + return -1; + + next_lba = next_lba + sz; + return 0; +} + +int main(int argc, char **argv) +{ + struct ptable ptbl; + struct efi_entry *entry; + struct efi_header *hdr = &ptbl.header; + struct stat s; + u32 n; + u64 sz, blk; + int fd; + const char *device; + int real_disk = 0; + + if (argc < 2) + return usage(); + + if (!strcmp(argv[1], "write")) { + if (argc < 3) + return usage(); + device = argv[2]; + argc -= 2; + argv += 2; + real_disk = 1; + } else if (!strcmp(argv[1], "test")) { + argc -= 1; + argv += 1; + real_disk = 0; + sz = 2097152 * 16; + fprintf(stderr,"< simulating 16GB disk >\n\n"); + } else { + return usage(); + } + + if (real_disk) { + if (!strcmp(device, "/dev/sda") || + !strcmp(device, "/dev/sdb")) { + fprintf(stderr,"error: refusing to partition sda or sdb\n"); + return -1; + } + + fd = open(device, O_RDWR); + if (fd < 0) { + fprintf(stderr,"error: cannot open '%s'\n", device); + return -1; + } + if (ioctl(fd, BLKGETSIZE64, &sz)) { + fprintf(stderr,"error: cannot query block device size\n"); + return -1; + } + sz /= 512; + fprintf(stderr,"blocks %lld\n", sz); + } + + memset(&ptbl, 0, sizeof(ptbl)); + + init_mbr(ptbl.mbr, sz - 1); + + memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic)); + hdr->version = EFI_VERSION; + hdr->header_sz = sizeof(struct efi_header); + hdr->header_lba = 1; + hdr->backup_lba = sz - 1; + hdr->first_lba = 34; + hdr->last_lba = sz - 1; + get_uuid(hdr->volume_uuid); + hdr->entries_lba = 2; + hdr->entries_count = 128; + hdr->entries_size = sizeof(struct efi_entry); + + while (argc > 1) { + if (argv[1][0] == '@') { + char line[256], *p; + FILE *f; + f = fopen(argv[1] + 1, "r"); + if (!f) { + fprintf(stderr,"cannot read partitions from '%s\n", argv[1]); + return -1; + } + while (fgets(line, sizeof(line), f)) { + p = line + strlen(line); + while (p > line) { + p--; + if (*p > ' ') + break; + *p = 0; + } + p = line; + while (*p && (*p <= ' ')) + p++; + if (*p == '#') + continue; + if (*p == 0) + continue; + if (parse_ptn(&ptbl, p)) + return -1; + } + fclose(f); + } else { + if (parse_ptn(&ptbl, argv[1])) + return -1; + } + argc--; + argv++; + } + + n = crc32(0, Z_NULL, 0); + n = crc32(n, (void*) ptbl.entry, sizeof(ptbl.entry)); + hdr->entries_crc32 = n; + + n = crc32(0, Z_NULL, 0); + n = crc32(n, (void*) &ptbl.header, sizeof(ptbl.header)); + hdr->crc32 = n; + + show(&ptbl); + + if (real_disk) { + write(fd, &ptbl, sizeof(ptbl)); + fsync(fd); + + if (ioctl(fd, BLKRRPART, 0)) { + fprintf(stderr,"could not re-read partition table\n"); + } + close(fd); + } + return 0; +} diff --git a/include/acc/acc.h b/include/acc/acc.h deleted file mode 100644 index 1182355281c..00000000000 --- a/include/acc/acc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_ACC_ACC_H -#define ANDROID_ACC_ACC_H - -#include -#include - -typedef char ACCchar; -typedef int32_t ACCint; -typedef uint32_t ACCuint; -typedef ssize_t ACCsizei; -typedef unsigned int ACCenum; -typedef void ACCvoid; -typedef struct ACCscript ACCscript; - -#define ACC_NO_ERROR 0x0000 -#define ACC_INVALID_ENUM 0x0500 -#define ACC_INVALID_OPERATION 0x0502 -#define ACC_INVALID_VALUE 0x0501 -#define ACC_OUT_OF_MEMORY 0x0505 - -#define ACC_COMPILE_STATUS 0x8B81 -#define ACC_INFO_LOG_LENGTH 0x8B84 - - -// ---------------------------------------------------------------------------- - -#ifdef __cplusplus -extern "C" { -#endif - -ACCscript* accCreateScript(); - -void accDeleteScript(ACCscript* script); - -typedef ACCvoid* (*ACCSymbolLookupFn)(ACCvoid* pContext, const ACCchar * name); - -void accRegisterSymbolCallback(ACCscript* script, ACCSymbolLookupFn pFn, - ACCvoid* pContext); - -ACCenum accGetError( ACCscript* script ); - -void accScriptSource(ACCscript* script, - ACCsizei count, - const ACCchar** string, - const ACCint* length); - -void accCompileScript(ACCscript* script); - -void accGetScriptiv(ACCscript* script, - ACCenum pname, - ACCint* params); - -void accGetScriptInfoLog(ACCscript* script, - ACCsizei maxLength, - ACCsizei* length, - ACCchar* infoLog); - -void accGetScriptLabel(ACCscript* script, const ACCchar * name, - ACCvoid** address); - -void accGetPragmas(ACCscript* script, ACCsizei* actualStringCount, - ACCsizei maxStringCount, ACCchar** strings); - -/* Used to implement disassembly */ - -void accGetProgramBinary(ACCscript* script, - ACCvoid** base, - ACCsizei* length); - -#ifdef __cplusplus -}; -#endif - -// ---------------------------------------------------------------------------- - -#endif diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h index ad16e0c2626..c8ccc7e7ac7 100644 --- a/include/arch/darwin-x86/AndroidConfig.h +++ b/include/arch/darwin-x86/AndroidConfig.h @@ -85,6 +85,11 @@ */ #define HAVE_TERMIO_H +/* + * Define this if you have + */ +/* #define HAVE_SYS_SENDFILE_H 1 */ + /* * Define this if you build against MSVCRT.DLL */ @@ -141,7 +146,7 @@ */ #if (defined(__ppc__) || defined(__ppc64__)) # define HAVE_BIG_ENDIAN -#elif defined(__i386__) +#elif (defined(__i386__) || defined(__x86_64__)) # define HAVE_LITTLE_ENDIAN #endif @@ -153,6 +158,11 @@ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE 1 +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +/* #define HAVE_OFF64_T */ + /* * Defined if we have the backtrace() call for retrieving a stack trace. * Needed for CallStack to operate; if not defined, CallStack is @@ -214,7 +224,7 @@ */ #if (defined(__ppc__) || defined(__ppc64__)) # define ARCH_PPC -#elif defined(__i386__) +#elif (defined(__i386__) || defined(__x86_64__)) # define ARCH_X86 #endif @@ -250,6 +260,16 @@ */ #define HAVE_STRLCPY 1 +/* + * Define if the open_memstream() function exists on the system. + */ +/* #define HAVE_OPEN_MEMSTREAM 1 */ + +/* + * Define if the BSD funopen() function exists on the system. + */ +#define HAVE_FUNOPEN 1 + /* * Define if writev() exists */ @@ -270,4 +290,19 @@ */ #define HAVE_SCHED_H 1 +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 + +/* + * Define if we have st_mtim in struct stat + */ +#define HAVE_STAT_ST_MTIM 1 + +/* + * Define if printf() supports %zd for size_t arguments + */ +#define HAVE_PRINTF_ZD 1 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/freebsd-x86/AndroidConfig.h b/include/arch/freebsd-x86/AndroidConfig.h index 39c564b93a2..d828bd5f8b4 100644 --- a/include/arch/freebsd-x86/AndroidConfig.h +++ b/include/arch/freebsd-x86/AndroidConfig.h @@ -90,6 +90,11 @@ */ /* #define HAVE_TERMIO_H */ +/* + * Define this if you have + */ +/* #define HAVE_SYS_SENDFILE_H 1 */ + /* * Define this if you build against MSVCRT.DLL */ @@ -169,6 +174,11 @@ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE 1 +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +/* #define HAVE_OFF64_T */ + /* * Defined if we have the backtrace() call for retrieving a stack trace. * Needed for CallStack to operate; if not defined, CallStack is @@ -289,6 +299,16 @@ */ #define HAVE_STRLCPY 1 +/* + * Define if the open_memstream() function exists on the system. + */ +/* #define HAVE_OPEN_MEMSTREAM 1 */ + +/* + * Define if the BSD funopen() function exists on the system. + */ +#define HAVE_FUNOPEN 1 + /* * Define if prctl() exists */ @@ -329,4 +349,18 @@ */ #define HAVE_SCHED_H 1 +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 +/* + * Define if we have st_mtim in struct stat + */ +#define HAVE_STAT_ST_MTIM 1 + +/* + * Define if printf() supports %zd for size_t arguments + */ +#define HAVE_PRINTF_ZD 1 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h index 82e39c01dff..7eba7d09d64 100644 --- a/include/arch/linux-arm/AndroidConfig.h +++ b/include/arch/linux-arm/AndroidConfig.h @@ -42,9 +42,16 @@ #define HAVE_PTHREADS /* - * Do we have the futex syscall? + * Do we have pthread_setname_np()? + * + * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with + * the same name but different parameters, so we can't use that here.) */ +#define HAVE_ANDROID_PTHREAD_SETNAME_NP +/* + * Do we have the futex syscall? + */ #define HAVE_FUTEX /* @@ -89,7 +96,12 @@ /* * Define this if you have */ -#define HAVE_TERMIO_H +#define HAVE_TERMIO_H 1 + +/* + * Define this if you have + */ +#define HAVE_SYS_SENDFILE_H 1 /* * Define this if you build against MSVCRT.DLL @@ -99,7 +111,7 @@ /* * Define this if you have sys/uio.h */ -#define HAVE_SYS_UIO_H +#define HAVE_SYS_UIO_H 1 /* * Define this if your platforms implements symbolic links @@ -161,6 +173,11 @@ /* #define _FILE_OFFSET_BITS 64 */ /* #define _LARGEFILE_SOURCE 1 */ +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +#define HAVE_OFF64_T + /* * Defined if we have the backtrace() call for retrieving a stack trace. * Needed for CallStack to operate; if not defined, CallStack is @@ -173,7 +190,7 @@ * with a memory address. If not defined, stack crawls will not have symbolic * information. */ -#define HAVE_DLADDR 0 +#define HAVE_DLADDR 1 /* * Defined if we have the cxxabi.h header for demangling C++ symbols. If @@ -194,7 +211,9 @@ /* * Add any extra platform-specific defines here. */ +#ifndef __linux__ #define __linux__ +#endif /* * Define if we have header @@ -292,6 +311,16 @@ */ #define HAVE_STRLCPY 1 +/* + * Define if the open_memstream() function exists on the system. + */ +/* #define HAVE_OPEN_MEMSTREAM 1 */ + +/* + * Define if the BSD funopen() function exists on the system. + */ +#define HAVE_FUNOPEN 1 + /* * Define if prctl() exists */ @@ -317,4 +346,19 @@ */ #define HAVE_SCHED_H 1 +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 + +/* + * Define if we have st_mtim in struct stat + */ +#define HAVE_STAT_ST_MTIM 1 + +/* + * Define if printf() supports %zd for size_t arguments + */ +#define HAVE_PRINTF_ZD 1 + #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-ppc/AndroidConfig.h b/include/arch/linux-ppc/AndroidConfig.h new file mode 100644 index 00000000000..2ec99f05261 --- /dev/null +++ b/include/arch/linux-ppc/AndroidConfig.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android config -- "Linux". Used for desktop ppc Linux. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have the futex syscall? + */ + +#define HAVE_FUTEX + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc//oom_adj to modify the out-of-memory + * badness adjustment. + */ +#define HAVE_OOM_ADJ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_SYSV_IPC + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP + +/* + * Define this if you have + */ +#define HAVE_TERMIO_H 1 + +/* + * Define this if you have + */ +#define HAVE_SYS_SENDFILE_H 1 + +/* + * Define this if you build against MSVCRT.DLL + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H 1 + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS + +/* + * Define this if we have localtime_r(). + */ +#define HAVE_LOCALTIME_R + +/* + * Define this if we have gethostbyname_r(). + */ +#define HAVE_GETHOSTBYNAME_R + +/* + * Define this if we have ioctl(). + */ +#define HAVE_IOCTL + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + * + * Desktop Linux has this in librt, but it's broken in goobuntu, yielding + * mildly or wildly inaccurate results. + */ +/*#define HAVE_POSIX_CLOCKS*/ + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +/* #define HAVE_TIMEDWAIT_MONOTONIC */ + +/* + * Define this if we have linux style epoll() + */ +#define HAVE_EPOLL + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#define HAVE_ENDIAN_H +#define HAVE_BIG_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 + +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +#define HAVE_OFF64_T + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 1 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 1 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +/* #define HAVE_GETTID */ + +/* + * Defined if we have the sched_setscheduler() call + */ +#define HAVE_SCHED_SETSCHEDULER + +/* + * Add any extra platform-specific defines here. + */ + +/* + * Define if we have header + */ +#define HAVE_MALLOC_H + +/* + * Define if we have Linux-style non-filesystem Unix Domain Sockets + */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_PPC + + +/* + * Define if we have Linux's inotify in . + */ +/*#define HAVE_INOTIFY 1*/ + +/* + * Define if we have madvise() in + */ +#define HAVE_MADVISE 1 + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if libc includes Android system properties implementation. + */ +/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +#define HAVE_SYSTEM_PROPERTY_SERVER + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * Do we have the sigaction flag SA_NOCLDWAIT? + */ +#define HAVE_SA_NOCLDWAIT + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + */ +#define OS_CASE_SENSITIVE + +/* + * Define if exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +/* #define HAVE_STRLCPY 1 */ + +/* + * Define if the open_memstream() function exists on the system. + */ +#define HAVE_OPEN_MEMSTREAM 1 + +/* + * Define if the BSD funopen() function exists on the system. + */ +/* #define HAVE_FUNOPEN 1 */ + +/* + * Define if prctl() exists + */ +#define HAVE_PRCTL 1 + +/* + * Define if writev() exists + */ +#define HAVE_WRITEV 1 + +/* + * Define if exists. + */ +#define HAVE_STDINT_H 1 + +/* + * Define if exists. + */ +#define HAVE_STDBOOL_H 1 + +/* + * Define if exists. + */ +#define HAVE_SCHED_H 1 + +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 + +#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-sh/AndroidConfig.h b/include/arch/linux-sh/AndroidConfig.h new file mode 100644 index 00000000000..f51caeb7216 --- /dev/null +++ b/include/arch/linux-sh/AndroidConfig.h @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android config -- "android-sh". Used for SuperH device builds. + */ +#ifndef _ANDROID_CONFIG_H +#define _ANDROID_CONFIG_H + +/* + * =========================================================================== + * !!! IMPORTANT !!! + * =========================================================================== + * + * This file is included by ALL C/C++ source files. Don't put anything in + * here unless you are absolutely certain it can't go anywhere else. + * + * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" + * comments. + */ + +/* + * Threading model. Choose one: + * + * HAVE_PTHREADS - use the pthreads library. + * HAVE_WIN32_THREADS - use Win32 thread primitives. + * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX + */ +#define HAVE_PTHREADS + +/* + * Do we have pthread_setname_np()? + * + * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with + * the same name but different parameters, so we can't use that here.) + */ +#define HAVE_ANDROID_PTHREAD_SETNAME_NP + +/* + * Do we have the futex syscall? + */ +#define HAVE_FUTEX + +/* + * Define if we already have the futex wrapper functions defined. Yes if + * compiling against bionic. + */ +#define HAVE_FUTEX_WRAPPERS 1 + +/* + * Process creation model. Choose one: + * + * HAVE_FORKEXEC - use fork() and exec() + * HAVE_WIN32_PROC - use CreateProcess() + */ +#define HAVE_FORKEXEC + +/* + * Process out-of-memory adjustment. Set if running on Linux, + * where we can write to /proc//oom_adj to modify the out-of-memory + * badness adjustment. + */ +#define HAVE_OOM_ADJ + +/* + * IPC model. Choose one: + * + * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). + * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). + * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). + * HAVE_ANDROID_IPC - use Android versions (?, mmap). + */ +#define HAVE_ANDROID_IPC + +/* + * Memory-mapping model. Choose one: + * + * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h + * HAVE_WIN32_FILEMAP - use Win32 filemaps + */ +#define HAVE_POSIX_FILEMAP + +/* + * Define this if you have + */ +#define HAVE_TERMIO_H 1 + +/* + * Define this if you have + */ +#define HAVE_SYS_SENDFILE_H 1 + +/* + * Define this if you build against MSVCRT.DLL + */ +/* #define HAVE_MS_C_RUNTIME */ + +/* + * Define this if you have sys/uio.h + */ +#define HAVE_SYS_UIO_H 1 + +/* + * Define this if your platforms implements symbolic links + * in its filesystems + */ +#define HAVE_SYMLINKS + +/* + * Define this if we have localtime_r(). + */ +/* #define HAVE_LOCALTIME_R */ + +/* + * Define this if we have gethostbyname_r(). + */ +/* #define HAVE_GETHOSTBYNAME_R */ + +/* + * Define this if we have ioctl(). + */ +#define HAVE_IOCTL + +/* + * Define this if we want to use WinSock. + */ +/* #define HAVE_WINSOCK */ + +/* + * Define this if have clock_gettime() and friends + */ +#define HAVE_POSIX_CLOCKS + +/* + * Define this if we have pthread_cond_timedwait_monotonic() and + * clock_gettime(CLOCK_MONOTONIC). + */ +/* #define HAVE_TIMEDWAIT_MONOTONIC */ + +/* + * Define this if we have linux style epoll() + */ +#define HAVE_EPOLL + +/* + * Endianness of the target machine. Choose one: + * + * HAVE_ENDIAN_H -- have endian.h header we can include. + * HAVE_LITTLE_ENDIAN -- we are little endian. + * HAVE_BIG_ENDIAN -- we are big endian. + */ +#define HAVE_ENDIAN_H +#define HAVE_LITTLE_ENDIAN + +/* + * We need to choose between 32-bit and 64-bit off_t. All of our code should + * agree on the same size. For desktop systems, use 64-bit values, + * because some of our libraries (e.g. wxWidgets) expect to be built that way. + */ +/* #define _FILE_OFFSET_BITS 64 */ +/* #define _LARGEFILE_SOURCE 1 */ + +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +#define HAVE_OFF64_T + +/* + * Defined if we have the backtrace() call for retrieving a stack trace. + * Needed for CallStack to operate; if not defined, CallStack is + * non-functional. + */ +#define HAVE_BACKTRACE 0 + +/* + * Defined if we have the dladdr() call for retrieving the symbol associated + * with a memory address. If not defined, stack crawls will not have symbolic + * information. + */ +#define HAVE_DLADDR 0 + +/* + * Defined if we have the cxxabi.h header for demangling C++ symbols. If + * not defined, stack crawls will be displayed with raw mangled symbols + */ +#define HAVE_CXXABI 0 + +/* + * Defined if we have the gettid() system call. + */ +#define HAVE_GETTID + +/* + * Defined if we have the sched_setscheduler() call + */ +#define HAVE_SCHED_SETSCHEDULER + +/* + * Add any extra platform-specific defines here. + */ +/* #define __linux__ */ /* for SuperH */ + +/* + * Define if we have header + */ +#define HAVE_MALLOC_H + +/* + * Define if we're running on *our* linux on device or emulator. + */ +#define HAVE_ANDROID_OS 1 + +/* + * Define if we have Linux-style non-filesystem Unix Domain Sockets + */ +#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 + +/* + * Define if we have Linux's inotify in . + */ +#define HAVE_INOTIFY 1 + +/* + * Define if we have madvise() in + */ +#define HAVE_MADVISE 1 + +/* + * Define if tm struct has tm_gmtoff field + */ +#define HAVE_TM_GMTOFF 1 + +/* + * Define if dirent struct has d_type field + */ +#define HAVE_DIRENT_D_TYPE 1 + +/* + * Define if libc includes Android system properties implementation. + */ +#define HAVE_LIBC_SYSTEM_PROPERTIES 1 + +/* + * Define if system provides a system property server (should be + * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). + */ +/* #define HAVE_SYSTEM_PROPERTY_SERVER */ + +/* + * What CPU architecture does this platform use? + */ +#define ARCH_SH + +/* + * Define if the size of enums is as short as possible, + */ +/* #define HAVE_SHORT_ENUMS */ + +/* + * sprintf() format string for shared library naming. + */ +#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" + +/* + * Do we have __memcmp16()? + * + * TODO : Investigate the perfomance impact of __memcmp16() + * and implement it. + * This influences on dalvikVM's string performance. + * See dalvik/vm/InlineNative.c. + */ +/* #define HAVE__MEMCMP16 */ + +/* + * type for the third argument to mincore(). + */ +#define MINCORE_POINTER_TYPE unsigned char * + +/* + * Do we have the sigaction flag SA_NOCLDWAIT? + */ +#define HAVE_SA_NOCLDWAIT + +/* + * The default path separator for the platform + */ +#define OS_PATH_SEPARATOR '/' + +/* + * Is the filesystem case sensitive? + */ +#define OS_CASE_SENSITIVE + +/* + * Define if exists. + */ +#define HAVE_SYS_SOCKET_H 1 + +/* + * Define if the strlcpy() function exists on the system. + */ +#define HAVE_STRLCPY 1 + +/* + * Define if the open_memstream() function exists on the system. + */ +/* #define HAVE_OPEN_MEMSTREAM 1 */ + +/* + * Define if the BSD funopen() function exists on the system. + */ +#define HAVE_FUNOPEN 1 + +/* + * Define if prctl() exists + */ +#define HAVE_PRCTL 1 + +/* + * Define if writev() exists + */ +#define HAVE_WRITEV 1 + +/* + * For dalvik/libcore + */ +#define CANT_PASS_VALIST_AS_CHARPTR + +/* + * For external/bluez/utils/tools/hciattach.c + * TODO : This definition should be somewhere in bionic/libc/kernel/(*). + * Cosider the place and move it there. + */ +#define N_TTY 0 + +/* + * Whether or not _Unwind_Context is defined as a struct. + */ +#define HAVE_UNWIND_CONTEXT_STRUCT + +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 + +/* + * Define if we have st_mtim in struct stat + */ +#define HAVE_STAT_ST_MTIM 1 + +/* + * Define if printf() supports %zd for size_t arguments + */ +#define HAVE_PRINTF_ZD 1 + +#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h index 557ec5f9466..24e77976f5d 100644 --- a/include/arch/linux-x86/AndroidConfig.h +++ b/include/arch/linux-x86/AndroidConfig.h @@ -83,7 +83,12 @@ /* * Define this if you have */ -#define HAVE_TERMIO_H +#define HAVE_TERMIO_H 1 + +/* + * Define this if you have + */ +#define HAVE_SYS_SENDFILE_H 1 /* * Define this if you build against MSVCRT.DLL @@ -93,7 +98,7 @@ /* * Define this if you have sys/uio.h */ -#define HAVE_SYS_UIO_H +#define HAVE_SYS_UIO_H 1 /* * Define this if your platforms implements symbolic links @@ -158,6 +163,11 @@ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE 1 +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +#define HAVE_OFF64_T + /* * Defined if we have the backtrace() call for retrieving a stack trace. * Needed for CallStack to operate; if not defined, CallStack is @@ -273,6 +283,16 @@ */ /* #define HAVE_STRLCPY 1 */ +/* + * Define if the open_memstream() function exists on the system. + */ +#define HAVE_OPEN_MEMSTREAM 1 + +/* + * Define if the BSD funopen() function exists on the system. + */ +/* #define HAVE_FUNOPEN 1 */ + /* * Define if prctl() exists */ @@ -298,4 +318,19 @@ */ #define HAVE_SCHED_H 1 +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 + +/* + * Define if we have st_mtim in struct stat + */ +#define HAVE_STAT_ST_MTIM 1 + +/* + * Define if printf() supports %zd for size_t arguments + */ +#define HAVE_PRINTF_ZD 1 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h index 6605723291b..05dd220795d 100644 --- a/include/arch/target_linux-x86/AndroidConfig.h +++ b/include/arch/target_linux-x86/AndroidConfig.h @@ -28,9 +28,16 @@ #define HAVE_PTHREADS /* - * Do we have the futex syscall? + * Do we have pthread_setname_np()? + * + * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with + * the same name but different parameters, so we can't use that here.) */ +#define HAVE_ANDROID_PTHREAD_SETNAME_NP +/* + * Do we have the futex syscall? + */ #define HAVE_FUTEX /* @@ -77,6 +84,11 @@ */ #define HAVE_TERMIO_H 1 +/* + * Define this if you have + */ +#define HAVE_SYS_SENDFILE_H 1 + /* * Define this if you build against have Microsoft C runtime (MSVCRT.DLL) */ @@ -150,6 +162,11 @@ * #define _LARGEFILE_SOURCE 1 */ +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +#define HAVE_OFF64_T + /* * Defined if we have the backtrace() call for retrieving a stack trace. * Needed for CallStack to operate; if not defined, CallStack is @@ -215,7 +232,7 @@ /* * Define if we have Linux's dbus */ -#define HAVE_DBUS 1 +/* #define HAVE_DBUS 1 */ /* * Define if tm struct has tm_gmtoff field @@ -283,6 +300,16 @@ */ #define HAVE_STRLCPY 1 +/* + * Define if the open_memstream() function exists on the system. + */ +/* #define HAVE_OPEN_MEMSTREAM 1 */ + +/* + * Define if the BSD funopen() function exists on the system. + */ +#define HAVE_FUNOPEN 1 + /* * Define if prctl() exists */ @@ -308,4 +335,19 @@ */ #define HAVE_SCHED_H 1 +/* + * Define if pread() exists + */ +#define HAVE_PREAD 1 + +/* + * Define if we have st_mtim in struct stat + */ +#define HAVE_STAT_ST_MTIM 1 + +/* + * Define if printf() supports %zd for size_t arguments + */ +#define HAVE_PRINTF_ZD 1 + #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h index b240519a058..ad890b4ddb5 100644 --- a/include/arch/windows/AndroidConfig.h +++ b/include/arch/windows/AndroidConfig.h @@ -100,6 +100,13 @@ # define HAVE_TERMIO_H #endif +/* + * Define this if you have + */ +#ifdef __CYGWIN__ +# define HAVE_SYS_SENDFILE_H 1 +#endif + /* * Define this if you build against MSVCRT.DLL */ @@ -169,6 +176,11 @@ #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE 1 +/* + * Define if platform has off64_t (and lseek64 and other xxx64 functions) + */ +#define HAVE_OFF64_T + /* * Defined if we have the backtrace() call for retrieving a stack trace. * Needed for CallStack to operate; if not defined, CallStack is @@ -264,6 +276,16 @@ */ /* #define HAVE_STRLCPY 1 */ +/* + * Define if the open_memstream() function exists on the system. + */ +/* #define HAVE_OPEN_MEMSTREAM 1 */ + +/* + * Define if the BSD funopen() function exists on the system. + */ +/* #define HAVE_FUNOPEN 1 */ + /* * Define if exists. * Only MinGW has it. @@ -279,7 +301,6 @@ */ #ifdef USE_MINGW #define S_IRGRP 0 -#define sleep _sleep #endif /* @@ -295,11 +316,26 @@ /* * Define if exists. */ -/* #define HAVE_STDBOOL_H */ +#define HAVE_STDBOOL_H /* * Define if exists. */ /* #define HAVE_SCHED_H */ +/* + * Define if pread() exists + */ +/* #define HAVE_PREAD 1 */ + +/* + * Define if we have st_mtim in struct stat + */ +/* #define HAVE_STAT_ST_MTIM 1 */ + +/* + * Define if printf() supports %zd for size_t arguments + */ +/* #define HAVE_PRINTF_ZD 1 */ + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h new file mode 100644 index 00000000000..0c79be7e37b --- /dev/null +++ b/include/cutils/android_reboot.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_ANDROID_REBOOT_H__ +#define __CUTILS_ANDROID_REBOOT_H__ + +__BEGIN_DECLS + +/* Commands */ +#define ANDROID_RB_RESTART 0xDEAD0001 +#define ANDROID_RB_POWEROFF 0xDEAD0002 +#define ANDROID_RB_RESTART2 0xDEAD0003 + +/* Flags */ +#define ANDROID_RB_FLAG_NO_SYNC 0x1 +#define ANDROID_RB_FLAG_NO_REMOUNT_RO 0x2 + +int android_reboot(int cmd, int flags, char *arg); + +__END_DECLS + +#endif /* __CUTILS_ANDROID_REBOOT_H__ */ diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h index fd56dbef344..25b233e6a1b 100644 --- a/include/cutils/ashmem.h +++ b/include/cutils/ashmem.h @@ -10,7 +10,7 @@ #ifndef _CUTILS_ASHMEM_H #define _CUTILS_ASHMEM_H -#include +#include #ifdef __cplusplus extern "C" { diff --git a/include/cutils/atomic-arm.h b/include/cutils/atomic-arm.h new file mode 100644 index 00000000000..16fe512423a --- /dev/null +++ b/include/cutils/atomic-arm.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_ARM_H +#define ANDROID_CUTILS_ATOMIC_ARM_H + +#include +#include + +extern inline void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern inline void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern inline void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#elif defined(__ARM_HAVE_DMB) +extern inline void android_memory_barrier(void) +{ + __asm__ __volatile__ ("dmb" : : : "memory"); +} +extern inline void android_memory_store_barrier(void) +{ + __asm__ __volatile__ ("dmb st" : : : "memory"); +} +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline void android_memory_barrier(void) +{ + __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory"); +} +extern inline void android_memory_store_barrier(void) +{ + android_memory_barrier(); +} +#else +extern inline void android_memory_barrier(void) +{ + typedef void (kuser_memory_barrier)(void); + (*(kuser_memory_barrier *)0xffff0fa0)(); +} +extern inline void android_memory_store_barrier(void) +{ + android_memory_barrier(); +} +#endif + +extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern inline void android_atomic_acquire_store(int32_t value, + volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern inline void android_atomic_release_store(int32_t value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +#if defined(__thumb__) +extern int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ("ldrex %0, [%3]\n" + "mov %1, #0\n" + "teq %0, %4\n" + "strexeq %1, %5, [%3]" + : "=&r" (prev), "=&r" (status), "+m"(*ptr) + : "r" (ptr), "Ir" (old_value), "r" (new_value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev != old_value; +} +#else +extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *); + int32_t prev, status; + prev = *ptr; + do { + status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr); + if (__builtin_expect(status == 0, 1)) + return 0; + prev = *ptr; + } while (prev == old_value); + return 1; +} +#endif + +extern inline int android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern inline int android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + + +#if defined(__thumb__) +extern int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "add %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (increment) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#else +extern inline int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev + increment, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#endif + +extern inline int32_t android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern inline int32_t android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +#if defined(__thumb__) +extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "and %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#else +extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#endif + +#if defined(__thumb__) +extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "orr %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#else +extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#endif + +#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */ diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h new file mode 100644 index 00000000000..49f3e702a20 --- /dev/null +++ b/include/cutils/atomic-inline.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_INLINE_H +#define ANDROID_CUTILS_ATOMIC_INLINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Inline declarations and macros for some special-purpose atomic + * operations. These are intended for rare circumstances where a + * memory barrier needs to be issued inline rather than as a function + * call. + * + * Most code should not use these. + * + * Anything that does include this file must set ANDROID_SMP to either + * 0 or 1, indicating compilation for UP or SMP, respectively. + * + * Macros defined in this header: + * + * void ANDROID_MEMBAR_FULL(void) + * Full memory barrier. Provides a compiler reordering barrier, and + * on SMP systems emits an appropriate instruction. + */ + +#if !defined(ANDROID_SMP) +# error "Must define ANDROID_SMP before including atomic-inline.h" +#endif + +#if defined(__arm__) +#include +#elif defined(__i386__) || defined(__x86_64__) +#include +#elif defined(__sh__) +/* implementation is in atomic-android-sh.c */ +#else +#error atomic operations are unsupported +#endif + +#if ANDROID_SMP == 0 +#define ANDROID_MEMBAR_FULL android_compiler_barrier +#else +#define ANDROID_MEMBAR_FULL android_memory_barrier +#endif + +#if ANDROID_SMP == 0 +#define ANDROID_MEMBAR_STORE android_compiler_barrier +#else +#define ANDROID_MEMBAR_STORE android_memory_store_barrier +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ANDROID_CUTILS_ATOMIC_INLINE_H */ diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h new file mode 100644 index 00000000000..438012e5047 --- /dev/null +++ b/include/cutils/atomic-x86.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_X86_H +#define ANDROID_CUTILS_ATOMIC_X86_H + +#include + +extern inline void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern inline void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern inline void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern inline void android_memory_barrier(void) +{ + __asm__ __volatile__ ("mfence" : : : "memory"); +} +extern inline void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#endif + +extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_compiler_barrier(); + return value; +} + +extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern inline void android_atomic_acquire_store(int32_t value, + volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern inline void android_atomic_release_store(int32_t value, + volatile int32_t *ptr) +{ + android_compiler_barrier(); + *ptr = value; +} + +extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev != old_value; +} + +extern inline int android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + /* Loads are not reordered with other loads. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern inline int android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + /* Stores are not reordered with other stores. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern inline int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr) +{ + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "+r" (increment), "+m" (*ptr) + : : "memory"); + /* increment now holds the old value of *ptr */ + return increment; +} + +extern inline int32_t android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern inline int32_t android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern inline int32_t android_atomic_and(int32_t value, + volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_X86_H */ diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h index 5694d66ac61..ae42eb8a0d5 100644 --- a/include/cutils/atomic.h +++ b/include/cutils/atomic.h @@ -25,53 +25,95 @@ extern "C" { #endif /* - * NOTE: memory shared between threads is synchronized by all atomic operations - * below, this means that no explicit memory barrier is required: all reads or - * writes issued before android_atomic_* operations are guaranteed to complete - * before the atomic operation takes place. + * A handful of basic atomic operations. The appropriate pthread + * functions should be used instead of these whenever possible. + * + * The "acquire" and "release" terms can be defined intuitively in terms + * of the placement of memory barriers in a simple lock implementation: + * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds + * - barrier + * - [do work] + * - barrier + * - store(lock-is-free) + * In very crude terms, the initial (acquire) barrier prevents any of the + * "work" from happening before the lock is held, and the later (release) + * barrier ensures that all of the work happens before the lock is released. + * (Think of cached writes, cache read-ahead, and instruction reordering + * around the CAS and store instructions.) + * + * The barriers must apply to both the compiler and the CPU. Note it is + * legal for instructions that occur before an "acquire" barrier to be + * moved down below it, and for instructions that occur after a "release" + * barrier to be moved up above it. + * + * The ARM-driven implementation we use here is short on subtlety, + * and actually requests a full barrier from the compiler and the CPU. + * The only difference between acquire and release is in whether they + * are issued before or after the atomic operation with which they + * are associated. To ease the transition to C/C++ atomic intrinsics, + * you should not rely on this, and instead assume that only the minimal + * acquire/release protection is provided. + * + * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries. + * If they are not, atomicity is not guaranteed. */ -void android_atomic_write(int32_t value, volatile int32_t* addr); - /* - * all these atomic operations return the previous value + * Basic arithmetic and bitwise operations. These all provide a + * barrier with "release" ordering, and return the previous value. + * + * These have the same characteristics (e.g. what happens on overflow) + * as the equivalent non-atomic C operations. */ - - int32_t android_atomic_inc(volatile int32_t* addr); int32_t android_atomic_dec(volatile int32_t* addr); - int32_t android_atomic_add(int32_t value, volatile int32_t* addr); int32_t android_atomic_and(int32_t value, volatile int32_t* addr); int32_t android_atomic_or(int32_t value, volatile int32_t* addr); -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr); - /* - * NOTE: Two "quasiatomic" operations on the exact same memory address - * are guaranteed to operate atomically with respect to each other, - * but no guarantees are made about quasiatomic operations mixed with - * non-quasiatomic operations on the same address, nor about - * quasiatomic operations that are performed on partially-overlapping - * memory. + * Perform an atomic load with "acquire" or "release" ordering. + * + * This is only necessary if you need the memory barrier. A 32-bit read + * from a 32-bit aligned address is atomic on all supported platforms. */ +int32_t android_atomic_acquire_load(volatile const int32_t* addr); +int32_t android_atomic_release_load(volatile const int32_t* addr); -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr); -int64_t android_quasiatomic_read_64(volatile int64_t* addr); - /* - * cmpxchg return a non zero value if the exchange was NOT performed, - * in other words if oldvalue != *addr + * Perform an atomic store with "acquire" or "release" ordering. + * + * This is only necessary if you need the memory barrier. A 32-bit write + * to a 32-bit aligned address is atomic on all supported platforms. */ +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr); +void android_atomic_release_store(int32_t value, volatile int32_t* addr); -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, +/* + * Compare-and-set operation with "acquire" or "release" ordering. + * + * This returns zero if the new value was successfully stored, which will + * only happen when *addr == oldvalue. + * + * (The return value is inverted from implementations on other platforms, + * but matches the ARM ldrex/strex result.) + * + * Implementations that use the release CAS in a loop may be less efficient + * than possible, because we re-issue the memory barrier on each iteration. + */ +int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr); +int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr); -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr); - +/* + * Aliases for code using an older version of this header. These are now + * deprecated and should not be used. The definitions will be removed + * in a future release. + */ +#define android_atomic_write android_atomic_release_store +#define android_atomic_cmpxchg android_atomic_release_cas - #ifdef __cplusplus } // extern "C" #endif diff --git a/include/cutils/bitops.h b/include/cutils/bitops.h new file mode 100644 index 00000000000..1b3b762e6bf --- /dev/null +++ b/include/cutils/bitops.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_BITOPS_H +#define __CUTILS_BITOPS_H + +#include + +__BEGIN_DECLS + +static inline int popcount(unsigned int x) +{ + return __builtin_popcount(x); +} + +static inline int popcountl(unsigned long x) +{ + return __builtin_popcountl(x); +} + +static inline int popcountll(unsigned long long x) +{ + return __builtin_popcountll(x); +} + +__END_DECLS + +#endif /* __CUTILS_BITOPS_H */ diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h index 09112d5ac67..70f884a1e70 100644 --- a/include/cutils/compiler.h +++ b/include/cutils/compiler.h @@ -29,4 +29,16 @@ # define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) #endif +/** + * exports marked symbols + * + * if used on a C++ class declaration, this macro must be inserted + * after the "class" keyword. For instance: + * + * template + * class ANDROID_API Singleton { } + */ + +#define ANDROID_API __attribute__((visibility("default"))) + #endif // ANDROID_CUTILS_COMPILER_H diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h index f3fb370a8a3..2dea6f19fa1 100644 --- a/include/cutils/config_utils.h +++ b/include/cutils/config_utils.h @@ -54,6 +54,9 @@ const char* config_str(cnode *root, const char *name, const char *_default); /* add a named child to a config node (or modify it if it already exists) */ void config_set(cnode *root, const char *name, const char *value); +/* free a config node tree */ +void config_free(cnode *root); + #ifdef __cplusplus } #endif diff --git a/include/cutils/iosched_policy.h b/include/cutils/iosched_policy.h new file mode 100644 index 00000000000..07c5d1fca50 --- /dev/null +++ b/include/cutils/iosched_policy.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_IOSCHED_POLICY_H +#define __CUTILS_IOSCHED_POLICY_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + IoSchedClass_NONE, + IoSchedClass_RT, + IoSchedClass_BE, + IoSchedClass_IDLE, +} IoSchedClass; + +extern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio); +extern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_IOSCHED_POLICY_H */ diff --git a/include/cutils/jstring.h b/include/cutils/jstring.h index ee0018fcc7a..bf729731596 100644 --- a/include/cutils/jstring.h +++ b/include/cutils/jstring.h @@ -24,7 +24,9 @@ extern "C" { #endif +#if __cplusplus < 201103L && !defined(__GXX_EXPERIMENTAL_CXX0X__) typedef uint16_t char16_t; +#endif extern char * strndup16to8 (const char16_t* s, size_t n); extern size_t strnlen16to8 (const char16_t* s, size_t n); diff --git a/include/cutils/klog.h b/include/cutils/klog.h new file mode 100644 index 00000000000..1335543041d --- /dev/null +++ b/include/cutils/klog.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CUTILS_KLOG_H_ +#define _CUTILS_KLOG_H_ + +void klog_init(void); +void klog_set_level(int level); +void klog_close(void); +void klog_write(int level, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); + +#define KLOG_ERROR(tag,x...) klog_write(3, "<3>" tag ": " x) +#define KLOG_WARNING(tag,x...) klog_write(4, "<4>" tag ": " x) +#define KLOG_NOTICE(tag,x...) klog_write(5, "<5>" tag ": " x) +#define KLOG_INFO(tag,x...) klog_write(6, "<6>" tag ": " x) +#define KLOG_DEBUG(tag,x...) klog_write(7, "<7>" tag ": " x) + +#define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ + +#endif diff --git a/include/cutils/list.h b/include/cutils/list.h new file mode 100644 index 00000000000..eb5a3c84a9c --- /dev/null +++ b/include/cutils/list.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CUTILS_LIST_H_ +#define _CUTILS_LIST_H_ + +#include + +struct listnode +{ + struct listnode *next; + struct listnode *prev; +}; + +#define node_to_item(node, container, member) \ + (container *) (((char*) (node)) - offsetof(container, member)) + +#define list_declare(name) \ + struct listnode name = { \ + .next = &name, \ + .prev = &name, \ + } + +#define list_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +#define list_for_each_reverse(node, list) \ + for (node = (list)->prev; node != (list); node = node->prev) + +void list_init(struct listnode *list); +void list_add_tail(struct listnode *list, struct listnode *item); +void list_remove(struct listnode *item); + +#define list_empty(list) ((list) == (list)->next) +#define list_head(list) ((list)->next) +#define list_tail(list) ((list)->prev) + +#endif diff --git a/include/cutils/log.h b/include/cutils/log.h index ec3cac878b2..42d738296ca 100644 --- a/include/cutils/log.h +++ b/include/cutils/log.h @@ -196,6 +196,91 @@ extern "C" { #define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG) #endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose system log message using the current LOG_TAG. + */ +#ifndef SLOGV +#if LOG_NDEBUG +#define SLOGV(...) ((void)0) +#else +#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef SLOGV_IF +#if LOG_NDEBUG +#define SLOGV_IF(cond, ...) ((void)0) +#else +#define SLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug system log message using the current LOG_TAG. + */ +#ifndef SLOGD +#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGD_IF +#define SLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info system log message using the current LOG_TAG. + */ +#ifndef SLOGI +#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGI_IF +#define SLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning system log message using the current LOG_TAG. + */ +#ifndef SLOGW +#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGW_IF +#define SLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error system log message using the current LOG_TAG. + */ +#ifndef SLOGE +#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGE_IF +#define SLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + + + // --------------------------------------------------------------------- /* @@ -204,13 +289,17 @@ extern "C" { * It is NOT stripped from release builds. Note that the condition test * is -inverted- from the normal assert() semantics. */ +#ifndef LOG_ALWAYS_FATAL_IF #define LOG_ALWAYS_FATAL_IF(cond, ...) \ ( (CONDITION(cond)) \ - ? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \ + ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ : (void)0 ) +#endif +#ifndef LOG_ALWAYS_FATAL #define LOG_ALWAYS_FATAL(...) \ - ( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) ) + ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) +#endif /* * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that @@ -218,13 +307,21 @@ extern "C" { */ #if LOG_NDEBUG +#ifndef LOG_FATAL_IF #define LOG_FATAL_IF(cond, ...) ((void)0) +#endif +#ifndef LOG_FATAL #define LOG_FATAL(...) ((void)0) +#endif #else -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__) +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) +#endif +#ifndef LOG_FATAL #define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) +#endif #endif @@ -232,8 +329,10 @@ extern "C" { * Assertion that generates a log message when the assertion fails. * Stripped out of release builds. Uses the current LOG_TAG. */ -#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__) +#ifndef LOG_ASSERT +#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) //#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) +#endif // --------------------------------------------------------------------- @@ -292,18 +391,24 @@ typedef enum { } AndroidEventLogType; +#ifndef LOG_EVENT_INT #define LOG_EVENT_INT(_tag, _value) { \ int intBuf = _value; \ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ sizeof(intBuf)); \ } +#endif +#ifndef LOG_EVENT_LONG #define LOG_EVENT_LONG(_tag, _value) { \ long long longBuf = _value; \ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ sizeof(longBuf)); \ } +#endif +#ifndef LOG_EVENT_STRING #define LOG_EVENT_STRING(_tag, _value) \ ((void) 0) /* not implemented -- must combine len with string */ +#endif /* TODO: something for LIST */ /* @@ -318,8 +423,24 @@ typedef enum { #define android_vprintLog(prio, cond, tag, fmt...) \ __android_log_vprint(prio, tag, fmt) +/* XXX Macros to work around syntax errors in places where format string + * arg is not passed to LOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF + * (happens only in debug builds). + */ + +/* Returns 2nd arg. Used to substitute default value if caller's vararg list + * is empty. + */ +#define __android_second(dummy, second, ...) second + +/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise + * returns nothing. + */ +#define __android_rest(first, ...) , ## __VA_ARGS__ + #define android_printAssert(cond, tag, fmt...) \ - __android_log_assert(cond, tag, fmt) + __android_log_assert(cond, tag, \ + __android_second(0, ## fmt, NULL) __android_rest(fmt)) #define android_writeLog(prio, tag, text) \ __android_log_write(prio, tag, text) @@ -328,7 +449,7 @@ typedef enum { __android_log_bwrite(tag, payload, len) #define android_btWriteLog(tag, type, payload, len) \ __android_log_btwrite(tag, type, payload, len) - + // TODO: remove these prototypes and their users #define android_testLog(prio, tag) (1) #define android_writevLog(vec,num) do{}while(0) @@ -338,6 +459,21 @@ typedef enum { #define android_logToFile(tag, file) (0) #define android_logToFd(tag, fd) (0) +typedef enum { + LOG_ID_MAIN = 0, + LOG_ID_RADIO = 1, + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + + LOG_ID_MAX +} log_id_t; + +/* + * Send a simple string to the log. + */ +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); + #ifdef __cplusplus } diff --git a/include/cutils/logger.h b/include/cutils/logger.h index 3a08019a886..b60f7ad59a1 100644 --- a/include/cutils/logger.h +++ b/include/cutils/logger.h @@ -25,6 +25,7 @@ struct logger_entry { #define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" #define LOGGER_ENTRY_MAX_LEN (4*1024) #define LOGGER_ENTRY_MAX_PAYLOAD \ diff --git a/include/cutils/logprint.h b/include/cutils/logprint.h index d6ec480cf79..1102154e74e 100644 --- a/include/cutils/logprint.h +++ b/include/cutils/logprint.h @@ -38,6 +38,11 @@ typedef enum { FORMAT_LONG, } AndroidLogPrintFormat; +typedef enum { + OUTPUT_COLOR_ON = 0, + OUTPUT_COLOR_OFF, +} AndroidLogColoredOutput; + typedef struct AndroidLogFormat_t AndroidLogFormat; typedef struct AndroidLogEntry_t { @@ -58,6 +63,8 @@ void android_log_format_free(AndroidLogFormat *p_format); void android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format); +void android_log_setColoredOutput(AndroidLogFormat *p_format); + /** * Returns FORMAT_OFF on invalid string */ @@ -142,7 +149,7 @@ char *android_log_formatLogLine ( * Assumes single threaded execution * */ -int android_log_filterAndPrintLogLine( +int android_log_printLogLine( AndroidLogFormat *p_format, int fd, const AndroidLogEntry *entry); diff --git a/include/cutils/mspace.h b/include/cutils/mspace.h index 33410c10500..93fe48eed07 100644 --- a/include/cutils/mspace.h +++ b/include/cutils/mspace.h @@ -80,7 +80,18 @@ mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity, mspace create_contiguous_mspace_with_name(size_t starting_capacity, size_t max_capacity, int locked, const char *name); +/* + Identical to create_contiguous_mspace, but uses previously mapped memory. +*/ +mspace create_contiguous_mspace_with_base(size_t starting_capacity, + size_t max_capacity, int locked, void *base); + size_t destroy_contiguous_mspace(mspace msp); + +/* + Returns the position of the "break" within the given mspace. +*/ +void *contiguous_mspace_sbrk0(mspace msp); #endif /* diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h index 89d6b65e246..268c5d3f51b 100644 --- a/include/cutils/native_handle.h +++ b/include/cutils/native_handle.h @@ -21,7 +21,7 @@ extern "C" { #endif -typedef struct +typedef struct native_handle { int version; /* sizeof(native_handle_t) */ int numFds; /* number of file-descriptors at &data[0] */ @@ -29,10 +29,6 @@ typedef struct int data[0]; /* numFds + numInts ints */ } native_handle_t; - -/* keep the old definition for backward source-compatibility */ -typedef native_handle_t native_handle; - /* * native_handle_close * diff --git a/include/cutils/open_memstream.h b/include/cutils/open_memstream.h new file mode 100644 index 00000000000..b7998be0e8d --- /dev/null +++ b/include/cutils/open_memstream.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_OPEN_MEMSTREAM_H__ +#define __CUTILS_OPEN_MEMSTREAM_H__ + +#include + +#ifndef HAVE_OPEN_MEMSTREAM + +#ifdef __cplusplus +extern "C" { +#endif + +FILE* open_memstream(char** bufp, size_t* sizep); + +#ifdef __cplusplus +} +#endif + +#endif /*!HAVE_OPEN_MEMSTREAM*/ + +#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/ diff --git a/include/cutils/partition_utils.h b/include/cutils/partition_utils.h new file mode 100644 index 00000000000..597df925e9e --- /dev/null +++ b/include/cutils/partition_utils.h @@ -0,0 +1,27 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_PARTITION_WIPED_H__ +#define __CUTILS_PARTITION_WIPED_H__ + +__BEGIN_DECLS + +int partition_wiped(char *source); +void erase_footer(const char *dev_path, long long size); + +__END_DECLS + +#endif /* __CUTILS_PARTITION_WIPED_H__ */ diff --git a/include/cutils/properties.h b/include/cutils/properties.h index 25fd67ae0e4..8589afcb091 100644 --- a/include/cutils/properties.h +++ b/include/cutils/properties.h @@ -43,7 +43,13 @@ int property_get(const char *key, char *value, const char *default_value); /* property_set: returns 0 on success, < 0 on failure */ int property_set(const char *key, const char *value); - + +/* property_set_sync: returns 0 on success, < 0 on failure +** +** symbol needed for Motorola RILs. Calls property_set +*/ +int property_set_sync(const char *key, const char *value); + int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie); diff --git a/include/cutils/qtaguid.h b/include/cutils/qtaguid.h new file mode 100644 index 00000000000..f8550fda83b --- /dev/null +++ b/include/cutils/qtaguid.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_QTAGUID_H +#define __CUTILS_QTAGUID_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Set tags (and owning UIDs) for network sockets. +*/ +extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid); + +/* + * Untag a network socket before closing. +*/ +extern int qtaguid_untagSocket(int sockfd); + +/* + * For the given uid, switch counter sets. + * The kernel only keeps a limited number of sets. + * 2 for now. + */ +extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid); + +/* + * Delete all tag info that relates to the given tag an uid. + * If the tag is 0, then ALL info about the uid is freeded. + * The delete data also affects active tagged socketd, which are + * then untagged. + * The calling process can only operate on its own tags. + * Unless it is part of the happy AID_NET_BW_ACCT group. + * In which case it can clobber everything. + */ +extern int qtaguid_deleteTagData(int tag, uid_t uid); + +/* + * Enable/disable qtaguid functionnality at a lower level. + * When pacified, the kernel will accept commands but do nothing. + */ +extern int qtaguid_setPacifier(int on); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_QTAG_UID_H */ diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h index aa8682ede3b..19cae0c3b0a 100644 --- a/include/cutils/sockets.h +++ b/include/cutils/sockets.h @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef HAVE_WINSOCK #include @@ -92,7 +93,18 @@ extern int socket_local_client_connect(int fd, const char *name, int namespaceId, int type); extern int socket_local_client(const char *name, int namespaceId, int type); extern int socket_inaddr_any_server(int port, int type); - + +/* + * socket_peer_is_trusted - Takes a socket which is presumed to be a + * connected local socket (e.g. AF_LOCAL) and returns whether the peer + * (the userid that owns the process on the other end of that socket) + * is one of the two trusted userids, root or shell. + * + * Note: This only works as advertised on the Android OS and always + * just returns true when called on other operating systems. + */ +extern bool socket_peer_is_trusted(int fd); + #ifdef __cplusplus } #endif diff --git a/include/cutils/str_parms.h b/include/cutils/str_parms.h new file mode 100644 index 00000000000..247c996f5ab --- /dev/null +++ b/include/cutils/str_parms.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_STR_PARMS_H +#define __CUTILS_STR_PARMS_H + +#include + +struct str_parms; + +struct str_parms *str_parms_create(void); +struct str_parms *str_parms_create_str(const char *_string); +void str_parms_destroy(struct str_parms *str_parms); + +void str_parms_del(struct str_parms *str_parms, const char *key); + +int str_parms_add_str(struct str_parms *str_parms, const char *key, + const char *value); +int str_parms_add_int(struct str_parms *str_parms, const char *key, int value); + +int str_parms_add_float(struct str_parms *str_parms, const char *key, + float value); + +int str_parms_get_str(struct str_parms *str_parms, const char *key, + char *out_val, int len); +int str_parms_get_int(struct str_parms *str_parms, const char *key, + int *out_val); +int str_parms_get_float(struct str_parms *str_parms, const char *key, + float *out_val); + +char *str_parms_to_str(struct str_parms *str_parms); + +/* debug */ +void str_parms_dump(struct str_parms *str_parms); + +#endif /* __CUTILS_STR_PARMS_H */ diff --git a/include/cutils/adb_networking.h b/include/cutils/uevent.h old mode 100755 new mode 100644 similarity index 65% rename from include/cutils/adb_networking.h rename to include/cutils/uevent.h index 409d577ecd0..4ebc300c887 --- a/include/cutils/adb_networking.h +++ b/include/cutils/uevent.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,21 @@ * limitations under the License. */ -#ifndef _ADB_NETWORKING_H -#define _ADB_NETWORKING_H 1 -#include -#include +#ifndef __CUTILS_UEVENT_H +#define __CUTILS_UEVENT_H + +#include #include #ifdef __cplusplus extern "C" { #endif -extern int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address); -extern int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr); +int uevent_open_socket(int buf_sz, bool passcred); +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length); #ifdef __cplusplus } #endif -#endif /*_ADB_NETWORKING_H*/ - +#endif /* __CUTILS_UEVENT_H */ diff --git a/include/diskconfig/diskconfig.h b/include/diskconfig/diskconfig.h new file mode 100644 index 00000000000..d4f468cdea6 --- /dev/null +++ b/include/diskconfig/diskconfig.h @@ -0,0 +1,129 @@ +/* system/core/include/diskconfig/diskconfig.h + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LIBS_DISKCONFIG_H +#define __LIBS_DISKCONFIG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_NAME_LEN 512 +#define MAX_NUM_PARTS 16 + +/* known partition schemes */ +#define PART_SCHEME_MBR 0x1 +#define PART_SCHEME_GPT 0x2 + +/* PC Bios partition status */ +#define PC_PART_ACTIVE 0x80 +#define PC_PART_NORMAL 0x0 + +/* Known (rather, used by us) partition types */ +#define PC_PART_TYPE_LINUX 0x83 +#define PC_PART_TYPE_EXTENDED 0x05 +#define PC_PART_TYPE_FAT32 0x0c + +#define PC_NUM_BOOT_RECORD_PARTS 4 + +#define PC_EBR_LOGICAL_PART 0 +#define PC_EBR_NEXT_PTR_PART 1 + +#define PC_BIOS_BOOT_SIG 0xAA55 + +#define PC_MBR_DISK_OFFSET 0 +#define PC_MBR_SIZE 512 + +#define PART_ACTIVE_FLAG 0x1 + +struct chs { + uint8_t head; + uint8_t sector; + uint8_t cylinder; +} __attribute__((__packed__)); + +/* 16 byte pc partition descriptor that sits in MBR and EPBR. + * Note: multi-byte entities have little-endian layout on disk */ +struct pc_partition { + uint8_t status; /* byte 0 */ + struct chs start; /* bytes 1-3 */ + uint8_t type; /* byte 4 */ + struct chs end; /* bytes 5-7 */ + uint32_t start_lba; /* bytes 8-11 */ + uint32_t len_lba; /* bytes 12-15 */ +} __attribute__((__packed__)); + +struct pc_boot_record { + uint8_t code[440]; /* bytes 0-439 */ + uint32_t disk_sig; /* bytes 440-443 */ + uint16_t pad; /* bytes 444-445 */ + struct pc_partition ptable[PC_NUM_BOOT_RECORD_PARTS]; /* bytes 446-509 */ + uint16_t mbr_sig; /* bytes 510-511 */ +} __attribute__((__packed__)); + +struct part_info { + char *name; + uint8_t flags; + uint8_t type; + uint32_t len_kb; /* in 1K-bytes */ + uint32_t start_lba; /* the LBA where this partition begins */ +}; + +struct disk_info { + char *device; + uint8_t scheme; + int sect_size; /* expected sector size in bytes. MUST BE POWER OF 2 */ + uint32_t skip_lba; /* in sectors (1 unit of LBA) */ + uint32_t num_lba; /* the size of the disk in LBA units */ + struct part_info *part_lst; + int num_parts; +}; + +struct write_list { + struct write_list *next; + loff_t offset; + uint32_t len; + uint8_t data[0]; +}; + + +struct write_list *alloc_wl(uint32_t data_len); +void free_wl(struct write_list *item); +struct write_list *wlist_add(struct write_list **lst, struct write_list *item); +void wlist_free(struct write_list *lst); +int wlist_commit(int fd, struct write_list *lst, int test); + +struct disk_info *load_diskconfig(const char *fn, char *path_override); +int dump_disk_config(struct disk_info *dinfo); +int apply_disk_config(struct disk_info *dinfo, int test); +char *find_part_device(struct disk_info *dinfo, const char *name); +int process_disk_config(struct disk_info *dinfo); +struct part_info *find_part(struct disk_info *dinfo, const char *name); + +int write_raw_image(const char *dst, const char *src, loff_t offset, int test); + +/* For MBR partition schemes */ +struct write_list *config_mbr(struct disk_info *dinfo); +char *find_mbr_part(struct disk_info *dinfo, const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBS_DISKCONFIG_H */ diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h new file mode 100644 index 00000000000..bd16240b57a --- /dev/null +++ b/include/netutils/dhcp.h @@ -0,0 +1,40 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NETUTILS_DHCP_H_ +#define _NETUTILS_DHCP_H_ + +#include +#include + +__BEGIN_DECLS + +extern int do_dhcp(char *iname); +extern int dhcp_do_request(const char *ifname, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns1, + char *dns2, + char *server, + uint32_t *lease); +extern int dhcp_stop(const char *ifname); +extern int dhcp_release_lease(const char *ifname); +extern char *dhcp_get_errmsg(); + +__END_DECLS + +#endif /* _NETUTILS_DHCP_H_ */ diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h new file mode 100644 index 00000000000..67a4a45648f --- /dev/null +++ b/include/netutils/ifc.h @@ -0,0 +1,71 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NETUTILS_IFC_H_ +#define _NETUTILS_IFC_H_ + +#include +#include + +__BEGIN_DECLS + +extern int ifc_init(void); +extern void ifc_close(void); + +extern int ifc_get_ifindex(const char *name, int *if_indexp); +extern int ifc_get_hwaddr(const char *name, void *ptr); + +extern int ifc_up(const char *name); +extern int ifc_down(const char *name); + +extern int ifc_enable(const char *ifname); +extern int ifc_disable(const char *ifname); + +extern int ifc_reset_connections(const char *ifname, const int reset_mask); + +extern int ifc_get_addr(const char *name, in_addr_t *addr); +extern int ifc_set_addr(const char *name, in_addr_t addr); +extern int ifc_add_address(const char *name, const char *address, + int prefixlen); +extern int ifc_del_address(const char *name, const char *address, + int prefixlen); +extern int ifc_set_prefixLength(const char *name, int prefixLength); +extern int ifc_set_hwaddr(const char *name, const void *ptr); +extern int ifc_clear_addresses(const char *name); + +/* This function is deprecated. Use ifc_add_route instead. */ +extern int ifc_add_host_route(const char *name, in_addr_t addr); +extern int ifc_remove_host_routes(const char *name); +extern int ifc_get_default_route(const char *ifname); +/* This function is deprecated. Use ifc_add_route instead */ +extern int ifc_set_default_route(const char *ifname, in_addr_t gateway); +/* This function is deprecated. Use ifc_add_route instead */ +extern int ifc_create_default_route(const char *name, in_addr_t addr); +extern int ifc_remove_default_route(const char *ifname); +extern int ifc_add_route(const char *name, const char *addr, int prefix_length, + const char *gw); +extern int ifc_remove_route(const char *ifname, const char *dst, + int prefix_length, const char *gw); +extern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, + unsigned *flags); + +extern int ifc_configure(const char *ifname, in_addr_t address, + uint32_t prefixLength, in_addr_t gateway, + in_addr_t dns1, in_addr_t dns2); + +__END_DECLS + +#endif /* _NETUTILS_IFC_H_ */ diff --git a/include/pixelflinger/format.h b/include/pixelflinger/format.h index 6b2050c447a..82eeca4d726 100644 --- a/include/pixelflinger/format.h +++ b/include/pixelflinger/format.h @@ -39,19 +39,25 @@ enum GGLPixelFormat { GGL_PIXEL_FORMAT_LA_88 = 0xA, // 16-bit LA GGL_PIXEL_FORMAT_RGB_332 = 0xB, // 8-bit RGB (non paletted) - // YCbCr formats (SP=semi-planar, P=planar) - GGL_PIXEL_FORMAT_YCbCr_422_SP= 0x10, - GGL_PIXEL_FORMAT_YCbCr_420_SP= 0x11, - GGL_PIXEL_FORMAT_YCbCr_422_P = 0x12, - GGL_PIXEL_FORMAT_YCbCr_420_P = 0x13, - GGL_PIXEL_FORMAT_YCbCr_422_I = 0x14, - GGL_PIXEL_FORMAT_YCbCr_420_I = 0x15, + // reserved range. don't use. + GGL_PIXEL_FORMAT_RESERVED_10 = 0x10, + GGL_PIXEL_FORMAT_RESERVED_11 = 0x11, + GGL_PIXEL_FORMAT_RESERVED_12 = 0x12, + GGL_PIXEL_FORMAT_RESERVED_13 = 0x13, + GGL_PIXEL_FORMAT_RESERVED_14 = 0x14, + GGL_PIXEL_FORMAT_RESERVED_15 = 0x15, + GGL_PIXEL_FORMAT_RESERVED_16 = 0x16, + GGL_PIXEL_FORMAT_RESERVED_17 = 0x17, // reserved/special formats GGL_PIXEL_FORMAT_Z_16 = 0x18, GGL_PIXEL_FORMAT_S_8 = 0x19, GGL_PIXEL_FORMAT_SZ_24 = 0x1A, GGL_PIXEL_FORMAT_SZ_8 = 0x1B, + + // reserved range. don't use. + GGL_PIXEL_FORMAT_RESERVED_20 = 0x20, + GGL_PIXEL_FORMAT_RESERVED_21 = 0x21, }; enum GGLFormatComponents { @@ -62,10 +68,6 @@ enum GGLFormatComponents { GGL_RGBA = 0x1908, GGL_LUMINANCE = 0x1909, GGL_LUMINANCE_ALPHA = 0x190A, - GGL_Y_CB_CR_SP = 0x8000, - GGL_Y_CB_CR = GGL_Y_CB_CR_SP, - GGL_Y_CB_CR_P = 0x8001, - GGL_Y_CB_CR_I = 0x8002, }; enum GGLFormatComponentIndex { diff --git a/include/pixelflinger/pixelflinger.h b/include/pixelflinger/pixelflinger.h index dca0b907394..8a2b4421b3d 100644 --- a/include/pixelflinger/pixelflinger.h +++ b/include/pixelflinger/pixelflinger.h @@ -315,7 +315,7 @@ extern "C" { ssize_t gglInit(GGLContext** context); ssize_t gglUninit(GGLContext* context); -GGLint gglBitBlti( +GGLint gglBitBlit( GGLContext* c, int tmu, GGLint crop[4], diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 24b7c819abf..ebb9f48995e 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -51,6 +51,16 @@ #define AID_SDCARD_RW 1015 /* external storage write access */ #define AID_VPN 1016 /* vpn system */ #define AID_KEYSTORE 1017 /* keystore subsystem */ +#define AID_USB 1018 /* USB devices */ +#define AID_DRM 1019 /* DRM server */ +#define AID_AVAILABLE 1020 /* available for use */ +#define AID_GPS 1021 /* GPS daemon */ +#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ +#define AID_MEDIA_RW 1023 /* internal media storage write access */ +#define AID_MTP 1024 /* MTP USB driver access */ +#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ +#define AID_DRMRPC 1026 /* group for drm rpc */ +#define AID_NFC 1027 /* nfc subsystem */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ @@ -63,6 +73,25 @@ #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ #define AID_NET_RAW 3004 /* can create raw INET sockets */ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ +#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ +#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ +#define AID_QCOM_ONCRPC 3008 /* can read/write /dev/oncrpc files */ + +#if defined(MOTOROLA_UIDS) +#define AID_MOT_OSH 5000 /* OSH */ +#define AID_MOT_ACCY 9000 /* access to accessory */ +#define AID_MOT_PWRIC 9001 /* power IC */ +#define AID_MOT_USB 9002 /* mot usb */ +#define AID_MOT_DRM 9003 /* can access DRM resource. */ +#define AID_MOT_TCMD 9004 /* mot_tcmd */ +#define AID_MOT_SEC_RTC 9005 /* mot cpcap rtc */ +#define AID_MOT_TOMBSTONE 9006 +#define AID_MOT_TPAPI 9007 /* mot_tpapi */ +#define AID_MOT_SECCLKD 9008 /* mot_secclkd */ +#define AID_MOT_WHISPER 9009 /* Whisper Protocol access */ +#define AID_MOT_CAIF 9010 /* can create CAIF sockets */ +#define AID_MOT_DLNA 9011 /* DLNA native */ +#endif // MOTOROLA_UIDS #define AID_MISC 9998 /* access to misc storage */ #define AID_NOBODY 9999 @@ -75,7 +104,7 @@ struct android_id_info { unsigned aid; }; -static struct android_id_info android_ids[] = { +static const struct android_id_info android_ids[] = { { "root", AID_ROOT, }, { "system", AID_SYSTEM, }, { "radio", AID_RADIO, }, @@ -92,17 +121,43 @@ static struct android_id_info android_ids[] = { { "adb", AID_ADB, }, { "install", AID_INSTALL, }, { "media", AID_MEDIA, }, + { "drm", AID_DRM, }, + { "available", AID_AVAILABLE, }, + { "nfc", AID_NFC, }, + { "drmrpc", AID_DRMRPC, }, { "shell", AID_SHELL, }, { "cache", AID_CACHE, }, { "diag", AID_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, }, { "net_bt", AID_NET_BT, }, { "sdcard_rw", AID_SDCARD_RW, }, + { "media_rw", AID_MEDIA_RW, }, { "vpn", AID_VPN, }, { "keystore", AID_KEYSTORE, }, + { "usb", AID_USB, }, + { "mtp", AID_MTP, }, + { "gps", AID_GPS, }, { "inet", AID_INET, }, { "net_raw", AID_NET_RAW, }, { "net_admin", AID_NET_ADMIN, }, + { "net_bw_stats", AID_NET_BW_STATS, }, + { "net_bw_acct", AID_NET_BW_ACCT, }, + { "qcom_oncrpc", AID_QCOM_ONCRPC, }, +#if defined(MOTOROLA_UIDS) + { "mot_osh", AID_MOT_OSH, }, + { "mot_accy", AID_MOT_ACCY, }, + { "mot_pwric", AID_MOT_PWRIC, }, + { "mot_usb", AID_MOT_USB, }, + { "mot_drm", AID_MOT_DRM, }, + { "mot_tcmd", AID_MOT_TCMD, }, + { "mot_sec_rtc", AID_MOT_SEC_RTC, }, + { "mot_tombstone", AID_MOT_TOMBSTONE, }, + { "mot_tpapi", AID_MOT_TPAPI, }, + { "mot_secclkd", AID_MOT_SECCLKD, }, + { "mot_whisper", AID_MOT_WHISPER, }, + { "mot_caif", AID_MOT_CAIF, }, + { "mot_dlna", AID_MOT_DLNA, }, +#endif { "misc", AID_MISC, }, { "nobody", AID_NOBODY, }, }; @@ -133,9 +188,13 @@ static struct fs_path_config android_dirs[] = { { 00771, AID_SHELL, AID_SHELL, "data/local" }, { 01771, AID_SYSTEM, AID_MISC, "data/misc" }, { 00770, AID_DHCP, AID_DHCP, "data/misc/dhcp" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, "data/media" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, "data/media/Music" }, { 00771, AID_SYSTEM, AID_SYSTEM, "data" }, { 00750, AID_ROOT, AID_SHELL, "sbin" }, + { 00755, AID_ROOT, AID_ROOT, "system/addon.d" }, { 00755, AID_ROOT, AID_SHELL, "system/bin" }, + { 00755, AID_ROOT, AID_SHELL, "system/vendor" }, { 00755, AID_ROOT, AID_SHELL, "system/xbin" }, { 00755, AID_ROOT, AID_ROOT, "system/etc/ppp" }, { 00777, AID_ROOT, AID_ROOT, "sdcard" }, @@ -156,30 +215,45 @@ static struct fs_path_config android_files[] = { { 00550, AID_ROOT, AID_SHELL, "system/etc/init.testmenu" }, { 00550, AID_DHCP, AID_SHELL, "system/etc/dhcpcd/dhcpcd-run-hooks" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/main.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/input.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/audio.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/main.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/input.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/audio.conf" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/network.conf" }, + { 00444, AID_NET_BT, AID_NET_BT, "system/etc/bluetooth/blacklist.conf" }, + { 00640, AID_SYSTEM, AID_SYSTEM, "system/etc/bluetooth/auto_pairing.conf" }, { 00444, AID_RADIO, AID_AUDIO, "system/etc/AudioPara4.csv" }, { 00555, AID_ROOT, AID_ROOT, "system/etc/ppp/*" }, + { 00555, AID_ROOT, AID_ROOT, "system/etc/rc.*" }, + { 00755, AID_ROOT, AID_ROOT, "system/addon.d/*" }, { 00644, AID_SYSTEM, AID_SYSTEM, "data/app/*" }, + { 00644, AID_MEDIA_RW, AID_MEDIA_RW, "data/media/*" }, { 00644, AID_SYSTEM, AID_SYSTEM, "data/app-private/*" }, { 00644, AID_APP, AID_APP, "data/data/*" }, /* the following two files are INTENTIONALLY set-gid and not set-uid. * Do not change. */ { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, - { 02755, AID_ROOT, AID_INET, "system/bin/netcfg" }, - /* the following four files are INTENTIONALLY set-uid, but they + { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" }, + /* the following five files are INTENTIONALLY set-uid, but they * are NOT included on user builds. */ { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/procrank" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" }, + { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" }, + /* the following file is INTENTIONALLY set-uid, and IS included + * in user builds. */ + { 06750, AID_ROOT, AID_SHELL, "system/bin/run-as" }, + { 06750, AID_ROOT, AID_SYSTEM, "system/bin/rebootcmd" }, { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, + { 00755, AID_ROOT, AID_ROOT, "system/lib/valgrind/*" }, { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, + { 00755, AID_ROOT, AID_SHELL, "system/vendor/bin/*" }, { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, { 00755, AID_ROOT, AID_ROOT, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, "init*" }, + { 00750, AID_ROOT, AID_SHELL, "charger*" }, + { 00755, AID_ROOT, AID_SHELL, "system/etc/init.d/*" }, { 00644, AID_ROOT, AID_ROOT, 0 }, }; diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h index 8a36fa9ee23..2d7fdcf5f1d 100644 --- a/include/private/pixelflinger/ggl_context.h +++ b/include/private/pixelflinger/ggl_context.h @@ -285,8 +285,7 @@ struct clear_state_t { }; struct fog_state_t { - uint8_t color[3]; - uint8_t reserved; + uint8_t color[4]; }; struct logic_op_state_t { diff --git a/include/system/audio.h b/include/system/audio.h new file mode 100644 index 00000000000..add6fe43972 --- /dev/null +++ b/include/system/audio.h @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_CORE_H +#define ANDROID_AUDIO_CORE_H + +#include +#include +#include +#include + +#include + +__BEGIN_DECLS + +/* The enums were moved here mostly from + * frameworks/base/include/media/AudioSystem.h + */ + +typedef int audio_io_handle_t; + +/* Audio stream types */ +typedef enum { + AUDIO_STREAM_DEFAULT = -1, + AUDIO_STREAM_VOICE_CALL = 0, + AUDIO_STREAM_SYSTEM = 1, + AUDIO_STREAM_RING = 2, + AUDIO_STREAM_MUSIC = 3, + AUDIO_STREAM_ALARM = 4, + AUDIO_STREAM_NOTIFICATION = 5, + AUDIO_STREAM_BLUETOOTH_SCO = 6, + AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */ + AUDIO_STREAM_DTMF = 8, + AUDIO_STREAM_TTS = 9, +#ifdef QCOM_HARDWARE + AUDIO_STREAM_FM = 10, +#endif + AUDIO_STREAM_CNT, + AUDIO_STREAM_MAX = AUDIO_STREAM_CNT - 1, +} audio_stream_type_t; + +/* Do not change these values without updating their counterparts + * in media/java/android/media/MediaRecorder.java! + */ +typedef enum { + AUDIO_SOURCE_DEFAULT = 0, + AUDIO_SOURCE_MIC = 1, + AUDIO_SOURCE_VOICE_UPLINK = 2, + AUDIO_SOURCE_VOICE_DOWNLINK = 3, + AUDIO_SOURCE_VOICE_CALL = 4, + AUDIO_SOURCE_CAMCORDER = 5, + AUDIO_SOURCE_VOICE_RECOGNITION = 6, + AUDIO_SOURCE_VOICE_COMMUNICATION = 7, +#ifdef QCOM_HARDWARE + AUDIO_SOURCE_FM_RX = 8, + AUDIO_SOURCE_FM_RX_A2DP = 9, +#endif + AUDIO_SOURCE_CNT, + AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, +} audio_source_t; + +/* special audio session values + * (XXX: should this be living in the audio effects land?) + */ +typedef enum { + /* session for effects attached to a particular output stream + * (value must be less than 0) + */ + AUDIO_SESSION_OUTPUT_STAGE = -1, + + /* session for effects applied to output mix. These effects can + * be moved by audio policy manager to another output stream + * (value must be 0) + */ + AUDIO_SESSION_OUTPUT_MIX = 0, +} audio_session_t; + +/* Audio sub formats (see enum audio_format). */ + +/* PCM sub formats */ +typedef enum { + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ + AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ + AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ + AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ +} audio_format_pcm_sub_fmt_t; + +/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 + * frame header to specify bit rate, stereo mode, version... + */ +typedef enum { + AUDIO_FORMAT_MP3_SUB_NONE = 0x0, +} audio_format_mp3_sub_fmt_t; + +/* AMR NB/WB sub format field definition: specify frame block interleaving, + * bandwidth efficient or octet aligned, encoding mode for recording... + */ +typedef enum { + AUDIO_FORMAT_AMR_SUB_NONE = 0x0, +} audio_format_amr_sub_fmt_t; + +/* AAC sub format field definition: specify profile or bitrate for recording... */ +typedef enum { + AUDIO_FORMAT_AAC_SUB_NONE = 0x0, +} audio_format_aac_sub_fmt_t; + +/* VORBIS sub format field definition: specify quality for recording... */ +typedef enum { + AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, +} audio_format_vorbis_sub_fmt_t; + +/* Audio format consists in a main format field (upper 8 bits) and a sub format + * field (lower 24 bits). + * + * The main format indicates the main codec type. The sub format field + * indicates options and parameters for each format. The sub format is mainly + * used for record to indicate for instance the requested bitrate or profile. + * It can also be used for certain formats to give informations not present in + * the encoded audio stream (e.g. octet alignement for AMR). + */ +typedef enum { + AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, + AUDIO_FORMAT_DEFAULT = 0, + AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ + AUDIO_FORMAT_MP3 = 0x01000000UL, + AUDIO_FORMAT_AMR_NB = 0x02000000UL, + AUDIO_FORMAT_AMR_WB = 0x03000000UL, + AUDIO_FORMAT_AAC = 0x04000000UL, + AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, + AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, + AUDIO_FORMAT_VORBIS = 0x07000000UL, +#ifdef QCOM_HARDWARE + AUDIO_FORMAT_QCELP = 0x08000000UL, + AUDIO_FORMAT_EVRC = 0x09000000UL, +#endif + AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, + AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, + + /* Aliases */ + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_16_BIT), + AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_8_BIT), + AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_32_BIT), + AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_8_24_BIT), +} audio_format_t; + +typedef enum { + /* output channels */ + AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, + AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, + AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, + AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, + AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, + AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, + AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, + AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, + AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, + + AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, + AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT), + AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT), + AUDIO_CHANNEL_OUT_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_BACK_CENTER), + AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT), + // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 + AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT | + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), + AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | + AUDIO_CHANNEL_OUT_BACK_CENTER| + AUDIO_CHANNEL_OUT_SIDE_LEFT| + AUDIO_CHANNEL_OUT_SIDE_RIGHT| + AUDIO_CHANNEL_OUT_TOP_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + + /* input channels */ + AUDIO_CHANNEL_IN_LEFT = 0x4, + AUDIO_CHANNEL_IN_RIGHT = 0x8, + AUDIO_CHANNEL_IN_FRONT = 0x10, + AUDIO_CHANNEL_IN_BACK = 0x20, + AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, + AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, + AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, + AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, + AUDIO_CHANNEL_IN_PRESSURE = 0x400, + AUDIO_CHANNEL_IN_X_AXIS = 0x800, + AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, + AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, + AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, + AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, + + AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, + AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), + AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | + AUDIO_CHANNEL_IN_RIGHT | + AUDIO_CHANNEL_IN_FRONT | + AUDIO_CHANNEL_IN_BACK| + AUDIO_CHANNEL_IN_LEFT_PROCESSED | + AUDIO_CHANNEL_IN_RIGHT_PROCESSED | + AUDIO_CHANNEL_IN_FRONT_PROCESSED | + AUDIO_CHANNEL_IN_BACK_PROCESSED| + AUDIO_CHANNEL_IN_PRESSURE | + AUDIO_CHANNEL_IN_X_AXIS | + AUDIO_CHANNEL_IN_Y_AXIS | + AUDIO_CHANNEL_IN_Z_AXIS | + AUDIO_CHANNEL_IN_VOICE_UPLINK | + AUDIO_CHANNEL_IN_VOICE_DNLINK), +} audio_channels_t; + +typedef enum { + AUDIO_MODE_INVALID = -2, + AUDIO_MODE_CURRENT = -1, + AUDIO_MODE_NORMAL = 0, + AUDIO_MODE_RINGTONE = 1, + AUDIO_MODE_IN_CALL = 2, + AUDIO_MODE_IN_COMMUNICATION = 3, + + AUDIO_MODE_CNT, + AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, +} audio_mode_t; + +typedef enum { + AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, + AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, + AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, + AUDIO_IN_ACOUSTICS_NS_DISABLE = 0, + AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004, + AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, +} audio_in_acoustics_t; + +typedef enum { + /* output devices */ + AUDIO_DEVICE_OUT_EARPIECE = 0x1, + AUDIO_DEVICE_OUT_SPEAKER = 0x2, + AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, + AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, + AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + AUDIO_DEVICE_OUT_FM = 0x2000, +#if defined(QCOM_HARDWARE) && !defined(USES_AUDIO_LEGACY) + AUDIO_DEVICE_OUT_ANC_HEADSET = 0x4000, + AUDIO_DEVICE_OUT_ANC_HEADPHONE = 0x8000, + AUDIO_DEVICE_OUT_FM_TX = 0x10000, + AUDIO_DEVICE_OUT_DIRECTOUTPUT = 0x20000, + AUDIO_DEVICE_OUT_PROXY = 0x40000, + AUDIO_DEVICE_OUT_DEFAULT = 0x80000, +#else + AUDIO_DEVICE_OUT_DEFAULT = 0x8000, +#endif + AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | + AUDIO_DEVICE_OUT_SPEAKER | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | + AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | +#if defined(QCOM_HARDWARE) && !defined(USES_AUDIO_LEGACY) + AUDIO_DEVICE_OUT_FM | + AUDIO_DEVICE_OUT_ANC_HEADSET | + AUDIO_DEVICE_OUT_ANC_HEADPHONE | + AUDIO_DEVICE_OUT_FM_TX | + AUDIO_DEVICE_OUT_DIRECTOUTPUT | + AUDIO_DEVICE_OUT_PROXY | +#endif + AUDIO_DEVICE_OUT_DEFAULT), + AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + + /* input devices */ +#if defined(QCOM_HARDWARE) && !defined(USES_AUDIO_LEGACY) + AUDIO_DEVICE_IN_COMMUNICATION = 0x100000, + AUDIO_DEVICE_IN_AMBIENT = 0x200000, + AUDIO_DEVICE_IN_BUILTIN_MIC = 0x400000, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x800000, + AUDIO_DEVICE_IN_WIRED_HEADSET = 0x1000000, + AUDIO_DEVICE_IN_AUX_DIGITAL = 0x2000000, + AUDIO_DEVICE_IN_VOICE_CALL = 0x4000000, + AUDIO_DEVICE_IN_BACK_MIC = 0x8000000, + AUDIO_DEVICE_IN_ANC_HEADSET = 0x10000000, + AUDIO_DEVICE_IN_FM_RX = 0x20000000, + AUDIO_DEVICE_IN_FM_RX_A2DP = 0x40000000, +#else + AUDIO_DEVICE_IN_COMMUNICATION = 0x10000, + AUDIO_DEVICE_IN_AMBIENT = 0x20000, + AUDIO_DEVICE_IN_BUILTIN_MIC = 0x40000, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000, + AUDIO_DEVICE_IN_WIRED_HEADSET = 0x100000, + AUDIO_DEVICE_IN_AUX_DIGITAL = 0x200000, + AUDIO_DEVICE_IN_VOICE_CALL = 0x400000, + AUDIO_DEVICE_IN_BACK_MIC = 0x800000, +#endif + AUDIO_DEVICE_IN_DEFAULT = 0x80000000, + + AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | + AUDIO_DEVICE_IN_AMBIENT | + AUDIO_DEVICE_IN_BUILTIN_MIC | + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_IN_WIRED_HEADSET | + AUDIO_DEVICE_IN_AUX_DIGITAL | + AUDIO_DEVICE_IN_VOICE_CALL | + AUDIO_DEVICE_IN_BACK_MIC | +#if defined(QCOM_HARDWARE) && !defined(USES_AUDIO_LEGACY) + AUDIO_DEVICE_IN_ANC_HEADSET | + AUDIO_DEVICE_IN_FM_RX | + AUDIO_DEVICE_IN_FM_RX_A2DP | +#endif + AUDIO_DEVICE_IN_DEFAULT), + AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, +} audio_devices_t; + +static inline bool audio_is_output_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) + return true; + else + return false; +} + +static inline bool audio_is_input_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) + return true; + else + return false; +} + +static inline bool audio_is_a2dp_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) + return true; + else + return false; +} + +static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) + return true; + else + return false; +} + +static inline bool audio_is_input_channel(uint32_t channel) +{ + if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) + return true; + else + return false; +} + +static inline bool audio_is_output_channel(uint32_t channel) +{ + if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0) + return true; + else + return false; +} + +static inline bool audio_is_valid_format(uint32_t format) +{ + switch (format & AUDIO_FORMAT_MAIN_MASK) { + case AUDIO_FORMAT_PCM: + if (format != AUDIO_FORMAT_PCM_16_BIT && + format != AUDIO_FORMAT_PCM_8_BIT) { + return false; + } + case AUDIO_FORMAT_MP3: + case AUDIO_FORMAT_AMR_NB: + case AUDIO_FORMAT_AMR_WB: + case AUDIO_FORMAT_AAC: + case AUDIO_FORMAT_HE_AAC_V1: + case AUDIO_FORMAT_HE_AAC_V2: + case AUDIO_FORMAT_VORBIS: +#ifdef QCOM_HARDWARE + case AUDIO_FORMAT_QCELP: + case AUDIO_FORMAT_EVRC: +#endif + return true; + default: + return false; + } +} + +static inline bool audio_is_linear_pcm(uint32_t format) +{ + return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); +} + +static inline size_t audio_bytes_per_sample(uint32_t format) +{ + size_t size = 0; + + switch (format) { + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + size = sizeof(int32_t); + break; + case AUDIO_FORMAT_PCM_16_BIT: + size = sizeof(int16_t); + break; + case AUDIO_FORMAT_PCM_8_BIT: + size = sizeof(uint8_t); + break; + default: + break; + } + return size; +} + +__END_DECLS + +#endif // ANDROID_AUDIO_CORE_H diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h new file mode 100644 index 00000000000..1e0af7d5919 --- /dev/null +++ b/include/system/audio_policy.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_POLICY_CORE_H +#define ANDROID_AUDIO_POLICY_CORE_H + +#include +#include +#include + +#include + +__BEGIN_DECLS + +/* The enums were moved here mostly from + * frameworks/base/include/media/AudioSystem.h + */ + +/* request to open a direct output with get_output() (by opposition to + * sharing an output with other AudioTracks) + */ +typedef enum { + AUDIO_POLICY_OUTPUT_FLAG_INDIRECT = 0x0, + AUDIO_POLICY_OUTPUT_FLAG_DIRECT = 0x1 +} audio_policy_output_flags_t; + +/* device categories used for audio_policy->set_force_use() */ +typedef enum { + AUDIO_POLICY_FORCE_NONE, + AUDIO_POLICY_FORCE_SPEAKER, + AUDIO_POLICY_FORCE_HEADPHONES, + AUDIO_POLICY_FORCE_BT_SCO, + AUDIO_POLICY_FORCE_BT_A2DP, + AUDIO_POLICY_FORCE_WIRED_ACCESSORY, + AUDIO_POLICY_FORCE_BT_CAR_DOCK, + AUDIO_POLICY_FORCE_BT_DESK_DOCK, + AUDIO_POLICY_FORCE_ANALOG_DOCK, + AUDIO_POLICY_FORCE_DIGITAL_DOCK, + + AUDIO_POLICY_FORCE_CFG_CNT, + AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, + + AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE, +} audio_policy_forced_cfg_t; + +/* usages used for audio_policy->set_force_use() */ +typedef enum { + AUDIO_POLICY_FORCE_FOR_COMMUNICATION, + AUDIO_POLICY_FORCE_FOR_MEDIA, + AUDIO_POLICY_FORCE_FOR_RECORD, + AUDIO_POLICY_FORCE_FOR_DOCK, + + AUDIO_POLICY_FORCE_USE_CNT, + AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, +} audio_policy_force_use_t; + +/* device connection states used for audio_policy->set_device_connection_state() + */ +typedef enum { + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + + AUDIO_POLICY_DEVICE_STATE_CNT, + AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1, +} audio_policy_dev_state_t; + +typedef enum { + /* Used to generate a tone to notify the user of a + * notification/alarm/ringtone while they are in a call. */ + AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION = 0, + + AUDIO_POLICY_TONE_CNT, + AUDIO_POLICY_TONE_MAX = AUDIO_POLICY_TONE_CNT - 1, +} audio_policy_tone_t; + + +static inline bool audio_is_low_visibility(audio_stream_type_t stream) +{ + switch (stream) { + case AUDIO_STREAM_SYSTEM: + case AUDIO_STREAM_NOTIFICATION: + case AUDIO_STREAM_RING: + return true; + default: + return false; + } +} + + +__END_DECLS + +#endif // ANDROID_AUDIO_POLICY_CORE_H diff --git a/include/system/camera.h b/include/system/camera.h new file mode 100644 index 00000000000..7a4529a1c76 --- /dev/null +++ b/include/system/camera.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H +#define SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H + +#include +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +/** + * A set of bit masks for specifying how the received preview frames are + * handled before the previewCallback() call. + * + * The least significant 3 bits of an "int" value are used for this purpose: + * + * ..... 0 0 0 + * ^ ^ ^ + * | | |---------> determine whether the callback is enabled or not + * | |-----------> determine whether the callback is one-shot or not + * |-------------> determine whether the frame is copied out or not + * + * WARNING: When a frame is sent directly without copying, it is the frame + * receiver's responsiblity to make sure that the frame data won't get + * corrupted by subsequent preview frames filled by the camera. This flag is + * recommended only when copying out data brings significant performance price + * and the handling/processing of the received frame data is always faster than + * the preview frame rate so that data corruption won't occur. + * + * For instance, + * 1. 0x00 disables the callback. In this case, copy out and one shot bits + * are ignored. + * 2. 0x01 enables a callback without copying out the received frames. A + * typical use case is the Camcorder application to avoid making costly + * frame copies. + * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical + * use case is the Camera application. + * 4. 0x07 is enabling a callback with frame copied out only once. A typical + * use case is the Barcode scanner application. + */ + +enum { + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK = 0x01, + CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK = 0x02, + CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK = 0x04, + /** Typical use cases */ + CAMERA_FRAME_CALLBACK_FLAG_NOOP = 0x00, + CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER = 0x01, + CAMERA_FRAME_CALLBACK_FLAG_CAMERA = 0x05, + CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER = 0x07 +}; + +/** msgType in notifyCallback and dataCallback functions */ +enum { + CAMERA_MSG_ERROR = 0x0001, // notifyCallback + CAMERA_MSG_SHUTTER = 0x0002, // notifyCallback + CAMERA_MSG_FOCUS = 0x0004, // notifyCallback + CAMERA_MSG_ZOOM = 0x0008, // notifyCallback + CAMERA_MSG_PREVIEW_FRAME = 0x0010, // dataCallback + CAMERA_MSG_VIDEO_FRAME = 0x0020, // data_timestamp_callback + CAMERA_MSG_POSTVIEW_FRAME = 0x0040, // dataCallback + CAMERA_MSG_RAW_IMAGE = 0x0080, // dataCallback + CAMERA_MSG_COMPRESSED_IMAGE = 0x0100, // dataCallback + CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x0200, // dataCallback + // Preview frame metadata. This can be combined with + // CAMERA_MSG_PREVIEW_FRAME in dataCallback. For example, the apps can + // request FRAME and METADATA. Or the apps can request only FRAME or only + // METADATA. + CAMERA_MSG_PREVIEW_METADATA = 0x0400, // dataCallback + CAMERA_MSG_STATS_DATA = 0x800, + + CAMERA_MSG_ALL_MSGS = 0xFFFF +}; + +/** cmdType in sendCommand functions */ +enum { + CAMERA_CMD_START_SMOOTH_ZOOM = 1, + CAMERA_CMD_STOP_SMOOTH_ZOOM = 2, + + /** + * Set the clockwise rotation of preview display (setPreviewDisplay) in + * degrees. This affects the preview frames and the picture displayed after + * snapshot. This method is useful for portrait mode applications. Note + * that preview display of front-facing cameras is flipped horizontally + * before the rotation, that is, the image is reflected along the central + * vertical axis of the camera sensor. So the users can see themselves as + * looking into a mirror. + * + * This does not affect the order of byte array of + * CAMERA_MSG_PREVIEW_FRAME, CAMERA_MSG_VIDEO_FRAME, + * CAMERA_MSG_POSTVIEW_FRAME, CAMERA_MSG_RAW_IMAGE, or + * CAMERA_MSG_COMPRESSED_IMAGE. This is allowed to be set during preview + * since API level 14. + */ + CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3, + + /** + * cmdType to disable/enable shutter sound. In sendCommand passing arg1 = + * 0 will disable, while passing arg1 = 1 will enable the shutter sound. + */ + CAMERA_CMD_ENABLE_SHUTTER_SOUND = 4, + + /* cmdType to play recording sound */ + CAMERA_CMD_PLAY_RECORDING_SOUND = 5, + + /** + * Start the face detection. This should be called after preview is started. + * The camera will notify the listener of CAMERA_MSG_FACE and the detected + * faces in the preview frame. The detected faces may be the same as the + * previous ones. Apps should call CAMERA_CMD_STOP_FACE_DETECTION to stop + * the face detection. This method is supported if CameraParameters + * KEY_MAX_NUM_HW_DETECTED_FACES or KEY_MAX_NUM_SW_DETECTED_FACES is + * bigger than 0. Hardware and software face detection should not be running + * at the same time. If the face detection has started, apps should not send + * this again. + * + * In hardware face detection mode, CameraParameters KEY_WHITE_BALANCE, + * KEY_FOCUS_AREAS and KEY_METERING_AREAS have no effect. + * + * arg1 is the face detection type. It can be CAMERA_FACE_DETECTION_HW or + * CAMERA_FACE_DETECTION_SW. + */ + CAMERA_CMD_START_FACE_DETECTION = 6, + + /** + * Stop the face detection. + */ + CAMERA_CMD_STOP_FACE_DETECTION = 7, + + CAMERA_CMD_HISTOGRAM_ON = 8, + CAMERA_CMD_HISTOGRAM_OFF = 9, + CAMERA_CMD_HISTOGRAM_SEND_DATA = 10, + +}; + +/** camera fatal errors */ +enum { + CAMERA_ERROR_UNKNOWN = 1, + CAMERA_ERROR_SERVER_DIED = 100 +}; + +enum { + /** The facing of the camera is opposite to that of the screen. */ + CAMERA_FACING_BACK = 0, + /** The facing of the camera is the same as that of the screen. */ + CAMERA_FACING_FRONT = 1 +}; + +enum { + CAMERA_SUPPORT_MODE_2D = 0x01, /* Camera Sensor supports 2D mode. */ + CAMERA_SUPPORT_MODE_3D = 0x02, /* Camera Sensor supports 3D mode. */ + CAMERA_SUPPORT_MODE_NONZSL = 0x04, /* Camera Sensor in NON-ZSL mode. */ + CAMERA_SUPPORT_MODE_ZSL = 0x08 /* Camera Sensor supports ZSL mode. */ +}; + +enum { + /** Hardware face detection. It does not use much CPU. */ + CAMERA_FACE_DETECTION_HW = 0, + /** + * Software face detection. It uses some CPU. Applications must use + * Camera.setPreviewTexture for preview in this mode. + */ + CAMERA_FACE_DETECTION_SW = 1 +}; + +/** + * The information of a face from camera face detection. + */ +typedef struct camera_face { + /** + * Bounds of the face [left, top, right, bottom]. (-1000, -1000) represents + * the top-left of the camera field of view, and (1000, 1000) represents the + * bottom-right of the field of view. The width and height cannot be 0 or + * negative. This is supported by both hardware and software face detection. + * + * The direction is relative to the sensor orientation, that is, what the + * sensor sees. The direction is not affected by the rotation or mirroring + * of CAMERA_CMD_SET_DISPLAY_ORIENTATION. + */ + int32_t rect[4]; + + /** + * The confidence level of the face. The range is 1 to 100. 100 is the + * highest confidence. This is supported by both hardware and software + * face detection. + */ + int32_t score; + + /** + * An unique id per face while the face is visible to the tracker. If + * the face leaves the field-of-view and comes back, it will get a new + * id. If the value is 0, id is not supported. + */ + int32_t id; + + /** + * The coordinates of the center of the left eye. The range is -1000 to + * 1000. -2000, -2000 if this is not supported. + */ + int32_t left_eye[2]; + + /** + * The coordinates of the center of the right eye. The range is -1000 to + * 1000. -2000, -2000 if this is not supported. + */ + int32_t right_eye[2]; + + /** + * The coordinates of the center of the mouth. The range is -1000 to 1000. + * -2000, -2000 if this is not supported. + */ + int32_t mouth[2]; + +} camera_face_t; + +/** + * The metadata of the frame data. + */ +typedef struct camera_frame_metadata { + /** + * The number of detected faces in the frame. + */ + int32_t number_of_faces; + + /** + * An array of the detected faces. The length is number_of_faces. + */ + camera_face_t *faces; +} camera_frame_metadata_t; + +__END_DECLS + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H */ diff --git a/include/system/graphics.h b/include/system/graphics.h new file mode 100644 index 00000000000..729e92c788e --- /dev/null +++ b/include/system/graphics.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H +#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * If the HAL needs to create service threads to handle graphics related + * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority + * if they can block the main rendering thread in any way. + * + * the priority of the current thread can be set with: + * + * #include + * setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + * + */ + +#define HAL_PRIORITY_URGENT_DISPLAY (-8) + +/** + * pixel format definitions + */ + +enum { + HAL_PIXEL_FORMAT_RGBA_8888 = 1, + HAL_PIXEL_FORMAT_RGBX_8888 = 2, + HAL_PIXEL_FORMAT_RGB_888 = 3, + HAL_PIXEL_FORMAT_RGB_565 = 4, + HAL_PIXEL_FORMAT_BGRA_8888 = 5, + HAL_PIXEL_FORMAT_RGBA_5551 = 6, + HAL_PIXEL_FORMAT_RGBA_4444 = 7, + + /* 0x8 - 0xFF range unavailable */ + + /* + * 0x100 - 0x1FF + * + * This range is reserved for pixel formats that are specific to the HAL + * implementation. Implementations can use any value in this range to + * communicate video pixel formats between their HAL modules. These formats + * must not have an alpha channel. Additionally, an EGLimage created from a + * gralloc buffer of one of these formats must be supported for use with the + * GL_OES_EGL_image_external OpenGL ES extension. + */ + + /* + * Android YUV format: + * + * This format is exposed outside of the HAL to software decoders and + * applications. EGLImageKHR must support it in conjunction with the + * OES_EGL_image_external extension. + * + * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed + * by (W/2) x (H/2) Cr and Cb planes. + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels + * - a vertical stride equal to the height + * + * y_size = stride * height + * c_size = ALIGN(stride/2, 16) * height/2 + * size = y_size + c_size * 2 + * cr_offset = y_size + * cb_offset = y_size + c_size + * + */ + HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar + + + + /* Legacy formats (deprecated), used by ImageFormat.java */ + HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 + HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 + HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2 +}; + + +/** + * Transformation definitions + * + * IMPORTANT NOTE: + * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}. + * + */ + +enum { + /* flip source image horizontally (around the vertical axis) */ + HAL_TRANSFORM_FLIP_H = 0x01, + /* flip source image vertically (around the horizontal axis)*/ + HAL_TRANSFORM_FLIP_V = 0x02, + /* rotate source image 90 degrees clockwise */ + HAL_TRANSFORM_ROT_90 = 0x04, + /* rotate source image 180 degrees */ + HAL_TRANSFORM_ROT_180 = 0x03, + /* rotate source image 270 degrees clockwise */ + HAL_TRANSFORM_ROT_270 = 0x07, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H */ diff --git a/include/system/window.h b/include/system/window.h new file mode 100644 index 00000000000..a6a4d18712d --- /dev/null +++ b/include/system/window.h @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H +#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H + +#include +#include +#include +#include + +#ifdef __cplusplus +#include +#endif + +__BEGIN_DECLS + +/*****************************************************************************/ + +#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \ + (((unsigned)(a)<<24)|((unsigned)(b)<<16)|((unsigned)(c)<<8)|(unsigned)(d)) + +#define ANDROID_NATIVE_WINDOW_MAGIC \ + ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d') + +#define ANDROID_NATIVE_BUFFER_MAGIC \ + ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r') + +// --------------------------------------------------------------------------- + +typedef const native_handle_t* buffer_handle_t; + +// --------------------------------------------------------------------------- + +typedef struct android_native_rect_t +{ + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} android_native_rect_t; + +// --------------------------------------------------------------------------- + +typedef struct android_native_base_t +{ + /* a magic value defined by the actual EGL native type */ + int magic; + + /* the sizeof() of the actual EGL native type */ + int version; + + void* reserved[4]; + + /* reference-counting interface */ + void (*incRef)(struct android_native_base_t* base); + void (*decRef)(struct android_native_base_t* base); +} android_native_base_t; + +typedef struct ANativeWindowBuffer +{ +#ifdef __cplusplus + ANativeWindowBuffer() { + common.magic = ANDROID_NATIVE_BUFFER_MAGIC; + common.version = sizeof(ANativeWindowBuffer); + memset(common.reserved, 0, sizeof(common.reserved)); + } + + // Implement the methods that sp expects so that it + // can be used to automatically refcount ANativeWindowBuffer's. + void incStrong(const void* id) const { + common.incRef(const_cast(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast(&common)); + } +#endif + + struct android_native_base_t common; + + int width; + int height; + int stride; + int format; + int usage; + + void* reserved[2]; + + buffer_handle_t handle; + + void* reserved_proc[8]; +} ANativeWindowBuffer_t; + +// Old typedef for backwards compatibility. +typedef ANativeWindowBuffer_t android_native_buffer_t; + +// --------------------------------------------------------------------------- + +/* attributes queriable with query() */ +enum { + NATIVE_WINDOW_WIDTH = 0, + NATIVE_WINDOW_HEIGHT = 1, + NATIVE_WINDOW_FORMAT = 2, + + /* The minimum number of buffers that must remain un-dequeued after a buffer + * has been queued. This value applies only if set_buffer_count was used to + * override the number of buffers and if a buffer has since been queued. + * Users of the set_buffer_count ANativeWindow method should query this + * value before calling set_buffer_count. If it is necessary to have N + * buffers simultaneously dequeued as part of the steady-state operation, + * and this query returns M then N+M buffers should be requested via + * native_window_set_buffer_count. + * + * Note that this value does NOT apply until a single buffer has been + * queued. In particular this means that it is possible to: + * + * 1. Query M = min undequeued buffers + * 2. Set the buffer count to N + M + * 3. Dequeue all N + M buffers + * 4. Cancel M buffers + * 5. Queue, dequeue, queue, dequeue, ad infinitum + */ + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3, + + /* Check whether queueBuffer operations on the ANativeWindow send the buffer + * to the window compositor. The query sets the returned 'value' argument + * to 1 if the ANativeWindow DOES send queued buffers directly to the window + * compositor and 0 if the buffers do not go directly to the window + * compositor. + * + * This can be used to determine whether protected buffer content should be + * sent to the ANativeWindow. Note, however, that a result of 1 does NOT + * indicate that queued buffers will be protected from applications or users + * capturing their contents. If that behavior is desired then some other + * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in + * conjunction with this query. + */ + NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4, + + /* Get the concrete type of a ANativeWindow. See below for the list of + * possible return values. + * + * This query should not be used outside the Android framework and will + * likely be removed in the near future. + */ + NATIVE_WINDOW_CONCRETE_TYPE = 5, + + + /* + * Default width and height of the ANativeWindow, these are the dimensions + * of the window irrespective of the NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS + * call. + */ + NATIVE_WINDOW_DEFAULT_WIDTH = 6, + NATIVE_WINDOW_DEFAULT_HEIGHT = 7, + + /* + * transformation that will most-likely be applied to buffers. This is only + * a hint, the actual transformation applied might be different. + * + * INTENDED USE: + * + * The transform hint can be used by a producer, for instance the GLES + * driver, to pre-rotate the rendering such that the final transformation + * in the composer is identity. This can be very useful when used in + * conjunction with the h/w composer HAL, in situations where it + * cannot handle arbitrary rotations. + * + * 1. Before dequeuing a buffer, the GL driver (or any other ANW client) + * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT. + * + * 2. The GL driver overrides the width and height of the ANW to + * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying + * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions + * according to NATIVE_WINDOW_TRANSFORM_HINT and calling + * native_window_set_buffers_dimensions(). + * + * 3. The GL driver dequeues a buffer of the new pre-rotated size. + * + * 4. The GL driver renders to the buffer such that the image is + * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT + * to the rendering. + * + * 5. The GL driver calls native_window_set_transform to apply + * inverse transformation to the buffer it just rendered. + * In order to do this, the GL driver needs + * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is + * done easily: + * + * int hintTransform, inverseTransform; + * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform); + * inverseTransform = hintTransform; + * if (hintTransform & HAL_TRANSFORM_ROT_90) + * inverseTransform ^= HAL_TRANSFORM_ROT_180; + * + * + * 6. The GL driver queues the pre-transformed buffer. + * + * 7. The composer combines the buffer transform with the display + * transform. If the buffer transform happens to cancel out the + * display transform then no rotation is needed. + * + */ + NATIVE_WINDOW_TRANSFORM_HINT = 8, + + NATIVE_WINDOW_NUM_BUFFERS = 9, +}; + +/* valid operations for the (*perform)() hook */ +enum { + NATIVE_WINDOW_SET_USAGE = 0, + NATIVE_WINDOW_CONNECT = 1, /* deprecated */ + NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ + NATIVE_WINDOW_SET_CROP = 3, + NATIVE_WINDOW_SET_BUFFER_COUNT = 4, + NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ + NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, + NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, + NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, + NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, + NATIVE_WINDOW_SET_SCALING_MODE = 10, + NATIVE_WINDOW_LOCK = 11, /* private */ + NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ + NATIVE_WINDOW_API_CONNECT = 13, /* private */ + NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ +}; + +/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ +enum { + /* Buffers will be queued by EGL via eglSwapBuffers after being filled using + * OpenGL ES. + */ + NATIVE_WINDOW_API_EGL = 1, + + /* Buffers will be queued after being filled using the CPU + */ + NATIVE_WINDOW_API_CPU = 2, + + /* Buffers will be queued by Stagefright after being filled by a video + * decoder. The video decoder can either be a software or hardware decoder. + */ + NATIVE_WINDOW_API_MEDIA = 3, + + /* Buffers will be queued by the the camera HAL. + */ + NATIVE_WINDOW_API_CAMERA = 4, +}; + +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */ +enum { + /* flip source image horizontally */ + NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , + /* flip source image vertically */ + NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, + /* rotate source image 90 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, + /* rotate source image 180 degrees */ + NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, + /* rotate source image 270 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, +}; + +/* parameter for NATIVE_WINDOW_SET_SCALING_MODE */ +enum { + /* the window content is not updated (frozen) until a buffer of + * the window size is received (enqueued) + */ + NATIVE_WINDOW_SCALING_MODE_FREEZE = 0, + /* the buffer is scaled in both dimensions to match the window size */ + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1, +}; + +/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */ +enum { + NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */ + NATIVE_WINDOW_SURFACE = 1, /* Surface */ + NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT = 2, /* SurfaceTextureClient */ +}; + +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP + * + * Special timestamp value to indicate that timestamps should be auto-generated + * by the native window when queueBuffer is called. This is equal to INT64_MIN, + * defined directly to avoid problems with C99/C++ inclusion of stdint.h. + */ +static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1); + +struct ANativeWindow +{ +#ifdef __cplusplus + ANativeWindow() + : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) + { + common.magic = ANDROID_NATIVE_WINDOW_MAGIC; + common.version = sizeof(ANativeWindow); + memset(common.reserved, 0, sizeof(common.reserved)); + } + + /* Implement the methods that sp expects so that it + can be used to automatically refcount ANativeWindow's. */ + void incStrong(const void* id) const { + common.incRef(const_cast(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast(&common)); + } +#endif + + struct android_native_base_t common; + + /* flags describing some attributes of this surface or its updater */ + const uint32_t flags; + + /* min swap interval supported by this updated */ + const int minSwapInterval; + + /* max swap interval supported by this updated */ + const int maxSwapInterval; + + /* horizontal and vertical resolution in DPI */ + const float xdpi; + const float ydpi; + + /* Some storage reserved for the OEM's driver. */ + intptr_t oem[4]; + + /* + * Set the swap interval for this surface. + * + * Returns 0 on success or -errno on error. + */ + int (*setSwapInterval)(struct ANativeWindow* window, + int interval); + + /* + * Hook called by EGL to acquire a buffer. After this call, the buffer + * is not locked, so its content cannot be modified. This call may block if + * no buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * Returns 0 on success or -errno on error. + */ + int (*dequeueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer); + + /* + * hook called by EGL to lock a buffer. This MUST be called before modifying + * the content of a buffer. The buffer must have been acquired with + * dequeueBuffer first. + * + * Returns 0 on success or -errno on error. + */ + int (*lockBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + /* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * Buffers MUST be queued in the same order than they were dequeued. + * + * Returns 0 on success or -errno on error. + */ + int (*queueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + /* + * hook used to retrieve information about the native window. + * + * Returns 0 on success or -errno on error. + */ + int (*query)(const struct ANativeWindow* window, + int what, int* value); + + /* + * hook used to perform various operations on the surface. + * (*perform)() is a generic mechanism to add functionality to + * ANativeWindow while keeping backward binary compatibility. + * + * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions + * defined below. + * + * (*perform)() returns -ENOENT if the 'what' parameter is not supported + * by the surface's implementation. + * + * The valid operations are: + * NATIVE_WINDOW_SET_USAGE + * NATIVE_WINDOW_CONNECT (deprecated) + * NATIVE_WINDOW_DISCONNECT (deprecated) + * NATIVE_WINDOW_SET_CROP + * NATIVE_WINDOW_SET_BUFFER_COUNT + * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY (deprecated) + * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM + * NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP + * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS + * NATIVE_WINDOW_SET_BUFFERS_FORMAT + * NATIVE_WINDOW_SET_SCALING_MODE + * NATIVE_WINDOW_LOCK (private) + * NATIVE_WINDOW_UNLOCK_AND_POST (private) + * NATIVE_WINDOW_API_CONNECT (private) + * NATIVE_WINDOW_API_DISCONNECT (private) + * + */ + + int (*perform)(struct ANativeWindow* window, + int operation, ... ); + + /* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + */ + int (*cancelBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + + void* reserved_proc[2]; +}; + + /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). + * android_native_window_t is deprecated. + */ +typedef struct ANativeWindow ANativeWindow; +typedef struct ANativeWindow android_native_window_t; + +/* + * native_window_set_usage(..., usage) + * Sets the intended usage flags for the next buffers + * acquired with (*lockBuffer)() and on. + * By default (if this function is never called), a usage of + * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE + * is assumed. + * Calling this function will usually cause following buffers to be + * reallocated. + */ + +static inline int native_window_set_usage( + struct ANativeWindow* window, int usage) +{ + return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage); +} + +/* deprecated. Always returns 0. Don't call. */ +static inline int native_window_connect( + struct ANativeWindow* window, int api) { + return 0; +} + +/* deprecated. Always returns 0. Don't call. */ +static inline int native_window_disconnect( + struct ANativeWindow* window, int api) { + return 0; +} + +/* + * native_window_set_crop(..., crop) + * Sets which region of the next queued buffers needs to be considered. + * A buffer's crop region is scaled to match the surface's size. + * + * The specified crop region applies to all buffers queued after it is called. + * + * if 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, + * out of the buffer's bound or if the window is invalid. + */ +static inline int native_window_set_crop( + struct ANativeWindow* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); +} + +/* + * native_window_set_buffer_count(..., count) + * Sets the number of buffers associated with this native window. + */ +static inline int native_window_set_buffer_count( + struct ANativeWindow* window, + size_t bufferCount) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); +} + +/* + * native_window_set_buffers_geometry(..., int w, int h, int format) + * All buffers dequeued after this call will have the dimensions and format + * specified. A successful call to this function has the same effect as calling + * native_window_set_buffers_size and native_window_set_buffers_format. + * + * XXX: This function is deprecated. The native_window_set_buffers_dimensions + * and native_window_set_buffers_format functions should be used instead. + */ +static inline int native_window_set_buffers_geometry( + struct ANativeWindow* window, + int w, int h, int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + w, h, format); +} + +/* + * native_window_set_buffers_dimensions(..., int w, int h) + * All buffers dequeued after this call will have the dimensions specified. + * In particular, all buffers will have a fixed-size, independent form the + * native-window size. They will be scaled according to the scaling mode + * (see native_window_set_scaling_mode) upon window composition. + * + * If w and h are 0, the normal behavior is restored. That is, dequeued buffers + * following this call will be sized to match the window's size. + * + * Calling this function will reset the window crop to a NULL value, which + * disables cropping of the buffers. + */ +static inline int native_window_set_buffers_dimensions( + struct ANativeWindow* window, + int w, int h) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS, + w, h); +} + +/* + * native_window_set_buffers_format(..., int format) + * All buffers dequeued after this call will have the format specified. + * + * If the specified format is 0, the default buffer format will be used. + */ +static inline int native_window_set_buffers_format( + struct ANativeWindow* window, + int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format); +} + +/* + * native_window_set_buffers_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified. + */ +static inline int native_window_set_buffers_transform( + struct ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, + transform); +} + +/* + * native_window_set_buffers_timestamp(..., int64_t timestamp) + * All buffers queued after this call will be associated with the timestamp + * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO + * (the default), timestamps will be generated automatically when queueBuffer is + * called. The timestamp is measured in nanoseconds, and is normally monotonically + * increasing. The timestamp should be unaffected by time-of-day adjustments, + * and for a camera should be strictly monotonic but for a media player may be + * reset when the position is set. + */ +static inline int native_window_set_buffers_timestamp( + struct ANativeWindow* window, + int64_t timestamp) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP, + timestamp); +} + +/* + * native_window_set_scaling_mode(..., int mode) + * All buffers queued after this call will be associated with the scaling mode + * specified. + */ +static inline int native_window_set_scaling_mode( + struct ANativeWindow* window, + int mode) +{ + return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE, + mode); +} + + +/* + * native_window_api_connect(..., int api) + * connects an API to this window. only one API can be connected at a time. + * Returns -EINVAL if for some reason the window cannot be connected, which + * can happen if it's connected to some other API. + */ +static inline int native_window_api_connect( + struct ANativeWindow* window, int api) +{ + return window->perform(window, NATIVE_WINDOW_API_CONNECT, api); +} + +/* + * native_window_api_disconnect(..., int api) + * disconnect the API from this window. + * An error is returned if for instance the window wasn't connected in the + * first place. + */ +static inline int native_window_api_disconnect( + struct ANativeWindow* window, int api) +{ + return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); +} + + +__END_DECLS + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */ diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h index 4e3d3964872..868784fc788 100644 --- a/include/sysutils/FrameworkListener.h +++ b/include/sysutils/FrameworkListener.h @@ -23,7 +23,7 @@ class SocketClient; class FrameworkListener : public SocketListener { public: - static const int CMD_ARGS_MAX = 8; + static const int CMD_ARGS_MAX = 32; private: FrameworkCommandCollection *mCommands; diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index 95e83a315f0..25a56f7715b 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -16,6 +16,8 @@ #ifndef _NETLINKEVENT_H #define _NETLINKEVENT_H +#include + #define NL_PARAMS_MAX 32 class NetlinkEvent { @@ -30,15 +32,23 @@ class NetlinkEvent { const static int NlActionAdd; const static int NlActionRemove; const static int NlActionChange; + const static int NlActionLinkDown; + const static int NlActionLinkUp; NetlinkEvent(); virtual ~NetlinkEvent(); - bool decode(char *buffer, int size); + bool decode(char *buffer, int size, int format = NetlinkListener::NETLINK_FORMAT_ASCII); const char *findParam(const char *paramName); const char *getSubsystem() { return mSubsystem; } int getAction() { return mAction; } + + void dump(); + + protected: + bool parseBinaryNetlinkMessage(char *buffer, int size); + bool parseAsciiNetlinkMessage(char *buffer, int size); }; #endif diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h index 6dcc005ca3c..beb8bda7019 100644 --- a/include/sysutils/NetlinkListener.h +++ b/include/sysutils/NetlinkListener.h @@ -22,11 +22,27 @@ class NetlinkEvent; class NetlinkListener : public SocketListener { char mBuffer[64 * 1024]; + int mFormat; public: + static const int NETLINK_FORMAT_ASCII = 0; + static const int NETLINK_FORMAT_BINARY = 1; + +#if 1 + /* temporary version until we can get Motorola to update their + * ril.so. Their prebuilt ril.so is using this private class + * so changing the NetlinkListener() constructor breaks their ril. + */ NetlinkListener(int socket); + NetlinkListener(int socket, int format); +#else + NetlinkListener(int socket, int format = NETLINK_FORMAT_ASCII); +#endif virtual ~NetlinkListener() {} + protected: virtual bool onDataAvailable(SocketClient *cli); + virtual void onEvent(NetlinkEvent *evt) = 0; }; + #endif diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h index 469dd9d0895..7d2b1d67c5f 100644 --- a/include/sysutils/SocketClient.h +++ b/include/sysutils/SocketClient.h @@ -4,19 +4,48 @@ #include "../../../frameworks/base/include/utils/List.h" #include +#include class SocketClient { int mSocket; + bool mSocketOwned; pthread_mutex_t mWriteMutex; + /* Peer process ID */ + pid_t mPid; + + /* Peer user ID */ + uid_t mUid; + + /* Peer group ID */ + gid_t mGid; + + /* Reference count (starts at 1) */ + pthread_mutex_t mRefCountMutex; + int mRefCount; + public: - SocketClient(int sock); - virtual ~SocketClient() {} + SocketClient(int sock, bool owned); + virtual ~SocketClient(); int getSocket() { return mSocket; } + pid_t getPid() const { return mPid; } + uid_t getUid() const { return mUid; } + gid_t getGid() const { return mGid; } + // Send null-terminated C strings: int sendMsg(int code, const char *msg, bool addErrno); int sendMsg(const char *msg); + + // Sending binary data: + int sendData(const void *data, int len); + + // Optional reference counting. Reference count starts at 1. If + // it's decremented to 0, it deletes itself. + // SocketListener creates a SocketClient (at refcount 1) and calls + // decRef() when it's done with the client. + void incRef(); + bool decRef(); // returns true at 0 (but note: SocketClient already deleted) }; typedef android::List SocketClientCollection; diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h index c7edfeb0076..6592b01b1ee 100644 --- a/include/sysutils/SocketListener.h +++ b/include/sysutils/SocketListener.h @@ -30,7 +30,7 @@ class SocketListener { pthread_t mThread; public: - SocketListener(const char *socketNames, bool listen); + SocketListener(const char *socketName, bool listen); SocketListener(int socketFd, bool listen); virtual ~SocketListener(); diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h new file mode 100644 index 00000000000..9a6b59c5f46 --- /dev/null +++ b/include/usbhost/usbhost.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __USB_HOST_H +#define __USB_HOST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include +#else +#include +#endif + +struct usb_host_context; +struct usb_endpoint_descriptor; + +struct usb_descriptor_iter { + unsigned char* config; + unsigned char* config_end; + unsigned char* curr_desc; +}; + +struct usb_request +{ + struct usb_device *dev; + void* buffer; + int buffer_length; + int actual_length; + int max_packet_size; + void *private_data; /* struct usbdevfs_urb* */ + int endpoint; + void *client_data; /* free for use by client */ +}; + +/* Callback for notification when new USB devices are attached. + * Return true to exit from usb_host_run. + */ +typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data); + +/* Callback for notification when USB devices are removed. + * Return true to exit from usb_host_run. + */ +typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data); + +/* Callback indicating that initial device discovery is done. + * Return true to exit from usb_host_run. + */ +typedef int (* usb_discovery_done_cb)(void *client_data); + +/* Call this to initialize the USB host library. */ +struct usb_host_context *usb_host_init(void); + +/* Call this to cleanup the USB host library. */ +void usb_host_cleanup(struct usb_host_context *context); + +/* Call this to monitor the USB bus for new and removed devices. + * This is intended to be called from a dedicated thread, + * as it will not return until one of the callbacks returns true. + * added_cb will be called immediately for each existing USB device, + * and subsequently each time a new device is added. + * removed_cb is called when USB devices are removed from the bus. + * discovery_done_cb is called after the initial discovery of already + * connected devices is complete. + */ +void usb_host_run(struct usb_host_context *context, + usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + usb_discovery_done_cb discovery_done_cb, + void *client_data); + +/* Creates a usb_device object for a USB device */ +struct usb_device *usb_device_open(const char *dev_name); + +/* Releases all resources associated with the USB device */ +void usb_device_close(struct usb_device *device); + +/* Creates a usb_device object for already open USB device */ +struct usb_device *usb_device_new(const char *dev_name, int fd); + +/* Returns the file descriptor for the usb_device */ +int usb_device_get_fd(struct usb_device *device); + +/* Returns the name for the USB device, which is the same as + * the dev_name passed to usb_device_open() + */ +const char* usb_device_get_name(struct usb_device *device); + +/* Returns a unique ID for the device. + *Currently this is generated from the dev_name path. + */ +int usb_device_get_unique_id(struct usb_device *device); + +/* Returns a unique ID for the device name. + * Currently this is generated from the device path. + */ +int usb_device_get_unique_id_from_name(const char* name); + +/* Returns the device name for the unique ID. + * Call free() to deallocate the returned string */ +char* usb_device_get_name_from_unique_id(int id); + +/* Returns the USB vendor ID from the device descriptor for the USB device */ +uint16_t usb_device_get_vendor_id(struct usb_device *device); + +/* Returns the USB product ID from the device descriptor for the USB device */ +uint16_t usb_device_get_product_id(struct usb_device *device); + +const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device); + +/* Returns a USB descriptor string for the given string ID. + * Used to implement usb_device_get_manufacturer_name, + * usb_device_get_product_name and usb_device_get_serial. + * Call free() to free the result when you are done with it. + */ +char* usb_device_get_string(struct usb_device *device, int id); + +/* Returns the manufacturer name for the USB device. + * Call free() to free the result when you are done with it. + */ +char* usb_device_get_manufacturer_name(struct usb_device *device); + +/* Returns the product name for the USB device. + * Call free() to free the result when you are done with it. + */ +char* usb_device_get_product_name(struct usb_device *device); + +/* Returns the USB serial number for the USB device. + * Call free() to free the result when you are done with it. + */ +char* usb_device_get_serial(struct usb_device *device); + +/* Returns true if we have write access to the USB device, + * and false if we only have access to the USB device configuration. + */ +int usb_device_is_writeable(struct usb_device *device); + +/* Initializes a usb_descriptor_iter, which can be used to iterate through all + * the USB descriptors for a USB device. + */ +void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter); + +/* Returns the next USB descriptor for a device, or NULL if we have reached the + * end of the list. + */ +struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter); + +/* Claims the specified interface of a USB device */ +int usb_device_claim_interface(struct usb_device *device, unsigned int interface); + +/* Releases the specified interface of a USB device */ +int usb_device_release_interface(struct usb_device *device, unsigned int interface); + +/* Requests the kernel to connect or disconnect its driver for the specified interface. + * This can be used to ask the kernel to disconnect its driver for a device + * so usb_device_claim_interface can claim it instead. + */ +int usb_device_connect_kernel_driver(struct usb_device *device, + unsigned int interface, int connect); + +/* Sends a control message to the specified device on endpoint zero */ +int usb_device_control_transfer(struct usb_device *device, + int requestType, + int request, + int value, + int index, + void* buffer, + int length, + unsigned int timeout); + +/* Reads or writes on a bulk endpoint. + * Returns number of bytes transferred, or negative value for error. + */ +int usb_device_bulk_transfer(struct usb_device *device, + int endpoint, + void* buffer, + int length, + unsigned int timeout); + +/* Creates a new usb_request. */ +struct usb_request *usb_request_new(struct usb_device *dev, + const struct usb_endpoint_descriptor *ep_desc); + +/* Releases all resources associated with the request */ +void usb_request_free(struct usb_request *req); + +/* Submits a read or write request on the specified device */ +int usb_request_queue(struct usb_request *req); + + /* Waits for the results of a previous usb_request_queue operation. + * Returns a usb_request, or NULL for error. + */ +struct usb_request *usb_request_wait(struct usb_device *dev); + +/* Cancels a pending usb_request_queue() operation. */ +int usb_request_cancel(struct usb_request *req); + +#ifdef __cplusplus +} +#endif +#endif /* __USB_HOST_H */ diff --git a/init/Android.mk b/init/Android.mk index d3766d4556c..f81a58852b1 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -10,13 +10,33 @@ LOCAL_SRC_FILES:= \ property_service.c \ util.c \ parser.c \ - logo.c + logo.c \ + keychords.c \ + signal_handler.c \ + init_parser.c \ + ueventd.c \ + ueventd_parser.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c LOCAL_CFLAGS += -DBOOTCHART=1 endif +ifeq ($(TARGET_NO_INITLOGO),true) +LOCAL_CFLAGS += -DNO_INITLOGO +endif + +SYSTEM_CORE_INIT_DEFINES := \ + BOARD_CHARGING_MODE_BOOTING_LPM \ + BOARD_CHARGING_CMDLINE_NAME \ + BOARD_CHARGING_CMDLINE_VALUE + +$(foreach system_core_init_define,$(SYSTEM_CORE_INIT_DEFINES), \ + $(if $($(system_core_init_define)), \ + $(eval LOCAL_CFLAGS += -D$(system_core_init_define)=\"$($(system_core_init_define))\") \ + ) \ + ) + LOCAL_MODULE:= init LOCAL_FORCE_STATIC_EXECUTABLE := true @@ -25,9 +45,20 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libcutils libc -#LOCAL_STATIC_LIBRARIES := libcutils libc libminui libpixelflinger_static -#LOCAL_STATIC_LIBRARIES += libminzip libunz libamend libmtdutils libmincrypt -#LOCAL_STATIC_LIBRARIES += libstdc++_static - include $(BUILD_EXECUTABLE) +# Make a symlink from /sbin/ueventd to /init +SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd +$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE) +$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> ../$(INIT_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf ../$(INIT_BINARY) $@ + +ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS) + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ + $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS) diff --git a/init/builtins.c b/init/builtins.c index 5a1b2637895..263d38befab 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -30,11 +31,15 @@ #include #include #include +#include #include "init.h" #include "keywords.h" #include "property_service.h" #include "devices.h" +#include "init_parser.h" +#include "util.h" +#include "log.h" #include @@ -65,6 +70,49 @@ static int write_file(const char *path, const char *value) } } + +static int _chown(const char *path, unsigned int uid, unsigned int gid) +{ + int ret; + + struct stat p_statbuf; + + ret = lstat(path, &p_statbuf); + if (ret < 0) { + return -1; + } + + if (S_ISLNK(p_statbuf.st_mode) == 1) { + errno = EINVAL; + return -1; + } + + ret = chown(path, uid, gid); + + return ret; +} + +static int _chmod(const char *path, mode_t mode) +{ + int ret; + + struct stat p_statbuf; + + ret = lstat(path, &p_statbuf); + if (ret < 0) { + return -1; + } + + if (S_ISLNK(p_statbuf.st_mode) == 1) { + errno = EINVAL; + return -1; + } + + ret = chmod(path, mode); + + return ret; +} + static int insmod(const char *filename, char *options) { void *module; @@ -131,6 +179,18 @@ static void service_start_if_not_disabled(struct service *svc) } } +int do_chdir(int nargs, char **args) +{ + chdir(args[1]); + return 0; +} + +int do_chroot(int nargs, char **args) +{ + chroot(args[1]); + return 0; +} + int do_class_start(int nargs, char **args) { /* Starting a class does not start services @@ -147,14 +207,53 @@ int do_class_stop(int nargs, char **args) return 0; } +int do_class_reset(int nargs, char **args) +{ + service_for_each_class(args[1], service_reset); + return 0; +} + int do_domainname(int nargs, char **args) { return write_file("/proc/sys/kernel/domainname", args[1]); } +/*exec ... */ +#define MAX_PARAMETERS 64 int do_exec(int nargs, char **args) { - return -1; + pid_t pid; + int status, i, j; + char *par[MAX_PARAMETERS]; + if (nargs > MAX_PARAMETERS) + { + return -1; + } + for(i=0, j=1; i<(nargs-1) ;i++,j++) + { + par[i] = args[j]; + } + par[i] = (char*)0; + pid = fork(); + if (!pid) + { + char tmp[32]; + int fd, sz; + get_property_workspace(&fd, &sz); + sprintf(tmp, "%d,%d", dup(fd), sz); + setenv("ANDROID_PROPERTY_WORKSPACE", tmp, 1); + execve(par[0],par,environ); + exit(0); + } + else + { + while (waitpid(pid, &status, 0) == -1 && errno == EINTR); + if (WEXITSTATUS(status) != 0) { + ERROR("exec: pid %1d exited with return code %d: %s", (int)pid, WEXITSTATUS(status), strerror(status)); + } + + } + return 0; } int do_export(int nargs, char **args) @@ -204,14 +303,36 @@ int do_insmod(int nargs, char **args) return do_insmod_inner(nargs, args, size); } -int do_import(int nargs, char **args) +int do_log(int nargs, char **args) { - return -1; + char* par[nargs+3]; + char* value; + int i; + + par[0] = "exec"; + par[1] = "/system/bin/log"; + par[2] = "-tinit"; + for (i = 1; i < nargs; ++i) { + value = args[i]; + if (value[0] == '$') { + /* system property if value starts with '$' */ + value++; + if (value[0] != '$') { + value = (char*) property_get(value); + if (!value) value = args[i]; + } + } + par[i+2] = value; + } + par[nargs+2] = NULL; + + return do_exec(nargs+2, par); } int do_mkdir(int nargs, char **args) { mode_t mode = 0755; + int ret; /* mkdir [mode] [owner] [group] */ @@ -219,7 +340,12 @@ int do_mkdir(int nargs, char **args) mode = strtoul(args[2], 0, 8); } - if (mkdir(args[1], mode)) { + ret = mkdir(args[1], mode); + /* chmod in case the directory already exists */ + if (ret == -1 && errno == EEXIST) { + ret = _chmod(args[1], mode); + } + if (ret == -1) { return -errno; } @@ -231,7 +357,7 @@ int do_mkdir(int nargs, char **args) gid = decode_uid(args[4]); } - if (chown(args[1], uid, gid)) { + if (_chown(args[1], uid, gid) < 0) { return -errno; } } @@ -244,6 +370,7 @@ static struct { unsigned flag; } mount_flags[] = { { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, { "nosuid", MS_NOSUID }, { "nodev", MS_NODEV }, { "nodiratime", MS_NODIRATIME }, @@ -254,6 +381,8 @@ static struct { { 0, 0 }, }; +#define DATA_MNT_POINT "/data" + /* mount */ int do_mount(int nargs, char **args) { @@ -262,6 +391,7 @@ int do_mount(int nargs, char **args) char *options = NULL; unsigned flags = 0; int n, i; + int wait = 0; for (n = 4; n < nargs; n++) { for (i = 0; mount_flags[i].name; i++) { @@ -271,9 +401,13 @@ int do_mount(int nargs, char **args) } } - /* if our last argument isn't a flag, wolf it up as an option string */ - if (n + 1 == nargs && !mount_flags[i].name) - options = args[n]; + if (!mount_flags[i].name) { + if (!strcmp(args[n], "wait")) + wait = 1; + /* if our last argument isn't a flag, wolf it up as an option string */ + else if (n + 1 == nargs) + options = args[n]; + } } system = args[1]; @@ -288,11 +422,13 @@ int do_mount(int nargs, char **args) sprintf(tmp, "/dev/block/mtdblock%d", n); + if (wait) + wait_for_file(tmp, COMMAND_RETRY_TIMEOUT); if (mount(tmp, target, system, flags, options) < 0) { return -1; } - return 0; + goto exit_success; } else if (!strncmp(source, "loop@", 5)) { int mode, loop, fd; struct loop_info info; @@ -323,7 +459,7 @@ int do_mount(int nargs, char **args) } close(loop); - return 0; + goto exit_success; } } @@ -334,12 +470,73 @@ int do_mount(int nargs, char **args) ERROR("out of loopback devices"); return -1; } else { + if (wait) + wait_for_file(source, COMMAND_RETRY_TIMEOUT); if (mount(source, target, system, flags, options) < 0) { - return -1; + /* If this fails, it may be an encrypted filesystem + * or it could just be wiped. If wiped, that will be + * handled later in the boot process. + * We only support encrypting /data. Check + * if we're trying to mount it, and if so, + * assume it's encrypted, mount a tmpfs instead. + * Then save the orig mount parms in properties + * for vold to query when it mounts the real + * encrypted /data. + */ + if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) { + const char *tmpfs_options; + + tmpfs_options = property_get("ro.crypto.tmpfs_options"); + + if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV, + tmpfs_options) < 0) { + return -1; + } + + /* Set the property that triggers the framework to do a minimal + * startup and ask the user for a password + */ + property_set("ro.crypto.state", "encrypted"); + property_set("vold.decrypt", "1"); + } else { + return -1; + } } - return 0; + if (!strcmp(target, DATA_MNT_POINT)) { + char fs_flags[32]; + + /* Save the original mount options */ + property_set("ro.crypto.fs_type", system); + property_set("ro.crypto.fs_real_blkdev", source); + property_set("ro.crypto.fs_mnt_point", target); + if (options) { + property_set("ro.crypto.fs_options", options); + } + snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags); + property_set("ro.crypto.fs_flags", fs_flags); + } } + +exit_success: + /* If not running encrypted, then set the property saying we are + * unencrypted, and also trigger the action for a nonencrypted system. + */ + if (!strcmp(target, DATA_MNT_POINT)) { + const char *prop; + + prop = property_get("ro.crypto.state"); + if (! prop) { + prop = "notset"; + } + if (strcmp(prop, "encrypted")) { + property_set("ro.crypto.state", "unencrypted"); + action_for_each_trigger("nonencrypted", action_add_queue_tail); + } + } + + return 0; + } int do_setkey(int nargs, char **args) @@ -353,7 +550,24 @@ int do_setkey(int nargs, char **args) int do_setprop(int nargs, char **args) { - property_set(args[1], args[2]); + const char *name = args[1]; + const char *value = args[2]; + + if (value[0] == '$') { + /* Use the value of a system property if value starts with '$' */ + value++; + if (value[0] != '$') { + value = property_get(value); + if (!value) { + ERROR("property %s has no value for assigning to %s\n", value, name); + return -EINVAL; + } + } /* else fall through to support double '$' prefix for setting properties + * to string literals that start with '$' + */ + } + + property_set(name, value); return 0; } @@ -392,14 +606,14 @@ int do_restart(int nargs, char **args) struct service *svc; svc = service_find_by_name(args[1]); if (svc) { - service_stop(svc); - service_start(svc, NULL); + service_restart(svc); } return 0; } int do_trigger(int nargs, char **args) { + action_for_each_trigger(args[1], action_add_queue_tail); return 0; } @@ -408,6 +622,16 @@ int do_symlink(int nargs, char **args) return symlink(args[1], args[2]); } +int do_rm(int nargs, char **args) +{ + return unlink(args[1]); +} + +int do_rmdir(int nargs, char **args) +{ + return rmdir(args[1]); +} + int do_sysclktz(int nargs, char **args) { struct timezone tz; @@ -424,7 +648,23 @@ int do_sysclktz(int nargs, char **args) int do_write(int nargs, char **args) { - return write_file(args[1], args[2]); + const char *path = args[1]; + const char *value = args[2]; + if (value[0] == '$') { + /* Write the value of a system property if value starts with '$' */ + value++; + if (value[0] != '$') { + value = property_get(value); + if (!value) { + ERROR("property %s has no value for writing to %s\n", value, path); + return -EINVAL; + } + } /* else fall through to support double '$' prefix for writing + * string literals that start with '$' + */ + } + + return write_file(path, value); } int do_copy(int nargs, char **args) @@ -492,10 +732,10 @@ int do_copy(int nargs, char **args) int do_chown(int nargs, char **args) { /* GID is optional. */ if (nargs == 3) { - if (chown(args[2], decode_uid(args[1]), -1) < 0) + if (_chown(args[2], decode_uid(args[1]), -1) < 0) return -errno; } else if (nargs == 4) { - if (chown(args[3], decode_uid(args[1]), decode_uid(args[2]))) + if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0) return -errno; } else { return -1; @@ -518,7 +758,7 @@ static mode_t get_mode(const char *s) { int do_chmod(int nargs, char **args) { mode_t mode = get_mode(args[1]); - if (chmod(args[2], mode) < 0) { + if (_chmod(args[2], mode) < 0) { return -errno; } return 0; @@ -526,35 +766,24 @@ int do_chmod(int nargs, char **args) { int do_loglevel(int nargs, char **args) { if (nargs == 2) { - log_set_level(atoi(args[1])); + klog_set_level(atoi(args[1])); return 0; } return -1; } -int do_device(int nargs, char **args) { - int len; - char tmp[64]; - char *source = args[1]; - int prefix = 0; - - if (nargs != 5) - return -1; - /* Check for wildcard '*' at the end which indicates a prefix. */ - len = strlen(args[1]) - 1; - if (args[1][len] == '*') { - args[1][len] = '\0'; - prefix = 1; +int do_load_persist_props(int nargs, char **args) { + if (nargs == 1) { + load_persist_props(); + return 0; } - /* If path starts with mtd@ lookup the mount number. */ - if (!strncmp(source, "mtd@", 4)) { - int n = mtd_name_to_number(source + 4); - if (n >= 0) { - snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n); - source = tmp; - } + return -1; +} + +int do_wait(int nargs, char **args) +{ + if (nargs == 2) { + return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT); } - add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]), - decode_uid(args[4]), prefix); - return 0; + return -1; } diff --git a/init/device_perms.h b/init/device_perms.h new file mode 100644 index 00000000000..504c0ab5e3a --- /dev/null +++ b/init/device_perms.h @@ -0,0 +1,29 @@ +// Overload this file in your own device-specific config if you need +// non-standard property_perms and/or control_perms structs +// +// To avoid conflicts... +// if you redefine property_perms, #define PROPERTY_PERMS there +// if you redefine control_perms, #define CONTROL_PARMS there +// +// A typical file will look like: +// +/* + +#define CONTROL_PERMS + +struct { + const char *service; + unsigned int uid; + unsigned int gid; +} control_perms[] = { + // The default perms + { "dumpstate",AID_SHELL, AID_LOG }, + { "ril-daemon",AID_RADIO, AID_RADIO }, + // My magic device perms + { "rawip_vsnet1",AID_RADIO, AID_RADIO }, + { "rawip_vsnet2",AID_RADIO, AID_RADIO }, + { "rawip_vsnet3",AID_RADIO, AID_RADIO }, + { "rawip_vsnet4",AID_RADIO, AID_RADIO }, + {NULL, 0, 0 } +}; +*/ diff --git a/init/devices.c b/init/devices.c index 4d93b441f06..a2f84aa3c93 100644 --- a/init/devices.c +++ b/init/devices.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -31,286 +32,231 @@ #include #include #include +#include + +#include +#include -#include "init.h" #include "devices.h" +#include "util.h" +#include "log.h" -#define CMDLINE_PREFIX "/dev" #define SYSFS_PREFIX "/sys" -#define FIRMWARE_DIR "/etc/firmware" -#define MAX_QEMU_PERM 6 +#define FIRMWARE_DIR1 "/etc/firmware" +#define FIRMWARE_DIR2 "/vendor/firmware" + +static int device_fd = -1; struct uevent { const char *action; const char *path; const char *subsystem; const char *firmware; + const char *partition_name; + int partition_num; int major; int minor; }; -static int open_uevent_socket(void) -{ - struct sockaddr_nl addr; - int sz = 64*1024; // XXX larger? udev uses 16MB! - int s; - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = getpid(); - addr.nl_groups = 0xffffffff; - - s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if(s < 0) - return -1; - - setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); - - if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(s); - return -1; - } - - return s; -} - struct perms_ { char *name; + char *attr; mode_t perm; unsigned int uid; unsigned int gid; unsigned short prefix; }; -static struct perms_ devperms[] = { - { "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* logger should be world writable (for logging) but not readable */ - { "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 }, - - /* the msm hw3d client device node is world writable/readable. */ - { "/dev/msm_hw3dc", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* gpu driver for adreno200 is globally accessible */ - { "/dev/kgsl", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* these should not be world writable */ - { "/dev/diag", 0660, AID_RADIO, AID_RADIO, 0 }, - { "/dev/diag_arm9", 0660, AID_RADIO, AID_RADIO, 0 }, - { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/uinput", 0660, AID_SYSTEM, AID_BLUETOOTH, 0 }, - { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 }, - { "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 }, - { "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 }, - { "/dev/msm_hw3dm", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, - { "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 }, - { "/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 }, - { "/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 }, - { "/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, - { "/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 }, - { "/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 }, - { "/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/msm_camera/", 0660, AID_SYSTEM, AID_SYSTEM, 1 }, - { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8973_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8973_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/bma150", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/cm3602", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/lightsensor", 0640, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_snd", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/audience_a1026", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/tpa2018d1", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/msm_audio_ctl", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/vdec", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/q6venc", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/snd/dsp", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/snd/dsp1", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/snd/mixer", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qemu_trace", 0666, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 }, - /* CDMA radio interface MUX */ - { "/dev/ts0710mux", 0640, AID_RADIO, AID_RADIO, 1 }, - { "/dev/ppp", 0660, AID_RADIO, AID_VPN, 0 }, - { "/dev/tun", 0640, AID_VPN, AID_VPN, 0 }, - { NULL, 0, 0, 0, 0 }, -}; -/* devperms_partners list and perm_node are for hardware specific /dev entries */ struct perm_node { struct perms_ dp; struct listnode plist; }; -list_declare(devperms_partners); -/* - * Permission override when in emulator mode, must be parsed before - * system properties is initalized. - */ -static int qemu_perm_count; -static struct perms_ qemu_perms[MAX_QEMU_PERM + 1]; +struct platform_node { + char *name; + int name_len; + struct listnode list; +}; + +static list_declare(sys_perms); +static list_declare(dev_perms); +static list_declare(platform_names); -int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix) { - int size; - struct perm_node *node = malloc(sizeof (struct perm_node)); +int add_dev_perms(const char *name, const char *attr, + mode_t perm, unsigned int uid, unsigned int gid, + unsigned short prefix) { + struct perm_node *node = calloc(1, sizeof(*node)); if (!node) return -ENOMEM; - size = strlen(name) + 1; - if ((node->dp.name = malloc(size)) == NULL) + node->dp.name = strdup(name); + if (!node->dp.name) return -ENOMEM; - memcpy(node->dp.name, name, size); + if (attr) { + node->dp.attr = strdup(attr); + if (!node->dp.attr) + return -ENOMEM; + } + node->dp.perm = perm; node->dp.uid = uid; node->dp.gid = gid; node->dp.prefix = prefix; - list_add_tail(&devperms_partners, &node->plist); - return 0; -} - -void qemu_init(void) { - qemu_perm_count = 0; - memset(&qemu_perms, 0, sizeof(qemu_perms)); -} + if (attr) + list_add_tail(&sys_perms, &node->plist); + else + list_add_tail(&dev_perms, &node->plist); -static int qemu_perm(const char* name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix) -{ - char *buf; - if (qemu_perm_count == MAX_QEMU_PERM) - return -ENOSPC; - - buf = malloc(strlen(name) + 1); - if (!buf) - return -errno; - - strlcpy(buf, name, strlen(name) + 1); - qemu_perms[qemu_perm_count].name = buf; - qemu_perms[qemu_perm_count].perm = perm; - qemu_perms[qemu_perm_count].uid = uid; - qemu_perms[qemu_perm_count].gid = gid; - qemu_perms[qemu_perm_count].prefix = prefix; - - qemu_perm_count++; return 0; } -/* Permission overrides for emulator that are parsed from /proc/cmdline. */ -void qemu_cmdline(const char* name, const char *value) +void fixup_sys_perms(const char *upath) { - char *buf; - if (!strcmp(name, "android.ril")) { - /* cmd line params currently assume /dev/ prefix */ - if (asprintf(&buf, CMDLINE_PREFIX"/%s", value) == -1) { - return; - } - INFO("nani- buf:: %s\n", buf); - qemu_perm(buf, 0660, AID_RADIO, AID_ROOT, 0); - } -} - -static int get_device_perm_inner(struct perms_ *perms, const char *path, - unsigned *uid, unsigned *gid, mode_t *perm) -{ - int i; - for(i = 0; perms[i].name; i++) { - - if(perms[i].prefix) { - if(strncmp(path, perms[i].name, strlen(perms[i].name))) + char buf[512]; + struct listnode *node; + struct perms_ *dp; + + /* upaths omit the "/sys" that paths in this list + * contain, so we add 4 when comparing... + */ + list_for_each(node, &sys_perms) { + dp = &(node_to_item(node, struct perm_node, plist))->dp; + if (dp->prefix) { + if (strncmp(upath, dp->name + 4, strlen(dp->name + 4))) continue; } else { - if(strcmp(path, perms[i].name)) + if (strcmp(upath, dp->name + 4)) continue; } - *uid = perms[i].uid; - *gid = perms[i].gid; - *perm = perms[i].perm; - return 0; + + if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf)) + return; + + sprintf(buf,"/sys%s/%s", upath, dp->attr); + INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); + chown(buf, dp->uid, dp->gid); + chmod(buf, dp->perm); } - return -1; } -/* First checks for emulator specific permissions specified in /proc/cmdline. */ static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) { mode_t perm; - - if (get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) { - return perm; - } else if (get_device_perm_inner(devperms, path, uid, gid, &perm) == 0) { - return perm; - } else { - struct listnode *node; - struct perm_node *perm_node; - struct perms_ *dp; - - /* Check partners list. */ - list_for_each(node, &devperms_partners) { - perm_node = node_to_item(node, struct perm_node, plist); - dp = &perm_node->dp; - - if (dp->prefix) { - if (strncmp(path, dp->name, strlen(dp->name))) - continue; - } else { - if (strcmp(path, dp->name)) - continue; - } - /* Found perm in partner list. */ - *uid = dp->uid; - *gid = dp->gid; - return dp->perm; + struct listnode *node; + struct perm_node *perm_node; + struct perms_ *dp; + + /* search the perms list in reverse so that ueventd.$hardware can + * override ueventd.rc + */ + list_for_each_reverse(node, &dev_perms) { + perm_node = node_to_item(node, struct perm_node, plist); + dp = &perm_node->dp; + + if (dp->prefix) { + if (strncmp(path, dp->name, strlen(dp->name))) + continue; + } else { + if (strcmp(path, dp->name)) + continue; } - /* Default if nothing found. */ - *uid = 0; - *gid = 0; - return 0600; + *uid = dp->uid; + *gid = dp->gid; + return dp->perm; } + /* Default if nothing found. */ + *uid = 0; + *gid = 0; + return 0600; } -static void make_device(const char *path, int block, int major, int minor) +static void make_device(const char *path, + const char *upath, + int block, int major, int minor) { unsigned uid; unsigned gid; mode_t mode; dev_t dev; - if(major > 255 || minor > 255) - return; - mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); - dev = (major << 8) | minor; + dev = makedev(major, minor); + /* Temporarily change egid to avoid race condition setting the gid of the + * device node. Unforunately changing the euid would prevent creation of + * some device nodes, so the uid has to be set with chown() and is still + * racy. Fixing the gid race at least fixed the issue with system_server + * opening dynamic input devices under the AID_INPUT gid. */ + setegid(gid); mknod(path, mode, dev); - chown(path, uid, gid); + chown(path, uid, -1); + setegid(AID_ROOT); +} + +static void add_platform_device(const char *name) +{ + int name_len = strlen(name); + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if ((bus->name_len < name_len) && + (name[bus->name_len] == '/') && + !strncmp(name, bus->name, bus->name_len)) + /* subdevice of an existing platform, ignore it */ + return; + } + + INFO("adding platform device %s\n", name); + + bus = calloc(1, sizeof(struct platform_node)); + bus->name = strdup(name); + bus->name_len = name_len; + list_add_tail(&platform_names, &bus->list); +} + +/* + * given a name that may start with a platform device, find the length of the + * platform device prefix. If it doesn't start with a platform device, return + * 0. + */ +static const char *find_platform_device(const char *name) +{ + int name_len = strlen(name); + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if ((bus->name_len < name_len) && + (name[bus->name_len] == '/') && + !strncmp(name, bus->name, bus->name_len)) + return bus->name; + } + + return NULL; +} + +static void remove_platform_device(const char *name) +{ + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if (!strcmp(name, bus->name)) { + INFO("removing platform device %s\n", name); + free(bus->name); + list_remove(node); + free(bus); + return; + } + } } -#ifdef LOG_UEVENTS +#if LOG_UEVENTS static inline suseconds_t get_usecs(void) { @@ -336,6 +282,8 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->firmware = ""; uevent->major = -1; uevent->minor = -1; + uevent->partition_name = NULL; + uevent->partition_num = -1; /* currently ignoring SEQNUM */ while(*msg) { @@ -357,6 +305,12 @@ static void parse_event(const char *msg, struct uevent *uevent) } else if(!strncmp(msg, "MINOR=", 6)) { msg += 6; uevent->minor = atoi(msg); + } else if(!strncmp(msg, "PARTN=", 6)) { + msg += 6; + uevent->partition_num = atoi(msg); + } else if(!strncmp(msg, "PARTNAME=", 9)) { + msg += 9; + uevent->partition_name = msg; } /* advance to after the next \0 */ @@ -369,74 +323,275 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->firmware, uevent->major, uevent->minor); } -static void handle_device_event(struct uevent *uevent) +static char **get_character_device_symlinks(struct uevent *uevent) { - char devpath[96]; - char *base, *name; - int block; + const char *parent; + char *slash; + char **links; + int link_num = 0; + int width; + + if (strncmp(uevent->path, "/devices/platform/", 18)) + return NULL; + + links = malloc(sizeof(char *) * 2); + if (!links) + return NULL; + memset(links, 0, sizeof(char *) * 2); + + /* skip "/devices/platform/" */ + parent = strchr(uevent->path + 18, '/'); + if (!*parent) + goto err; + + if (!strncmp(parent, "/usb", 4)) { + /* skip root hub name and device. use device interface */ + while (*++parent && *parent != '/'); + if (*parent) + while (*++parent && *parent != '/'); + if (!*parent) + goto err; + slash = strchr(++parent, '/'); + if (!slash) + goto err; + width = slash - parent; + if (width <= 0) + goto err; + + if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0) + link_num++; + else + links[link_num] = NULL; + mkdir("/dev/usb", 0755); + } + else { + goto err; + } - /* if it's not a /dev device, nothing to do */ + return links; +err: + free(links); + return NULL; +} + +static char **parse_platform_block_device(struct uevent *uevent) +{ + const char *device; + const char *path; + char *slash; + int width; + char buf[256]; + char link_path[256]; + int fd; + int link_num = 0; + int ret; + char *p; + unsigned int size; + struct stat info; + + char **links = malloc(sizeof(char *) * 4); + if (!links) + return NULL; + memset(links, 0, sizeof(char *) * 4); + + /* Drop "/devices/platform/" */ + path = uevent->path; + device = path + 18; + device = find_platform_device(device); + if (!device) + goto err; + + INFO("found platform device %s\n", device); + + snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); + + if (uevent->partition_name) { + p = strdup(uevent->partition_name); + sanitize(p); + if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0) + link_num++; + else + links[link_num] = NULL; + free(p); + } + + if (uevent->partition_num >= 0) { + if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0) + link_num++; + else + links[link_num] = NULL; + } + + slash = strrchr(path, '/'); + if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) + link_num++; + else + links[link_num] = NULL; + + return links; + +err: + free(links); + return NULL; +} + +static void handle_device(const char *action, const char *devpath, + const char *path, int block, int major, int minor, char **links) +{ + int i; + + if(!strcmp(action, "add")) { + make_device(devpath, path, block, major, minor); + if (links) { + for (i = 0; links[i]; i++) + make_link(devpath, links[i]); + } + } + + if(!strcmp(action, "remove")) { + if (links) { + for (i = 0; links[i]; i++) + remove_link(devpath, links[i]); + } + unlink(devpath); + } + + if (links) { + for (i = 0; links[i]; i++) + free(links[i]); + free(links); + } +} + +static void handle_platform_device_event(struct uevent *uevent) +{ + const char *name = uevent->path + 18; /* length of /devices/platform/ */ + + if (!strcmp(uevent->action, "add")) + add_platform_device(name); + else if (!strcmp(uevent->action, "remove")) + remove_platform_device(name); +} + +static const char *parse_device_name(struct uevent *uevent, unsigned int len) +{ + const char *name; + + /* if it's not a /dev device, nothing else to do */ if((uevent->major < 0) || (uevent->minor < 0)) - return; + return NULL; - /* do we have a name? */ + /* do we have a name? */ name = strrchr(uevent->path, '/'); if(!name) - return; + return NULL; name++; - /* too-long names would overrun our buffer */ - if(strlen(name) > 64) - return; + /* too-long names would overrun our buffer */ + if(strlen(name) > len) + return NULL; - /* are we block or char? where should we live? */ - if(!strncmp(uevent->subsystem, "block", 5)) { - block = 1; - base = "/dev/block/"; - mkdir(base, 0755); - } else { - block = 0; - /* this should probably be configurable somehow */ - if(!strncmp(uevent->subsystem, "graphics", 8)) { - base = "/dev/graphics/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { - base = "/dev/oncrpc/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "adsp", 4)) { - base = "/dev/adsp/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { - base = "/dev/msm_camera/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "input", 5)) { - base = "/dev/input/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "mtd", 3)) { - base = "/dev/mtd/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "sound", 5)) { - base = "/dev/snd/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "misc", 4) && - !strncmp(name, "log_", 4)) { - base = "/dev/log/"; - mkdir(base, 0755); - name += 4; - } else - base = "/dev/"; - } + return name; +} - snprintf(devpath, sizeof(devpath), "%s%s", base, name); +static void handle_block_device_event(struct uevent *uevent) +{ + const char *base = "/dev/block/"; + const char *name; + char devpath[96]; + char **links = NULL; - if(!strcmp(uevent->action, "add")) { - make_device(devpath, block, uevent->major, uevent->minor); + name = parse_device_name(uevent, 64); + if (!name) return; - } - if(!strcmp(uevent->action, "remove")) { - unlink(devpath); + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + mkdir(base, 0755); + + if (!strncmp(uevent->path, "/devices/platform/", 18)) + links = parse_platform_block_device(uevent); + + handle_device(uevent->action, devpath, uevent->path, 1, + uevent->major, uevent->minor, links); +} + +static void handle_generic_device_event(struct uevent *uevent) +{ + char *base; + const char *name; + char devpath[96] = {0}; + char **links = NULL; + + name = parse_device_name(uevent, 64); + if (!name) return; + + if (!strncmp(uevent->subsystem, "usb", 3)) { + if (!strcmp(uevent->subsystem, "usb")) { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int bus_id = uevent->minor / 128 + 1; + int device_id = uevent->minor % 128 + 1; + /* build directories */ + mkdir("/dev/bus", 0755); + mkdir("/dev/bus/usb", 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); + mkdir(devpath, 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + } else { + /* ignore other USB events */ + return; + } + } else if (!strncmp(uevent->subsystem, "graphics", 8)) { + base = "/dev/graphics/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { + base = "/dev/oncrpc/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "adsp", 4)) { + base = "/dev/adsp/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { + base = "/dev/msm_camera/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "input", 5)) { + base = "/dev/input/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "mtd", 3)) { + base = "/dev/mtd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "sound", 5)) { + base = "/dev/snd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "misc", 4) && + !strncmp(name, "log_", 4)) { + base = "/dev/log/"; + mkdir(base, 0755); + name += 4; + } else + base = "/dev/"; + links = get_character_device_symlinks(uevent); + + if (!devpath[0]) + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + + handle_device(uevent->action, devpath, uevent->path, 0, + uevent->major, uevent->minor, links); +} + +static void handle_device_event(struct uevent *uevent) +{ + if (!strcmp(uevent->action,"add")) + fixup_sys_perms(uevent->path); + + if (!strncmp(uevent->subsystem, "block", 5)) { + handle_block_device_event(uevent); + } else if (!strncmp(uevent->subsystem, "platform", 8)) { + handle_platform_device_event(uevent); + } else { + handle_generic_device_event(uevent); } } @@ -486,13 +641,19 @@ static int load_firmware(int fw_fd, int loading_fd, int data_fd) return ret; } +static int is_booting(void) +{ + return access("/dev/.booting", F_OK) == 0; +} + static void process_firmware_event(struct uevent *uevent) { - char *root, *loading, *data, *file; + char *root, *loading, *data, *file1 = NULL, *file2 = NULL; int l, loading_fd, data_fd, fw_fd; + int booting = is_booting(); - log_event_print("firmware event { '%s', '%s' }\n", - uevent->path, uevent->firmware); + INFO("firmware: loading '%s' for '%s'\n", + uevent->firmware, uevent->path); l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); if (l == -1) @@ -506,7 +667,11 @@ static void process_firmware_event(struct uevent *uevent) if (l == -1) goto loading_free_out; - l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware); + l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware); + if (l == -1) + goto data_free_out; + + l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware); if (l == -1) goto data_free_out; @@ -518,14 +683,29 @@ static void process_firmware_event(struct uevent *uevent) if(data_fd < 0) goto loading_close_out; - fw_fd = open(file, O_RDONLY); - if(fw_fd < 0) - goto data_close_out; +try_loading_again: + fw_fd = open(file1, O_RDONLY); + if(fw_fd < 0) { + fw_fd = open(file2, O_RDONLY); + if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); + write(loading_fd, "-1", 2); + goto data_close_out; + } + } if(!load_firmware(fw_fd, loading_fd, data_fd)) - log_event_print("firmware copy success { '%s', '%s' }\n", root, file); + INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); else - log_event_print("firmware copy failure { '%s', '%s' }\n", root, file); + INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); close(fw_fd); data_close_out: @@ -533,7 +713,8 @@ static void process_firmware_event(struct uevent *uevent) loading_close_out: close(loading_fd); file_free_out: - free(file); + free(file1); + free(file2); data_free_out: free(data); loading_free_out: @@ -545,6 +726,7 @@ static void process_firmware_event(struct uevent *uevent) static void handle_firmware_event(struct uevent *uevent) { pid_t pid; + int ret; if(strcmp(uevent->subsystem, "firmware")) return; @@ -561,20 +743,18 @@ static void handle_firmware_event(struct uevent *uevent) } #define UEVENT_MSG_LEN 1024 -void handle_device_fd(int fd) +void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; int n; - - while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { - struct uevent uevent; - - if(n == UEVENT_MSG_LEN) /* overflow -- discard */ + while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { + if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ continue; msg[n] = '\0'; msg[n+1] = '\0'; + struct uevent uevent; parse_event(msg, &uevent); handle_device_event(&uevent); @@ -591,7 +771,7 @@ void handle_device_fd(int fd) ** socket's buffer. */ -static void do_coldboot(int event_fd, DIR *d) +static void do_coldboot(DIR *d) { struct dirent *de; int dfd, fd; @@ -602,7 +782,7 @@ static void do_coldboot(int event_fd, DIR *d) if(fd >= 0) { write(fd, "add\n", 4); close(fd); - handle_device_fd(event_fd); + handle_device_fd(); } while((de = readdir(d))) { @@ -619,40 +799,50 @@ static void do_coldboot(int event_fd, DIR *d) if(d2 == 0) close(fd); else { - do_coldboot(event_fd, d2); + do_coldboot(d2); closedir(d2); } } } -static void coldboot(int event_fd, const char *path) +static void coldboot(const char *path) { DIR *d = opendir(path); if(d) { - do_coldboot(event_fd, d); + do_coldboot(d); closedir(d); } } -int device_init(void) +void device_init(void) { suseconds_t t0, t1; + struct stat info; int fd; - fd = open_uevent_socket(); - if(fd < 0) - return -1; - - fcntl(fd, F_SETFD, FD_CLOEXEC); - fcntl(fd, F_SETFL, O_NONBLOCK); + /* is 64K enough? udev uses 16MB! */ + device_fd = uevent_open_socket(64*1024, true); + if(device_fd < 0) + return; - t0 = get_usecs(); - coldboot(fd, "/sys/class"); - coldboot(fd, "/sys/block"); - coldboot(fd, "/sys/devices"); - t1 = get_usecs(); + fcntl(device_fd, F_SETFD, FD_CLOEXEC); + fcntl(device_fd, F_SETFL, O_NONBLOCK); - log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); + if (stat(coldboot_done, &info) < 0) { + t0 = get_usecs(); + coldboot("/sys/class"); + coldboot("/sys/block"); + coldboot("/sys/devices"); + t1 = get_usecs(); + fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000); + close(fd); + log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); + } else { + log_event_print("skipping coldboot, already done\n"); + } +} - return fd; +int get_device_fd() +{ + return device_fd; } diff --git a/init/devices.h b/init/devices.h index b484da45e3d..a84fa582750 100644 --- a/init/devices.h +++ b/init/devices.h @@ -17,11 +17,12 @@ #ifndef _INIT_DEVICES_H #define _INIT_DEVICES_H -extern void handle_device_fd(int fd); -extern int device_init(void); -extern void qemu_init(void); -extern void qemu_cmdline(const char* name, const char *value); -extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix); +#include +extern void handle_device_fd(); +extern void device_init(void); +extern int add_dev_perms(const char *name, const char *attr, + mode_t perm, unsigned int uid, + unsigned int gid, unsigned short prefix); +int get_device_fd(); #endif /* _INIT_DEVICES_H */ diff --git a/init/init.c b/init/init.c index 8c2a0582e4b..010e19faf07 100644 --- a/init/init.c +++ b/init/init.c @@ -25,26 +25,32 @@ #include #include #include -#include #include #include #include #include #include #include -#include +#include +#include #include +#include +#include #include -#include -#include #include #include "devices.h" #include "init.h" +#include "log.h" #include "property_service.h" #include "bootchart.h" +#include "signal_handler.h" +#include "keychords.h" +#include "init_parser.h" +#include "util.h" +#include "ueventd.h" static int property_triggers_enabled = 0; @@ -52,22 +58,28 @@ static int property_triggers_enabled = 0; static int bootchart_count; #endif +#ifndef BOARD_CHARGING_CMDLINE_NAME +#define BOARD_CHARGING_CMDLINE_NAME "androidboot.battchg_pause" +#define BOARD_CHARGING_CMDLINE_VALUE "true" +#endif + static char console[32]; static char serialno[32]; static char bootmode[32]; +static char battchg_pause[32]; static char baseband[32]; static char carrier[32]; static char bootloader[32]; static char hardware[32]; +static char modelno[32]; static unsigned revision = 0; static char qemu[32]; -static struct input_keychord *keychords = 0; -static int keychords_count = 0; -static int keychords_length = 0; -static void drain_action_queue(void); +static struct action *cur_action = NULL; +static struct command *cur_command = NULL; +static struct listnode *command_queue = NULL; -static void notify_service_state(const char *name, const char *state) +void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); @@ -83,11 +95,15 @@ static time_t process_needs_restart; static const char *ENV[32]; +static unsigned emmc_boot = 0; + +static unsigned charging_mode = 0; + /* add_environment - add "key=value" to the current environment */ int add_environment(const char *key, const char *val) { int n; - + for (n = 0; n < 31; n++) { if (!ENV[n]) { size_t len = strlen(key) + strlen(val) + 2; @@ -123,24 +139,6 @@ static void open_console() close(fd); } -/* - * gettime() - returns the time in seconds of the system's monotonic clock or - * zero on error. - */ -static time_t gettime(void) -{ - struct timespec ts; - int ret; - - ret = clock_gettime(CLOCK_MONOTONIC, &ts); - if (ret < 0) { - ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); - return 0; - } - - return ts.tv_sec; -} - static void publish_socket(const char *name, int fd) { char key[64] = ANDROID_SOCKET_ENV_PREFIX; @@ -163,13 +161,13 @@ void service_start(struct service *svc, const char *dynamic_args) int needs_console; int n; - /* starting a service removes it from the disabled + /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there */ - svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING)); + svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); svc->time_started = 0; - + /* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless @@ -209,23 +207,33 @@ void service_start(struct service *svc, const char *dynamic_args) char tmp[32]; int fd, sz; - get_property_workspace(&fd, &sz); - sprintf(tmp, "%d,%d", dup(fd), sz); - add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); + if (properties_inited()) { + get_property_workspace(&fd, &sz); + sprintf(tmp, "%d,%d", dup(fd), sz); + add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); + } for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); for (si = svc->sockets; si; si = si->next) { - int s = create_socket(si->name, - !strcmp(si->type, "dgram") ? - SOCK_DGRAM : SOCK_STREAM, + int socket_type = ( + !strcmp(si->type, "stream") ? SOCK_STREAM : + (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); + int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid); if (s >= 0) { publish_socket(si->name, s); } } + if (svc->ioprio_class != IoSchedClass_NONE) { + if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { + ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", + getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno)); + } + } + if (needs_console) { setsid(); open_console(); @@ -246,13 +254,22 @@ void service_start(struct service *svc, const char *dynamic_args) /* as requested, set our gid, supplemental gids, and uid */ if (svc->gid) { - setgid(svc->gid); + if (setgid(svc->gid) != 0) { + ERROR("setgid failed: %s\n", strerror(errno)); + _exit(127); + } } if (svc->nr_supp_gids) { - setgroups(svc->nr_supp_gids, svc->supp_gids); + if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) { + ERROR("setgroups failed: %s\n", strerror(errno)); + _exit(127); + } } if (svc->uid) { - setuid(svc->uid); + if (setuid(svc->uid) != 0) { + ERROR("setuid failed: %s\n", strerror(errno)); + _exit(127); + } } if (!dynamic_args) { @@ -260,7 +277,7 @@ void service_start(struct service *svc, const char *dynamic_args) ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { - char *arg_ptrs[SVC_MAXARGS+1]; + char *arg_ptrs[INIT_PARSER_MAXARGS+1]; int arg_idx = svc->nargs; char *tmp = strdup(dynamic_args); char *next = tmp; @@ -271,7 +288,7 @@ void service_start(struct service *svc, const char *dynamic_args) while((bword = strsep(&next, " "))) { arg_ptrs[arg_idx++] = bword; - if (arg_idx == SVC_MAXARGS) + if (arg_idx == INIT_PARSER_MAXARGS) break; } arg_ptrs[arg_idx] = '\0'; @@ -290,115 +307,64 @@ void service_start(struct service *svc, const char *dynamic_args) svc->pid = pid; svc->flags |= SVC_RUNNING; - notify_service_state(svc->name, "running"); + if (properties_inited()) + notify_service_state(svc->name, "running"); } -void service_stop(struct service *svc) +/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ +static void service_stop_or_reset(struct service *svc, int how) { - /* we are no longer running, nor should we - * attempt to restart - */ - svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING)); + /* The service is still SVC_RUNNING until its process exits, but if it has + * already exited it shoudn't attempt a restart yet. */ + svc->flags &= (~SVC_RESTARTING); + if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) { + /* Hrm, an illegal flag. Default to SVC_DISABLED */ + how = SVC_DISABLED; + } /* if the service has not yet started, prevent * it from auto-starting with its class */ - svc->flags |= SVC_DISABLED; + if (how == SVC_RESET) { + svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET; + } else { + svc->flags |= how; + } if (svc->pid) { NOTICE("service '%s' is being killed\n", svc->name); - kill(-svc->pid, SIGTERM); + kill(-svc->pid, SIGKILL); notify_service_state(svc->name, "stopping"); } else { notify_service_state(svc->name, "stopped"); } } -void property_changed(const char *name, const char *value) +void service_reset(struct service *svc) { - if (property_triggers_enabled) { - queue_property_triggers(name, value); - drain_action_queue(); - } + service_stop_or_reset(svc, SVC_RESET); } -#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */ -#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/ - -static int wait_for_one_process(int block) +void service_stop(struct service *svc) { - pid_t pid; - int status; - struct service *svc; - struct socketinfo *si; - time_t now; - struct listnode *node; - struct command *cmd; - - while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); - if (pid <= 0) return -1; - INFO("waitpid returned pid %d, status = %08x\n", pid, status); - - svc = service_find_by_pid(pid); - if (!svc) { - ERROR("untracked pid %d exited\n", pid); - return 0; - } - - NOTICE("process '%s', pid %d exited\n", svc->name, pid); - - if (!(svc->flags & SVC_ONESHOT)) { - kill(-pid, SIGKILL); - NOTICE("process '%s' killing any children in process group\n", svc->name); - } - - /* remove any sockets we may have created */ - for (si = svc->sockets; si; si = si->next) { - char tmp[128]; - snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); - unlink(tmp); - } - - svc->pid = 0; - svc->flags &= (~SVC_RUNNING); - - /* oneshot processes go into the disabled state on exit */ - if (svc->flags & SVC_ONESHOT) { - svc->flags |= SVC_DISABLED; - } - - /* disabled processes do not get restarted automatically */ - if (svc->flags & SVC_DISABLED) { - notify_service_state(svc->name, "stopped"); - return 0; - } + service_stop_or_reset(svc, SVC_DISABLED); +} - now = gettime(); - if (svc->flags & SVC_CRITICAL) { - if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { - if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { - ERROR("critical process '%s' exited %d times in %d minutes; " - "rebooting into recovery mode\n", svc->name, - CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); - sync(); - __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, - LINUX_REBOOT_CMD_RESTART2, "recovery"); - return 0; - } - } else { - svc->time_crashed = now; - svc->nr_crashed = 1; - } - } +void service_restart(struct service *svc) +{ + if (svc->flags & SVC_RUNNING) { + /* Stop, wait, then start the service. */ + service_stop_or_reset(svc, SVC_RESTART); + } else if (!(svc->flags & SVC_RESTARTING)) { + /* Just start the service since it's not running. */ + service_start(svc, NULL); + } /* else: Service is restarting anyways. */ +} - /* Execute all onrestart commands for this service. */ - list_for_each(node, &svc->onrestart.commands) { - cmd = node_to_item(node, struct command, clist); - cmd->func(cmd->nargs, cmd->args); - } - svc->flags |= SVC_RESTARTING; - notify_service_state(svc->name, "restarting"); - return 0; +void property_changed(const char *name, const char *value) +{ + if (property_triggers_enabled) + queue_property_triggers(name, value); } static void restart_service_if_needed(struct service *svc) @@ -424,13 +390,6 @@ static void restart_processes() restart_service_if_needed); } -static int signal_fd = -1; - -static void sigchld_handler(int s) -{ - write(signal_fd, &s, 1); -} - static void msg_start(const char *name) { struct service *svc; @@ -447,7 +406,7 @@ static void msg_start(const char *name) svc = service_find_by_name(tmp); } - + if (svc) { service_start(svc, args); } else { @@ -468,89 +427,30 @@ static void msg_stop(const char *name) } } +static void msg_restart(const char *name) +{ + struct service *svc = service_find_by_name(name); + + if (svc) { + service_restart(svc); + } else { + ERROR("no such service '%s'\n", name); + } +} + void handle_control_message(const char *msg, const char *arg) { if (!strcmp(msg,"start")) { msg_start(arg); } else if (!strcmp(msg,"stop")) { msg_stop(arg); + } else if (!strcmp(msg,"restart")) { + msg_restart(arg); } else { ERROR("unknown control msg '%s'\n", msg); } } -#define MAX_MTD_PARTITIONS 16 - -static struct { - char name[16]; - int number; -} mtd_part_map[MAX_MTD_PARTITIONS]; - -static int mtd_part_count = -1; - -static void find_mtd_partitions(void) -{ - int fd; - char buf[1024]; - char *pmtdbufp; - ssize_t pmtdsize; - int r; - - fd = open("/proc/mtd", O_RDONLY); - if (fd < 0) - return; - - buf[sizeof(buf) - 1] = '\0'; - pmtdsize = read(fd, buf, sizeof(buf) - 1); - pmtdbufp = buf; - while (pmtdsize > 0) { - int mtdnum, mtdsize, mtderasesize; - char mtdname[16]; - mtdname[0] = '\0'; - mtdnum = -1; - r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", - &mtdnum, &mtdsize, &mtderasesize, mtdname); - if ((r == 4) && (mtdname[0] == '"')) { - char *x = strchr(mtdname + 1, '"'); - if (x) { - *x = 0; - } - INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); - if (mtd_part_count < MAX_MTD_PARTITIONS) { - strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); - mtd_part_map[mtd_part_count].number = mtdnum; - mtd_part_count++; - } else { - ERROR("too many mtd partitions\n"); - } - } - while (pmtdsize > 0 && *pmtdbufp != '\n') { - pmtdbufp++; - pmtdsize--; - } - if (pmtdsize > 0) { - pmtdbufp++; - pmtdsize--; - } - } - close(fd); -} - -int mtd_name_to_number(const char *name) -{ - int n; - if (mtd_part_count < 0) { - mtd_part_count = 0; - find_mtd_partitions(); - } - for (n = 0; n < mtd_part_count; n++) { - if (!strcmp(name, mtd_part_map[n].name)) { - return mtd_part_map[n].number; - } - } - return -1; -} - static void import_kernel_nv(char *name, int in_qemu) { char *value = strchr(name, '='); @@ -568,6 +468,8 @@ static void import_kernel_nv(char *name, int in_qemu) strlcpy(console, value, sizeof(console)); } else if (!strcmp(name,"androidboot.mode")) { strlcpy(bootmode, value, sizeof(bootmode)); + } else if (!strcmp(name,BOARD_CHARGING_CMDLINE_NAME)) { + strlcpy(battchg_pause, value, sizeof(battchg_pause)); } else if (!strcmp(name,"androidboot.serialno")) { strlcpy(serialno, value, sizeof(serialno)); } else if (!strcmp(name,"androidboot.baseband")) { @@ -578,10 +480,14 @@ static void import_kernel_nv(char *name, int in_qemu) strlcpy(bootloader, value, sizeof(bootloader)); } else if (!strcmp(name,"androidboot.hardware")) { strlcpy(hardware, value, sizeof(hardware)); - } else { - qemu_cmdline(name, value); + } else if (!strcmp(name,"androidboot.emmc")) { + if (!strcmp(value,"true")) { + emmc_boot = 1; + } + } else if (!strcmp(name,"androidboot.modelno")) { + strlcpy(modelno, value, sizeof(modelno)); } - } else { + } else { /* in the emulator, export any kernel option with the * ro.kernel. prefix */ char buff[32]; @@ -592,220 +498,241 @@ static void import_kernel_nv(char *name, int in_qemu) } } -static void import_kernel_cmdline(int in_qemu) +static struct command *get_first_command(struct action *act) { - char cmdline[1024]; - char *ptr; - int fd; + struct listnode *node; + node = list_head(&act->commands); + if (!node || list_empty(&act->commands)) + return NULL; + + return node_to_item(node, struct command, clist); +} + +static struct command *get_next_command(struct action *act, struct command *cmd) +{ + struct listnode *node; + node = cmd->clist.next; + if (!node) + return NULL; + if (node == &act->commands) + return NULL; + + return node_to_item(node, struct command, clist); +} - fd = open("/proc/cmdline", O_RDONLY); - if (fd >= 0) { - int n = read(fd, cmdline, 1023); - if (n < 0) n = 0; +static int is_last_command(struct action *act, struct command *cmd) +{ + return (list_tail(&act->commands) == &cmd->clist); +} - /* get rid of trailing newline, it happens */ - if (n > 0 && cmdline[n-1] == '\n') n--; +void execute_one_command(void) +{ + int ret; - cmdline[n] = 0; - close(fd); + if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { + cur_action = action_remove_queue_head(); + cur_command = NULL; + if (!cur_action) + return; + INFO("processing action %p (%s)\n", cur_action, cur_action->name); + cur_command = get_first_command(cur_action); } else { - cmdline[0] = 0; + cur_command = get_next_command(cur_action, cur_command); } - ptr = cmdline; - while (ptr && *ptr) { - char *x = strchr(ptr, ' '); - if (x != 0) *x++ = 0; - import_kernel_nv(ptr, in_qemu); - ptr = x; - } + if (!cur_command) + return; - /* don't expose the raw commandline to nonpriv processes */ - chmod("/proc/cmdline", 0440); + ret = cur_command->func(cur_command->nargs, cur_command->args); + INFO("command '%s' r=%d\n", cur_command->args[0], ret); } -static void get_hardware_name(void) +static int wait_for_coldboot_done_action(int nargs, char **args) { - char data[1024]; - int fd, n; - char *x, *hw, *rev; - - /* Hardware string was provided on kernel command line */ - if (hardware[0]) - return; + int ret; + INFO("wait for %s\n", coldboot_done); + ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT); + if (ret) + ERROR("Timed out waiting for %s\n", coldboot_done); + return ret; +} - fd = open("/proc/cpuinfo", O_RDONLY); - if (fd < 0) return; +static int charging_mode_booting(void) +{ +#ifndef BOARD_CHARGING_MODE_BOOTING_LPM + return 0; +#else + int f; + char cmb; + f = open(BOARD_CHARGING_MODE_BOOTING_LPM, O_RDONLY); + if (f < 0) + return 0; - n = read(fd, data, 1023); - close(fd); - if (n < 0) return; - - data[n] = 0; - hw = strstr(data, "\nHardware"); - rev = strstr(data, "\nRevision"); - - if (hw) { - x = strstr(hw, ": "); - if (x) { - x += 2; - n = 0; - while (*x && !isspace(*x)) { - hardware[n++] = tolower(*x); - x++; - if (n == 31) break; - } - hardware[n] = 0; - } - } + if (1 != read(f, (void *)&cmb,1)) + return 0; - if (rev) { - x = strstr(rev, ": "); - if (x) { - revision = strtoul(x + 2, 0, 16); - } - } + close(f); + return ('1' == cmb); +#endif } -static void drain_action_queue(void) +static int property_init_action(int nargs, char **args) { - struct listnode *node; - struct command *cmd; - struct action *act; - int ret; + bool load_defaults = true; - while ((act = action_remove_queue_head())) { - INFO("processing action %p (%s)\n", act, act->name); - list_for_each(node, &act->commands) { - cmd = node_to_item(node, struct command, clist); - ret = cmd->func(cmd->nargs, cmd->args); - INFO("command '%s' r=%d\n", cmd->args[0], ret); - } - } + INFO("property init\n"); + if (charging_mode) + load_defaults = false; + property_init(load_defaults); + return 0; +} + +static int keychord_init_action(int nargs, char **args) +{ + keychord_init(); + return 0; } -void open_devnull_stdio(void) +static int console_init_action(int nargs, char **args) { int fd; - static const char *name = "/dev/__null__"; - if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { - fd = open(name, O_RDWR); - unlink(name); + char tmp[PROP_VALUE_MAX]; + + if (console[0]) { + snprintf(tmp, sizeof(tmp), "/dev/%s", console); + console_name = strdup(tmp); + } + + fd = open(console_name, O_RDWR); + if (fd >= 0) + have_console = 1; + close(fd); + + if( load_565rle_image(INIT_IMAGE_FILE) ) { + fd = open("/dev/tty0", O_WRONLY); if (fd >= 0) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) { - close(fd); - } - return; + const char *msg; + msg = "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" // console is 40 cols x 30 lines + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " A N D R O I D "; + write(fd, msg, strlen(msg)); + close(fd); } } - - exit(1); + return 0; } -void add_service_keycodes(struct service *svc) +static int set_init_properties_action(int nargs, char **args) { - struct input_keychord *keychord; - int i, size; - - if (svc->keycodes) { - /* add a new keychord to the list */ - size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); - keychords = realloc(keychords, keychords_length + size); - if (!keychords) { - ERROR("could not allocate keychords\n"); - keychords_length = 0; - keychords_count = 0; - return; - } + char tmp[PROP_VALUE_MAX]; - keychord = (struct input_keychord *)((char *)keychords + keychords_length); - keychord->version = KEYCHORD_VERSION; - keychord->id = keychords_count + 1; - keychord->count = svc->nkeycodes; - svc->keychord_id = keychord->id; + if (qemu[0]) + import_kernel_cmdline(1, import_kernel_nv); - for (i = 0; i < svc->nkeycodes; i++) { - keychord->keycodes[i] = svc->keycodes[i]; - } - keychords_count++; - keychords_length += size; - } + if (!strcmp(bootmode,"factory")) + property_set("ro.factorytest", "1"); + else if (!strcmp(bootmode,"factory2")) + property_set("ro.factorytest", "2"); + else + property_set("ro.factorytest", "0"); + + property_set("ro.serialno", serialno[0] ? serialno : ""); + property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); + property_set("ro.baseband", baseband[0] ? baseband : "unknown"); + property_set("ro.carrier", carrier[0] ? carrier : "unknown"); + property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); + + if (modelno[0]) + property_set("ro.boot.modelno", modelno); + + property_set("ro.hardware", hardware); + snprintf(tmp, PROP_VALUE_MAX, "%d", revision); + property_set("ro.revision", tmp); + property_set("ro.emmc",emmc_boot ? "1" : "0"); + return 0; } -int open_keychord() +static int property_service_init_action(int nargs, char **args) { - int fd, ret; - - service_for_each(add_service_keycodes); - - /* nothing to do if no services require keychords */ - if (!keychords) - return -1; - - fd = open("/dev/keychord", O_RDWR); - if (fd < 0) { - ERROR("could not open /dev/keychord\n"); - return fd; - } - fcntl(fd, F_SETFD, FD_CLOEXEC); + /* read any property files on system or data and + * fire up the property service. This must happen + * after the ro.foo properties are set above so + * that /data/local.prop cannot interfere with them. + */ + start_property_service(); + return 0; +} - ret = write(fd, keychords, keychords_length); - if (ret != keychords_length) { - ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); - close(fd); - fd = -1; +static int signal_init_action(int nargs, char **args) +{ + signal_init(); + return 0; +} + +static int check_startup_action(int nargs, char **args) +{ + /* make sure we actually have all the pieces we need */ + if ((get_property_set_fd() < 0) || + (get_signal_fd() < 0)) { + ERROR("init startup failure\n"); + exit(1); } - free(keychords); - keychords = 0; + /* signal that we hit this point */ + unlink("/dev/.booting"); - return fd; + return 0; } -void handle_keychord(int fd) +static int queue_property_triggers_action(int nargs, char **args) { - struct service *svc; - int ret; - __u16 id; - - ret = read(fd, &id, sizeof(id)); - if (ret != sizeof(id)) { - ERROR("could not read keychord id\n"); - return; - } + queue_all_property_triggers(); + /* enable property triggers */ + property_triggers_enabled = 1; + return 0; +} - svc = service_find_by_keychord(id); - if (svc) { - INFO("starting service %s from keychord\n", svc->name); - service_start(svc, NULL); +#if BOOTCHART +static int bootchart_init_action(int nargs, char **args) +{ + bootchart_count = bootchart_init(); + if (bootchart_count < 0) { + ERROR("bootcharting init failure\n"); + } else if (bootchart_count > 0) { + NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); } else { - ERROR("service for keychord %d not found\n", id); + NOTICE("bootcharting ignored\n"); } + + return 0; } +#endif int main(int argc, char **argv) { - int device_fd = -1; - int property_set_fd = -1; - int signal_recv_fd = -1; - int keychord_fd = -1; - int fd_count; - int s[2]; - int fd; - struct sigaction act; - char tmp[PROP_VALUE_MAX]; + int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; + char tmp[32]; + int property_set_fd_init = 0; + int signal_fd_init = 0; + int keychord_fd_init = 0; - act.sa_handler = sigchld_handler; - act.sa_flags = SA_NOCLDSTOP; - act.sa_mask = 0; - act.sa_restorer = NULL; - sigaction(SIGCHLD, &act, 0); + if (!strcmp(basename(argv[0]), "ueventd")) + return ueventd_main(argc, argv); /* clear the umask */ umask(0); @@ -818,13 +745,16 @@ int main(int argc, char **argv) mkdir("/proc", 0755); mkdir("/sys", 0755); - mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755"); + mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); + /* indicate that booting is in progress to background fw loaders, etc */ + close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); + /* We must have some place other than / to create the * device nodes for kmsg and null, otherwise we won't * be able to remount / read-only later on. @@ -832,171 +762,121 @@ int main(int argc, char **argv) * talk to the outside world. */ open_devnull_stdio(); - log_init(); - + klog_init(); + INFO("reading config file\n"); - parse_config_file("/init.rc"); - /* pull the kernel commandline and ramdisk properties file in */ - qemu_init(); - import_kernel_cmdline(0); + if (!charging_mode_booting()) + init_parse_config_file("/init.rc"); + else + init_parse_config_file("/lpm.rc"); - get_hardware_name(); - snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); - parse_config_file(tmp); + /* pull the kernel commandline and ramdisk properties file in */ + import_kernel_cmdline(0, import_kernel_nv); + /* don't expose the raw commandline to nonpriv processes */ + chmod("/proc/cmdline", 0440); - action_for_each_trigger("early-init", action_add_queue_tail); - drain_action_queue(); + get_hardware_name(hardware, &revision); - INFO("device init\n"); - device_fd = device_init(); + if (strcmp(bootmode, "charger") == 0 || strcmp(battchg_pause, BOARD_CHARGING_CMDLINE_VALUE) == 0) + charging_mode = 1; - property_init(); - - // only listen for keychords if ro.debuggable is true - debuggable = property_get("ro.debuggable"); - if (debuggable && !strcmp(debuggable, "1")) { - keychord_fd = open_keychord(); - } + if (!charging_mode_booting()) { + snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); + init_parse_config_file(tmp); - if (console[0]) { - snprintf(tmp, sizeof(tmp), "/dev/%s", console); - console_name = strdup(tmp); - } - - fd = open(console_name, O_RDWR); - if (fd >= 0) - have_console = 1; - close(fd); + /* Check for an emmc initialisation file and read if present */ + if (emmc_boot && access("/init.emmc.rc", R_OK) == 0) { + INFO("Reading emmc config file"); + init_parse_config_file("/init.emmc.rc"); + } - if( load_565rle_image(INIT_IMAGE_FILE) ) { - fd = open("/dev/tty0", O_WRONLY); - if (fd >= 0) { - const char *msg; - msg = "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" // console is 40 cols x 30 lines - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - " A N D R O I D "; - write(fd, msg, strlen(msg)); - close(fd); - } + /* Check for a target specific initialisation file and read if present */ + if (access("/init.target.rc", R_OK) == 0) { + INFO("Reading target specific config file"); + init_parse_config_file("/init.target.rc"); + } } - if (qemu[0]) - import_kernel_cmdline(1); - - if (!strcmp(bootmode,"factory")) - property_set("ro.factorytest", "1"); - else if (!strcmp(bootmode,"factory2")) - property_set("ro.factorytest", "2"); - else - property_set("ro.factorytest", "0"); - - property_set("ro.serialno", serialno[0] ? serialno : ""); - property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); - property_set("ro.baseband", baseband[0] ? baseband : "unknown"); - property_set("ro.carrier", carrier[0] ? carrier : "unknown"); - property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); + action_for_each_trigger("early-init", action_add_queue_tail); - property_set("ro.hardware", hardware); - snprintf(tmp, PROP_VALUE_MAX, "%d", revision); - property_set("ro.revision", tmp); + queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); + queue_builtin_action(property_init_action, "property_init"); + queue_builtin_action(keychord_init_action, "keychord_init"); + queue_builtin_action(console_init_action, "console_init"); + queue_builtin_action(set_init_properties_action, "set_init_properties"); - /* execute all the boot actions to get us started */ + /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); - drain_action_queue(); - /* read any property files on system or data and - * fire up the property service. This must happen - * after the ro.foo properties are set above so - * that /data/local.prop cannot interfere with them. - */ - property_set_fd = start_property_service(); - - /* create a signalling mechanism for the sigchld handler */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { - signal_fd = s[0]; - signal_recv_fd = s[1]; - fcntl(s[0], F_SETFD, FD_CLOEXEC); - fcntl(s[0], F_SETFL, O_NONBLOCK); - fcntl(s[1], F_SETFD, FD_CLOEXEC); - fcntl(s[1], F_SETFL, O_NONBLOCK); - } - - /* make sure we actually have all the pieces we need */ - if ((device_fd < 0) || - (property_set_fd < 0) || - (signal_recv_fd < 0)) { - ERROR("init startup failure\n"); - return 1; + /* skip mounting filesystems in charger mode */ + if (strcmp(bootmode, "charger") != 0) { + action_for_each_trigger("early-fs", action_add_queue_tail); + if (emmc_boot) { + action_for_each_trigger("emmc-fs", action_add_queue_tail); + } else { + action_for_each_trigger("fs", action_add_queue_tail); + } + action_for_each_trigger("post-fs", action_add_queue_tail); + action_for_each_trigger("post-fs-data", action_add_queue_tail); } - /* execute all the boot actions to get us started */ - action_for_each_trigger("early-boot", action_add_queue_tail); - action_for_each_trigger("boot", action_add_queue_tail); - drain_action_queue(); + queue_builtin_action(property_service_init_action, "property_service_init"); + queue_builtin_action(signal_init_action, "signal_init"); + queue_builtin_action(check_startup_action, "check_startup"); - /* run all property triggers based on current state of the properties */ - queue_all_property_triggers(); - drain_action_queue(); - - /* enable property triggers */ - property_triggers_enabled = 1; - - ufds[0].fd = device_fd; - ufds[0].events = POLLIN; - ufds[1].fd = property_set_fd; - ufds[1].events = POLLIN; - ufds[2].fd = signal_recv_fd; - ufds[2].events = POLLIN; - fd_count = 3; - - if (keychord_fd > 0) { - ufds[3].fd = keychord_fd; - ufds[3].events = POLLIN; - fd_count++; + if (charging_mode) { + action_for_each_trigger("charger", action_add_queue_tail); } else { - ufds[3].events = 0; - ufds[3].revents = 0; + action_for_each_trigger("early-boot", action_add_queue_tail); + action_for_each_trigger("boot", action_add_queue_tail); } + /* run all property triggers based on current state of the properties */ + queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); + + #if BOOTCHART - bootchart_count = bootchart_init(); - if (bootchart_count < 0) { - ERROR("bootcharting init failure\n"); - } else if (bootchart_count > 0) { - NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); - } else { - NOTICE("bootcharting ignored\n"); - } + queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif for(;;) { int nr, i, timeout = -1; - for (i = 0; i < fd_count; i++) - ufds[i].revents = 0; - - drain_action_queue(); + execute_one_command(); restart_processes(); + if (!property_set_fd_init && get_property_set_fd() > 0) { + ufds[fd_count].fd = get_property_set_fd(); + ufds[fd_count].events = POLLIN; + ufds[fd_count].revents = 0; + fd_count++; + property_set_fd_init = 1; + } + if (!signal_fd_init && get_signal_fd() > 0) { + ufds[fd_count].fd = get_signal_fd(); + ufds[fd_count].events = POLLIN; + ufds[fd_count].revents = 0; + fd_count++; + signal_fd_init = 1; + } + if (!keychord_fd_init && get_keychord_fd() > 0) { + ufds[fd_count].fd = get_keychord_fd(); + ufds[fd_count].events = POLLIN; + ufds[fd_count].revents = 0; + fd_count++; + keychord_fd_init = 1; + } + if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } + if (!action_queue_empty() || cur_action) + timeout = 0; + #if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) @@ -1007,25 +887,21 @@ int main(int argc, char **argv) } } #endif + nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; - if (ufds[2].revents == POLLIN) { - /* we got a SIGCHLD - reap and restart as needed */ - read(signal_recv_fd, tmp, sizeof(tmp)); - while (!wait_for_one_process(0)) - ; - continue; + for (i = 0; i < fd_count; i++) { + if (ufds[i].revents == POLLIN) { + if (ufds[i].fd == get_property_set_fd()) + handle_property_set_fd(); + else if (ufds[i].fd == get_keychord_fd()) + handle_keychord(); + else if (ufds[i].fd == get_signal_fd()) + handle_signal(); + } } - - if (ufds[0].revents == POLLIN) - handle_device_fd(device_fd); - - if (ufds[1].revents == POLLIN) - handle_property_set_fd(property_set_fd); - if (ufds[3].revents == POLLIN) - handle_keychord(keychord_fd); } return 0; diff --git a/init/init.h b/init/init.h index f306b7bcaf7..41f085f22a2 100644 --- a/init/init.h +++ b/init/init.h @@ -17,55 +17,11 @@ #ifndef _INIT_INIT_H #define _INIT_INIT_H -int mtd_name_to_number(const char *name); +#include -void handle_control_message(const char *msg, const char *arg); - -int create_socket(const char *name, int type, mode_t perm, - uid_t uid, gid_t gid); - -void *read_file(const char *fn, unsigned *_sz); - -void log_init(void); -void log_set_level(int level); -void log_close(void); -void log_write(int level, const char *fmt, ...) - __attribute__ ((format(printf, 2, 3))); - -#define ERROR(x...) log_write(3, "<3>init: " x) -#define NOTICE(x...) log_write(5, "<5>init: " x) -#define INFO(x...) log_write(6, "<6>init: " x) +#include -#define LOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ -#define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ - -unsigned int decode_uid(const char *s); - -struct listnode -{ - struct listnode *next; - struct listnode *prev; -}; - -#define node_to_item(node, container, member) \ - (container *) (((char*) (node)) - offsetof(container, member)) - -#define list_declare(name) \ - struct listnode name = { \ - .next = &name, \ - .prev = &name, \ - } - -#define list_for_each(node, list) \ - for (node = (list)->next; node != (list); node = node->next) - -void list_init(struct listnode *list); -void list_add_tail(struct listnode *list, struct listnode *item); -void list_remove(struct listnode *item); - -#define list_empty(list) ((list) == (list)->next) -#define list_head(list) ((list)->next) -#define list_tail(list) ((list)->prev) +void handle_control_message(const char *msg, const char *arg); struct command { @@ -113,10 +69,14 @@ struct svcenvinfo { #define SVC_RESTARTING 0x08 /* waiting to restart */ #define SVC_CONSOLE 0x10 /* requires console */ #define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */ +#define SVC_RESET 0x40 /* Use when stopping a process, but not disabling + so it can be restarted with its class */ +#define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ +#define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ -#define NR_SVC_SUPP_GIDS 6 /* six supplementary groups */ +#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ -#define SVC_MAXARGS 64 +#define COMMAND_RETRY_TIMEOUT 5 struct service { /* list of all services */ @@ -146,12 +106,15 @@ struct service { int nkeycodes; int keychord_id; + int ioprio_class; + int ioprio_pri; + int nargs; /* "MUST BE AT THE END OF THE STRUCT" */ char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */ -int parse_config_file(const char *fn); +void notify_service_state(const char *name, const char *state); struct service *service_find_by_name(const char *name); struct service *service_find_by_pid(pid_t pid); @@ -162,16 +125,11 @@ void service_for_each_class(const char *classname, void service_for_each_flags(unsigned matchflags, void (*func)(struct service *svc)); void service_stop(struct service *svc); +void service_reset(struct service *svc); +void service_restart(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); -struct action *action_remove_queue_head(void); -void action_add_queue_tail(struct action *act); -void action_for_each_trigger(const char *trigger, - void (*func)(struct action *act)); -void queue_property_triggers(const char *name, const char *value); -void queue_all_property_triggers(); - #define INIT_IMAGE_FILE "/initlogo.rle" int load_565rle_image( char *file_name ); diff --git a/init/init_parser.c b/init/init_parser.c new file mode 100644 index 00000000000..9dced9f89e6 --- /dev/null +++ b/init/init_parser.c @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "parser.h" +#include "init_parser.h" +#include "log.h" +#include "property_service.h" +#include "util.h" + +#include +#include + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include + +static list_declare(service_list); +static list_declare(action_list); +static list_declare(action_queue); + +static void *parse_service(struct parse_state *state, int nargs, char **args); +static void parse_line_service(struct parse_state *state, int nargs, char **args); + +static void *parse_action(struct parse_state *state, int nargs, char **args); +static void parse_line_action(struct parse_state *state, int nargs, char **args); + +#define SECTION 0x01 +#define COMMAND 0x02 +#define OPTION 0x04 + +#include "keywords.h" + +#define KEYWORD(symbol, flags, nargs, func) \ + [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, + +struct { + const char *name; + int (*func)(int nargs, char **args); + unsigned char nargs; + unsigned char flags; +} keyword_info[KEYWORD_COUNT] = { + [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, +#include "keywords.h" +}; +#undef KEYWORD + +#define kw_is(kw, type) (keyword_info[kw].flags & (type)) +#define kw_name(kw) (keyword_info[kw].name) +#define kw_func(kw) (keyword_info[kw].func) +#define kw_nargs(kw) (keyword_info[kw].nargs) + +int lookup_keyword(const char *s) +{ + switch (*s++) { + case 'c': + if (!strcmp(s, "opy")) return K_copy; + if (!strcmp(s, "apability")) return K_capability; + if (!strcmp(s, "hdir")) return K_chdir; + if (!strcmp(s, "hroot")) return K_chroot; + if (!strcmp(s, "lass")) return K_class; + if (!strcmp(s, "lass_start")) return K_class_start; + if (!strcmp(s, "lass_stop")) return K_class_stop; + if (!strcmp(s, "lass_reset")) return K_class_reset; + if (!strcmp(s, "onsole")) return K_console; + if (!strcmp(s, "hown")) return K_chown; + if (!strcmp(s, "hmod")) return K_chmod; + if (!strcmp(s, "ritical")) return K_critical; + break; + case 'd': + if (!strcmp(s, "isabled")) return K_disabled; + if (!strcmp(s, "omainname")) return K_domainname; + break; + case 'e': + if (!strcmp(s, "xec")) return K_exec; + if (!strcmp(s, "xport")) return K_export; + break; + case 'g': + if (!strcmp(s, "roup")) return K_group; + break; + case 'h': + if (!strcmp(s, "ostname")) return K_hostname; + break; + case 'i': + if (!strcmp(s, "oprio")) return K_ioprio; + if (!strcmp(s, "fup")) return K_ifup; + if (!strcmp(s, "nsmod")) return K_insmod; + if (!strcmp(s, "mport")) return K_import; + break; + case 'k': + if (!strcmp(s, "eycodes")) return K_keycodes; + break; + case 'l': + if (!strcmp(s, "oglevel")) return K_loglevel; + if (!strcmp(s, "og")) return K_log; + if (!strcmp(s, "oad_persist_props")) return K_load_persist_props; + break; + case 'm': + if (!strcmp(s, "kdir")) return K_mkdir; + if (!strcmp(s, "ount")) return K_mount; + break; + case 'o': + if (!strcmp(s, "n")) return K_on; + if (!strcmp(s, "neshot")) return K_oneshot; + if (!strcmp(s, "nrestart")) return K_onrestart; + break; + case 'r': + if (!strcmp(s, "estart")) return K_restart; + if (!strcmp(s, "mdir")) return K_rmdir; + if (!strcmp(s, "m")) return K_rm; + break; + case 's': + if (!strcmp(s, "ervice")) return K_service; + if (!strcmp(s, "etenv")) return K_setenv; + if (!strcmp(s, "etkey")) return K_setkey; + if (!strcmp(s, "etprop")) return K_setprop; + if (!strcmp(s, "etrlimit")) return K_setrlimit; + if (!strcmp(s, "ocket")) return K_socket; + if (!strcmp(s, "tart")) return K_start; + if (!strcmp(s, "top")) return K_stop; + if (!strcmp(s, "ymlink")) return K_symlink; + if (!strcmp(s, "ysclktz")) return K_sysclktz; + break; + case 't': + if (!strcmp(s, "rigger")) return K_trigger; + break; + case 'u': + if (!strcmp(s, "ser")) return K_user; + break; + case 'w': + if (!strcmp(s, "rite")) return K_write; + if (!strcmp(s, "ait")) return K_wait; + break; + } + return K_UNKNOWN; +} + +void parse_line_no_op(struct parse_state *state, int nargs, char **args) +{ +} + +void parse_new_section(struct parse_state *state, int kw, + int nargs, char **args) +{ + printf("[ %s %s ]\n", args[0], + nargs > 1 ? args[1] : ""); + switch(kw) { + case K_service: + state->context = parse_service(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_service; + return; + } + break; + case K_on: + state->context = parse_action(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_action; + return; + } + break; + case K_import: + if (nargs != 2) { + ERROR("single argument needed for import\n"); + } else { + int ret = init_parse_config_file(args[1]); + if (ret) + ERROR("could not import file %s\n", args[1]); + } + } + state->parse_line = parse_line_no_op; +} + +static void parse_config(const char *fn, char *s) +{ + struct parse_state state; + char *args[INIT_PARSER_MAXARGS]; + int nargs; + + nargs = 0; + state.filename = fn; + state.line = 0; + state.ptr = s; + state.nexttoken = 0; + state.parse_line = parse_line_no_op; + for (;;) { + switch (next_token(&state)) { + case T_EOF: + state.parse_line(&state, 0, 0); + return; + case T_NEWLINE: + state.line++; + if (nargs) { + int kw = lookup_keyword(args[0]); + if (kw_is(kw, SECTION)) { + state.parse_line(&state, 0, 0); + parse_new_section(&state, kw, nargs, args); + } else { + state.parse_line(&state, nargs, args); + } + nargs = 0; + } + break; + case T_TEXT: + if (nargs < INIT_PARSER_MAXARGS) { + args[nargs++] = state.text; + } + break; + } + } +} + +int init_parse_config_file(const char *fn) +{ + char *data; + data = read_file(fn, 0); + if (!data) return -1; + + parse_config(fn, data); + DUMP(); + return 0; +} + +static int valid_name(const char *name) +{ + if (strlen(name) > 16) { + return 0; + } + while (*name) { + if (!isalnum(*name) && (*name != '_') && (*name != '-')) { + return 0; + } + name++; + } + return 1; +} + +struct service *service_find_by_name(const char *name) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (!strcmp(svc->name, name)) { + return svc; + } + } + return 0; +} + +struct service *service_find_by_pid(pid_t pid) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->pid == pid) { + return svc; + } + } + return 0; +} + +struct service *service_find_by_keychord(int keychord_id) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->keychord_id == keychord_id) { + return svc; + } + } + return 0; +} + +void service_for_each(void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + func(svc); + } +} + +void service_for_each_class(const char *classname, + void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (!strcmp(svc->classname, classname)) { + func(svc); + } + } +} + +void service_for_each_flags(unsigned matchflags, + void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->flags & matchflags) { + func(svc); + } + } +} + +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)) +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strcmp(act->name, trigger)) { + func(act); + } + } +} + +void queue_property_triggers(const char *name, const char *value) +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strncmp(act->name, "property:", strlen("property:"))) { + const char *test = act->name + strlen("property:"); + int name_length = strlen(name); + + if (!strncmp(name, test, name_length) && + test[name_length] == '=' && + (!strcmp(test + name_length + 1, value) || + !strcmp(test + name_length + 1, "*"))) { + action_add_queue_tail(act); + } + } + } +} + +void queue_all_property_triggers() +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strncmp(act->name, "property:", strlen("property:"))) { + /* parse property name and value + syntax is property:= */ + const char* name = act->name + strlen("property:"); + const char* equals = strchr(name, '='); + if (equals) { + char prop_name[PROP_NAME_MAX + 1]; + const char* value; + int length = equals - name; + if (length > PROP_NAME_MAX) { + ERROR("property name too long in trigger %s", act->name); + } else { + memcpy(prop_name, name, length); + prop_name[length] = 0; + + /* does the property exist, and match the trigger value? */ + value = property_get(prop_name); + if (value && (!strcmp(equals + 1, value) || + !strcmp(equals + 1, "*"))) { + action_add_queue_tail(act); + } + } + } + } + } +} + +void queue_builtin_action(int (*func)(int nargs, char **args), char *name) +{ + struct action *act; + struct command *cmd; + + act = calloc(1, sizeof(*act)); + act->name = name; + list_init(&act->commands); + + cmd = calloc(1, sizeof(*cmd)); + cmd->func = func; + cmd->args[0] = name; + list_add_tail(&act->commands, &cmd->clist); + + list_add_tail(&action_list, &act->alist); + action_add_queue_tail(act); +} + +void action_add_queue_tail(struct action *act) +{ + list_add_tail(&action_queue, &act->qlist); +} + +struct action *action_remove_queue_head(void) +{ + if (list_empty(&action_queue)) { + return 0; + } else { + struct listnode *node = list_head(&action_queue); + struct action *act = node_to_item(node, struct action, qlist); + list_remove(node); + return act; + } +} + +int action_queue_empty() +{ + return list_empty(&action_queue); +} + +static void *parse_service(struct parse_state *state, int nargs, char **args) +{ + struct service *svc; + if (nargs < 3) { + parse_error(state, "services must have a name and a program\n"); + return 0; + } + if (!valid_name(args[1])) { + parse_error(state, "invalid service name '%s'\n", args[1]); + return 0; + } + + svc = service_find_by_name(args[1]); + if (svc) { + parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); + return 0; + } + + nargs -= 2; + svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); + if (!svc) { + parse_error(state, "out of memory\n"); + return 0; + } + svc->name = args[1]; + svc->classname = "default"; + memcpy(svc->args, args + 2, sizeof(char*) * nargs); + svc->args[nargs] = 0; + svc->nargs = nargs; + svc->onrestart.name = "onrestart"; + list_init(&svc->onrestart.commands); + list_add_tail(&service_list, &svc->slist); + return svc; +} + +static void parse_line_service(struct parse_state *state, int nargs, char **args) +{ + struct service *svc = state->context; + struct command *cmd; + int i, kw, kw_nargs; + + if (nargs == 0) { + return; + } + + svc->ioprio_class = IoSchedClass_NONE; + + kw = lookup_keyword(args[0]); + switch (kw) { + case K_capability: + break; + case K_class: + if (nargs != 2) { + parse_error(state, "class option requires a classname\n"); + } else { + svc->classname = args[1]; + } + break; + case K_console: + svc->flags |= SVC_CONSOLE; + break; + case K_disabled: + svc->flags |= SVC_DISABLED; + svc->flags |= SVC_RC_DISABLED; + break; + case K_ioprio: + if (nargs != 3) { + parse_error(state, "ioprio optin usage: ioprio \n"); + } else { + svc->ioprio_pri = strtoul(args[2], 0, 8); + + if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) { + parse_error(state, "priority value must be range 0 - 7\n"); + break; + } + + if (!strcmp(args[1], "rt")) { + svc->ioprio_class = IoSchedClass_RT; + } else if (!strcmp(args[1], "be")) { + svc->ioprio_class = IoSchedClass_BE; + } else if (!strcmp(args[1], "idle")) { + svc->ioprio_class = IoSchedClass_IDLE; + } else { + parse_error(state, "ioprio option usage: ioprio <0-7>\n"); + } + } + break; + case K_group: + if (nargs < 2) { + parse_error(state, "group option requires a group id\n"); + } else if (nargs > NR_SVC_SUPP_GIDS + 2) { + parse_error(state, "group option accepts at most %d supp. groups\n", + NR_SVC_SUPP_GIDS); + } else { + int n; + svc->gid = decode_uid(args[1]); + for (n = 2; n < nargs; n++) { + svc->supp_gids[n-2] = decode_uid(args[n]); + } + svc->nr_supp_gids = n - 2; + } + break; + case K_keycodes: + if (nargs < 2) { + parse_error(state, "keycodes option requires atleast one keycode\n"); + } else { + svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0])); + if (!svc->keycodes) { + parse_error(state, "could not allocate keycodes\n"); + } else { + svc->nkeycodes = nargs - 1; + for (i = 1; i < nargs; i++) { + svc->keycodes[i - 1] = atoi(args[i]); + } + } + } + break; + case K_oneshot: + svc->flags |= SVC_ONESHOT; + break; + case K_onrestart: + nargs--; + args++; + kw = lookup_keyword(args[0]); + if (!kw_is(kw, COMMAND)) { + parse_error(state, "invalid command '%s'\n", args[0]); + break; + } + kw_nargs = kw_nargs(kw); + if (nargs < kw_nargs) { + parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, + kw_nargs > 2 ? "arguments" : "argument"); + break; + } + + cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); + cmd->func = kw_func(kw); + cmd->nargs = nargs; + memcpy(cmd->args, args, sizeof(char*) * nargs); + list_add_tail(&svc->onrestart.commands, &cmd->clist); + break; + case K_critical: + svc->flags |= SVC_CRITICAL; + break; + case K_setenv: { /* name value */ + struct svcenvinfo *ei; + if (nargs < 2) { + parse_error(state, "setenv option requires name and value arguments\n"); + break; + } + ei = calloc(1, sizeof(*ei)); + if (!ei) { + parse_error(state, "out of memory\n"); + break; + } + ei->name = args[1]; + ei->value = args[2]; + ei->next = svc->envvars; + svc->envvars = ei; + break; + } + case K_socket: {/* name type perm [ uid gid ] */ + struct socketinfo *si; + if (nargs < 4) { + parse_error(state, "socket option requires name, type, perm arguments\n"); + break; + } + if (strcmp(args[2],"dgram") && strcmp(args[2],"stream") + && strcmp(args[2],"seqpacket")) { + parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n"); + break; + } + si = calloc(1, sizeof(*si)); + if (!si) { + parse_error(state, "out of memory\n"); + break; + } + si->name = args[1]; + si->type = args[2]; + si->perm = strtoul(args[3], 0, 8); + if (nargs > 4) + si->uid = decode_uid(args[4]); + if (nargs > 5) + si->gid = decode_uid(args[5]); + si->next = svc->sockets; + svc->sockets = si; + break; + } + case K_user: + if (nargs != 2) { + parse_error(state, "user option requires a user id\n"); + } else { + svc->uid = decode_uid(args[1]); + } + break; + default: + parse_error(state, "invalid option '%s'\n", args[0]); + } +} + +static void *parse_action(struct parse_state *state, int nargs, char **args) +{ + struct action *act; + if (nargs < 2) { + parse_error(state, "actions must have a trigger\n"); + return 0; + } + if (nargs > 2) { + parse_error(state, "actions may not have extra parameters\n"); + return 0; + } + act = calloc(1, sizeof(*act)); + act->name = args[1]; + list_init(&act->commands); + list_add_tail(&action_list, &act->alist); + /* XXX add to hash */ + return act; +} + +static void parse_line_action(struct parse_state* state, int nargs, char **args) +{ + struct command *cmd; + struct action *act = state->context; + int (*func)(int nargs, char **args); + int kw, n; + + if (nargs == 0) { + return; + } + + kw = lookup_keyword(args[0]); + if (!kw_is(kw, COMMAND)) { + parse_error(state, "invalid command '%s'\n", args[0]); + return; + } + + n = kw_nargs(kw); + if (nargs < n) { + parse_error(state, "%s requires %d %s\n", args[0], n - 1, + n > 2 ? "arguments" : "argument"); + return; + } + cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); + cmd->func = kw_func(kw); + cmd->nargs = nargs; + memcpy(cmd->args, args, sizeof(char*) * nargs); + list_add_tail(&act->commands, &cmd->clist); +} diff --git a/init/init_parser.h b/init/init_parser.h new file mode 100644 index 00000000000..ff13b04f231 --- /dev/null +++ b/init/init_parser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _INIT_INIT_PARSER_H_ +#define _INIT_INIT_PARSER_H_ + +#define INIT_PARSER_MAXARGS 64 + +struct action; + +struct action *action_remove_queue_head(void); +void action_add_queue_tail(struct action *act); +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)); +int action_queue_empty(void); +void queue_property_triggers(const char *name, const char *value); +void queue_all_property_triggers(); +void queue_builtin_action(int (*func)(int nargs, char **args), char *name); + +int init_parse_config_file(const char *fn); + +#endif diff --git a/init/keychords.c b/init/keychords.c new file mode 100644 index 00000000000..aab08195e0d --- /dev/null +++ b/init/keychords.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "log.h" +#include "property_service.h" + +static struct input_keychord *keychords = 0; +static int keychords_count = 0; +static int keychords_length = 0; +static int keychord_fd = -1; + +void add_service_keycodes(struct service *svc) +{ + struct input_keychord *keychord; + int i, size; + + if (svc->keycodes) { + /* add a new keychord to the list */ + size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); + keychords = realloc(keychords, keychords_length + size); + if (!keychords) { + ERROR("could not allocate keychords\n"); + keychords_length = 0; + keychords_count = 0; + return; + } + + keychord = (struct input_keychord *)((char *)keychords + keychords_length); + keychord->version = KEYCHORD_VERSION; + keychord->id = keychords_count + 1; + keychord->count = svc->nkeycodes; + svc->keychord_id = keychord->id; + + for (i = 0; i < svc->nkeycodes; i++) { + keychord->keycodes[i] = svc->keycodes[i]; + } + keychords_count++; + keychords_length += size; + } +} + +void keychord_init() +{ + int fd, ret; + + service_for_each(add_service_keycodes); + + /* nothing to do if no services require keychords */ + if (!keychords) + return; + + fd = open("/dev/keychord", O_RDWR); + if (fd < 0) { + ERROR("could not open /dev/keychord\n"); + return; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + + ret = write(fd, keychords, keychords_length); + if (ret != keychords_length) { + ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); + close(fd); + fd = -1; + } + + free(keychords); + keychords = 0; + + keychord_fd = fd; +} + +void handle_keychord() +{ + struct service *svc; + const char* debuggable; + const char* adb_enabled; + int ret; + __u16 id; + + // only handle keychords if ro.debuggable is set or adb is enabled. + // the logic here is that bugreports should be enabled in userdebug or eng builds + // and on user builds for users that are developers. + debuggable = property_get("ro.debuggable"); + adb_enabled = property_get("init.svc.adbd"); + ret = read(keychord_fd, &id, sizeof(id)); + if (ret != sizeof(id)) { + ERROR("could not read keychord id\n"); + return; + } + + if ((debuggable && !strcmp(debuggable, "1")) || + (adb_enabled && !strcmp(adb_enabled, "running"))) { + svc = service_find_by_keychord(id); + if (svc) { + INFO("starting service %s from keychord\n", svc->name); + service_start(svc, NULL); + } else { + ERROR("service for keychord %d not found\n", id); + } + } +} + +int get_keychord_fd() +{ + return keychord_fd; +} diff --git a/vold/mmc.h b/init/keychords.h similarity index 69% rename from vold/mmc.h rename to init/keychords.h index 5a5d184ed56..070b85891df 100644 --- a/vold/mmc.h +++ b/init/keychords.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +14,14 @@ * limitations under the License. */ -#ifndef _MMC_H -#define _MMC_H +#ifndef _INIT_KEYCHORDS_H_ +#define _INIT_KEYCHORDS_H_ + +struct service; -#define SYSFS_CLASS_MMC_PATH "/sys/class/mmc_host" +void add_service_keycodes(struct service *svc); +void keychord_init(void); +void handle_keychord(void); +int get_keychord_fd(void); #endif diff --git a/init/keywords.h b/init/keywords.h index 641426cafd0..4f6ecba12d2 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -1,17 +1,22 @@ #ifndef KEYWORD +int do_chroot(int nargs, char **args); +int do_chdir(int nargs, char **args); int do_class_start(int nargs, char **args); int do_class_stop(int nargs, char **args); +int do_class_reset(int nargs, char **args); int do_domainname(int nargs, char **args); int do_exec(int nargs, char **args); int do_export(int nargs, char **args); int do_hostname(int nargs, char **args); int do_ifup(int nargs, char **args); int do_insmod(int nargs, char **args); -int do_import(int nargs, char **args); +int do_log(int nargs, char **args); int do_mkdir(int nargs, char **args); int do_mount(int nargs, char **args); int do_restart(int nargs, char **args); +int do_rm(int nargs, char **args); +int do_rmdir(int nargs, char **args); int do_setkey(int nargs, char **args); int do_setprop(int nargs, char **args); int do_setrlimit(int nargs, char **args); @@ -25,16 +30,20 @@ int do_copy(int nargs, char **args); int do_chown(int nargs, char **args); int do_chmod(int nargs, char **args); int do_loglevel(int nargs, char **args); -int do_device(int nargs, char **args); +int do_load_persist_props(int nargs, char **args); +int do_wait(int nargs, char **args); #define __MAKE_KEYWORD_ENUM__ #define KEYWORD(symbol, flags, nargs, func) K_##symbol, enum { K_UNKNOWN, #endif KEYWORD(capability, OPTION, 0, 0) + KEYWORD(chdir, COMMAND, 1, do_chdir) + KEYWORD(chroot, COMMAND, 1, do_chroot) KEYWORD(class, OPTION, 0, 0) KEYWORD(class_start, COMMAND, 1, do_class_start) KEYWORD(class_stop, COMMAND, 1, do_class_stop) + KEYWORD(class_reset, COMMAND, 1, do_class_reset) KEYWORD(console, OPTION, 0, 0) KEYWORD(critical, OPTION, 0, 0) KEYWORD(disabled, OPTION, 0, 0) @@ -45,14 +54,17 @@ enum { KEYWORD(hostname, COMMAND, 1, do_hostname) KEYWORD(ifup, COMMAND, 1, do_ifup) KEYWORD(insmod, COMMAND, 1, do_insmod) - KEYWORD(import, COMMAND, 1, do_import) + KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) + KEYWORD(log, COMMAND, 1, do_log) KEYWORD(mkdir, COMMAND, 1, do_mkdir) KEYWORD(mount, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) KEYWORD(onrestart, OPTION, 0, 0) KEYWORD(restart, COMMAND, 1, do_restart) + KEYWORD(rm, COMMAND, 1, do_rm) + KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(service, SECTION, 0, 0) KEYWORD(setenv, OPTION, 2, 0) KEYWORD(setkey, COMMAND, 0, do_setkey) @@ -65,12 +77,14 @@ enum { KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) KEYWORD(user, OPTION, 0, 0) + KEYWORD(wait, COMMAND, 1, do_wait) KEYWORD(write, COMMAND, 2, do_write) KEYWORD(copy, COMMAND, 2, do_copy) KEYWORD(chown, COMMAND, 2, do_chown) KEYWORD(chmod, COMMAND, 2, do_chmod) KEYWORD(loglevel, COMMAND, 1, do_loglevel) - KEYWORD(device, COMMAND, 4, do_device) + KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) + KEYWORD(ioprio, OPTION, 0, 0) #ifdef __MAKE_KEYWORD_ENUM__ KEYWORD_COUNT, }; diff --git a/init/log.h b/init/log.h new file mode 100644 index 00000000000..4aac3df5d89 --- /dev/null +++ b/init/log.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _INIT_LOG_H_ +#define _INIT_LOG_H_ + +#include + +#define ERROR(x...) KLOG_ERROR("init", x) +#define NOTICE(x...) KLOG_NOTICE("init", x) +#define INFO(x...) KLOG_INFO("init", x) + +#define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ + +#endif diff --git a/init/logo.c b/init/logo.c index 6a740bfd61f..1b12f330561 100644 --- a/init/logo.c +++ b/init/logo.c @@ -25,7 +25,7 @@ #include #include -#include "init.h" +#include "log.h" #ifdef ANDROID #include @@ -113,6 +113,7 @@ int load_565rle_image(char *fn) if (vt_set_mode(1)) return -1; +#ifndef NO_INITLOGO fd = open(fn, O_RDONLY); if (fd < 0) { ERROR("cannot open '%s'\n", fn); @@ -150,6 +151,7 @@ int load_565rle_image(char *fn) fb_close(&fb); close(fd); unlink(fn); +#endif return 0; fail_unmap_data: diff --git a/init/parser.c b/init/parser.c index affc80c9aa8..48e7aec23cd 100644 --- a/init/parser.c +++ b/init/parser.c @@ -1,21 +1,9 @@ #include -#include -#include -#include #include #include -#include -#include -#include "init.h" -#include "property_service.h" - -#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ -#include - -static list_declare(service_list); -static list_declare(action_list); -static list_declare(action_queue); +#include "parser.h" +#include "log.h" #define RAW(x...) log_write(6, x) @@ -60,27 +48,6 @@ void DUMP(void) #endif } -#define T_EOF 0 -#define T_TEXT 1 -#define T_NEWLINE 2 - -struct parse_state -{ - char *ptr; - char *text; - int line; - int nexttoken; - void *context; - void (*parse_line)(struct parse_state *state, int nargs, char **args); - const char *filename; -}; - -static void *parse_service(struct parse_state *state, int nargs, char **args); -static void parse_line_service(struct parse_state *state, int nargs, char **args); - -static void *parse_action(struct parse_state *state, int nargs, char **args); -static void parse_line_action(struct parse_state *state, int nargs, char **args); - void parse_error(struct parse_state *state, const char *fmt, ...) { va_list ap; @@ -98,112 +65,6 @@ void parse_error(struct parse_state *state, const char *fmt, ...) ERROR("%s", buf); } -#define SECTION 0x01 -#define COMMAND 0x02 -#define OPTION 0x04 - -#include "keywords.h" - -#define KEYWORD(symbol, flags, nargs, func) \ - [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, - -struct { - const char *name; - int (*func)(int nargs, char **args); - unsigned char nargs; - unsigned char flags; -} keyword_info[KEYWORD_COUNT] = { - [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, -#include "keywords.h" -}; -#undef KEYWORD - -#define kw_is(kw, type) (keyword_info[kw].flags & (type)) -#define kw_name(kw) (keyword_info[kw].name) -#define kw_func(kw) (keyword_info[kw].func) -#define kw_nargs(kw) (keyword_info[kw].nargs) - -int lookup_keyword(const char *s) -{ - switch (*s++) { - case 'c': - if (!strcmp(s, "opy")) return K_copy; - if (!strcmp(s, "apability")) return K_capability; - if (!strcmp(s, "lass")) return K_class; - if (!strcmp(s, "lass_start")) return K_class_start; - if (!strcmp(s, "lass_stop")) return K_class_stop; - if (!strcmp(s, "onsole")) return K_console; - if (!strcmp(s, "hown")) return K_chown; - if (!strcmp(s, "hmod")) return K_chmod; - if (!strcmp(s, "ritical")) return K_critical; - break; - case 'd': - if (!strcmp(s, "isabled")) return K_disabled; - if (!strcmp(s, "omainname")) return K_domainname; - if (!strcmp(s, "evice")) return K_device; - break; - case 'e': - if (!strcmp(s, "xec")) return K_exec; - if (!strcmp(s, "xport")) return K_export; - break; - case 'g': - if (!strcmp(s, "roup")) return K_group; - break; - case 'h': - if (!strcmp(s, "ostname")) return K_hostname; - break; - case 'i': - if (!strcmp(s, "fup")) return K_ifup; - if (!strcmp(s, "nsmod")) return K_insmod; - if (!strcmp(s, "mport")) return K_import; - break; - case 'k': - if (!strcmp(s, "eycodes")) return K_keycodes; - break; - case 'l': - if (!strcmp(s, "oglevel")) return K_loglevel; - break; - case 'm': - if (!strcmp(s, "kdir")) return K_mkdir; - if (!strcmp(s, "ount")) return K_mount; - break; - case 'o': - if (!strcmp(s, "n")) return K_on; - if (!strcmp(s, "neshot")) return K_oneshot; - if (!strcmp(s, "nrestart")) return K_onrestart; - break; - case 'r': - if (!strcmp(s, "estart")) return K_restart; - break; - case 's': - if (!strcmp(s, "ervice")) return K_service; - if (!strcmp(s, "etenv")) return K_setenv; - if (!strcmp(s, "etkey")) return K_setkey; - if (!strcmp(s, "etprop")) return K_setprop; - if (!strcmp(s, "etrlimit")) return K_setrlimit; - if (!strcmp(s, "ocket")) return K_socket; - if (!strcmp(s, "tart")) return K_start; - if (!strcmp(s, "top")) return K_stop; - if (!strcmp(s, "ymlink")) return K_symlink; - if (!strcmp(s, "ysclktz")) return K_sysclktz; - break; - case 't': - if (!strcmp(s, "rigger")) return K_trigger; - break; - case 'u': - if (!strcmp(s, "ser")) return K_user; - break; - case 'w': - if (!strcmp(s, "rite")) return K_write; - break; - } - return K_UNKNOWN; -} - -void parse_line_no_op(struct parse_state *state, int nargs, char **args) -{ -} - int next_token(struct parse_state *state) { char *x = state->ptr; @@ -221,7 +82,6 @@ int next_token(struct parse_state *state) state->ptr = x; return T_EOF; case '\n': - state->line++; x++; state->ptr = x; return T_NEWLINE; @@ -232,9 +92,13 @@ int next_token(struct parse_state *state) continue; case '#': while (*x && (*x != '\n')) x++; - state->line++; - state->ptr = x; - return T_NEWLINE; + if (*x == '\n') { + state->ptr = x+1; + return T_NEWLINE; + } else { + state->ptr = x; + return T_EOF; + } default: goto text; } @@ -317,480 +181,3 @@ int next_token(struct parse_state *state) } return T_EOF; } - -void parse_line(int nargs, char **args) -{ - int n; - int id = lookup_keyword(args[0]); - printf("%s(%d)", args[0], id); - for (n = 1; n < nargs; n++) { - printf(" '%s'", args[n]); - } - printf("\n"); -} - -void parse_new_section(struct parse_state *state, int kw, - int nargs, char **args) -{ - printf("[ %s %s ]\n", args[0], - nargs > 1 ? args[1] : ""); - switch(kw) { - case K_service: - state->context = parse_service(state, nargs, args); - if (state->context) { - state->parse_line = parse_line_service; - return; - } - break; - case K_on: - state->context = parse_action(state, nargs, args); - if (state->context) { - state->parse_line = parse_line_action; - return; - } - break; - } - state->parse_line = parse_line_no_op; -} - -static void parse_config(const char *fn, char *s) -{ - struct parse_state state; - char *args[SVC_MAXARGS]; - int nargs; - - nargs = 0; - state.filename = fn; - state.line = 1; - state.ptr = s; - state.nexttoken = 0; - state.parse_line = parse_line_no_op; - for (;;) { - switch (next_token(&state)) { - case T_EOF: - state.parse_line(&state, 0, 0); - return; - case T_NEWLINE: - if (nargs) { - int kw = lookup_keyword(args[0]); - if (kw_is(kw, SECTION)) { - state.parse_line(&state, 0, 0); - parse_new_section(&state, kw, nargs, args); - } else { - state.parse_line(&state, nargs, args); - } - nargs = 0; - } - break; - case T_TEXT: - if (nargs < SVC_MAXARGS) { - args[nargs++] = state.text; - } - break; - } - } -} - -int parse_config_file(const char *fn) -{ - char *data; - data = read_file(fn, 0); - if (!data) return -1; - - parse_config(fn, data); - DUMP(); - return 0; -} - -static int valid_name(const char *name) -{ - if (strlen(name) > 16) { - return 0; - } - while (*name) { - if (!isalnum(*name) && (*name != '_') && (*name != '-')) { - return 0; - } - name++; - } - return 1; -} - -struct service *service_find_by_name(const char *name) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (!strcmp(svc->name, name)) { - return svc; - } - } - return 0; -} - -struct service *service_find_by_pid(pid_t pid) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (svc->pid == pid) { - return svc; - } - } - return 0; -} - -struct service *service_find_by_keychord(int keychord_id) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (svc->keychord_id == keychord_id) { - return svc; - } - } - return 0; -} - -void service_for_each(void (*func)(struct service *svc)) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - func(svc); - } -} - -void service_for_each_class(const char *classname, - void (*func)(struct service *svc)) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (!strcmp(svc->classname, classname)) { - func(svc); - } - } -} - -void service_for_each_flags(unsigned matchflags, - void (*func)(struct service *svc)) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (svc->flags & matchflags) { - func(svc); - } - } -} - -void action_for_each_trigger(const char *trigger, - void (*func)(struct action *act)) -{ - struct listnode *node; - struct action *act; - list_for_each(node, &action_list) { - act = node_to_item(node, struct action, alist); - if (!strcmp(act->name, trigger)) { - func(act); - } - } -} - -void queue_property_triggers(const char *name, const char *value) -{ - struct listnode *node; - struct action *act; - list_for_each(node, &action_list) { - act = node_to_item(node, struct action, alist); - if (!strncmp(act->name, "property:", strlen("property:"))) { - const char *test = act->name + strlen("property:"); - int name_length = strlen(name); - - if (!strncmp(name, test, name_length) && - test[name_length] == '=' && - !strcmp(test + name_length + 1, value)) { - action_add_queue_tail(act); - } - } - } -} - -void queue_all_property_triggers() -{ - struct listnode *node; - struct action *act; - list_for_each(node, &action_list) { - act = node_to_item(node, struct action, alist); - if (!strncmp(act->name, "property:", strlen("property:"))) { - /* parse property name and value - syntax is property:= */ - const char* name = act->name + strlen("property:"); - const char* equals = strchr(name, '='); - if (equals) { - char prop_name[PROP_NAME_MAX + 1]; - const char* value; - int length = equals - name; - if (length > PROP_NAME_MAX) { - ERROR("property name too long in trigger %s", act->name); - } else { - memcpy(prop_name, name, length); - prop_name[length] = 0; - - /* does the property exist, and match the trigger value? */ - value = property_get(prop_name); - if (value && !strcmp(equals + 1, value)) { - action_add_queue_tail(act); - } - } - } - } - } -} - -void action_add_queue_tail(struct action *act) -{ - list_add_tail(&action_queue, &act->qlist); -} - -struct action *action_remove_queue_head(void) -{ - if (list_empty(&action_queue)) { - return 0; - } else { - struct listnode *node = list_head(&action_queue); - struct action *act = node_to_item(node, struct action, qlist); - list_remove(node); - return act; - } -} - -static void *parse_service(struct parse_state *state, int nargs, char **args) -{ - struct service *svc; - if (nargs < 3) { - parse_error(state, "services must have a name and a program\n"); - return 0; - } - if (!valid_name(args[1])) { - parse_error(state, "invalid service name '%s'\n", args[1]); - return 0; - } - - svc = service_find_by_name(args[1]); - if (svc) { - parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); - return 0; - } - - nargs -= 2; - svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); - if (!svc) { - parse_error(state, "out of memory\n"); - return 0; - } - svc->name = args[1]; - svc->classname = "default"; - memcpy(svc->args, args + 2, sizeof(char*) * nargs); - svc->args[nargs] = 0; - svc->nargs = nargs; - svc->onrestart.name = "onrestart"; - list_init(&svc->onrestart.commands); - list_add_tail(&service_list, &svc->slist); - return svc; -} - -static void parse_line_service(struct parse_state *state, int nargs, char **args) -{ - struct service *svc = state->context; - struct command *cmd; - int i, kw, kw_nargs; - - if (nargs == 0) { - return; - } - - kw = lookup_keyword(args[0]); - switch (kw) { - case K_capability: - break; - case K_class: - if (nargs != 2) { - parse_error(state, "class option requires a classname\n"); - } else { - svc->classname = args[1]; - } - break; - case K_console: - svc->flags |= SVC_CONSOLE; - break; - case K_disabled: - svc->flags |= SVC_DISABLED; - break; - case K_group: - if (nargs < 2) { - parse_error(state, "group option requires a group id\n"); - } else if (nargs > NR_SVC_SUPP_GIDS + 2) { - parse_error(state, "group option accepts at most %d supp. groups\n", - NR_SVC_SUPP_GIDS); - } else { - int n; - svc->gid = decode_uid(args[1]); - for (n = 2; n < nargs; n++) { - svc->supp_gids[n-2] = decode_uid(args[n]); - } - svc->nr_supp_gids = n - 2; - } - break; - case K_keycodes: - if (nargs < 2) { - parse_error(state, "keycodes option requires atleast one keycode\n"); - } else { - svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0])); - if (!svc->keycodes) { - parse_error(state, "could not allocate keycodes\n"); - } else { - svc->nkeycodes = nargs - 1; - for (i = 1; i < nargs; i++) { - svc->keycodes[i - 1] = atoi(args[i]); - } - } - } - break; - case K_oneshot: - svc->flags |= SVC_ONESHOT; - break; - case K_onrestart: - nargs--; - args++; - kw = lookup_keyword(args[0]); - if (!kw_is(kw, COMMAND)) { - parse_error(state, "invalid command '%s'\n", args[0]); - break; - } - kw_nargs = kw_nargs(kw); - if (nargs < kw_nargs) { - parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, - kw_nargs > 2 ? "arguments" : "argument"); - break; - } - - cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); - cmd->func = kw_func(kw); - cmd->nargs = nargs; - memcpy(cmd->args, args, sizeof(char*) * nargs); - list_add_tail(&svc->onrestart.commands, &cmd->clist); - break; - case K_critical: - svc->flags |= SVC_CRITICAL; - break; - case K_setenv: { /* name value */ - struct svcenvinfo *ei; - if (nargs < 2) { - parse_error(state, "setenv option requires name and value arguments\n"); - break; - } - ei = calloc(1, sizeof(*ei)); - if (!ei) { - parse_error(state, "out of memory\n"); - break; - } - ei->name = args[1]; - ei->value = args[2]; - ei->next = svc->envvars; - svc->envvars = ei; - break; - } - case K_socket: {/* name type perm [ uid gid ] */ - struct socketinfo *si; - if (nargs < 4) { - parse_error(state, "socket option requires name, type, perm arguments\n"); - break; - } - if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) { - parse_error(state, "socket type must be 'dgram' or 'stream'\n"); - break; - } - si = calloc(1, sizeof(*si)); - if (!si) { - parse_error(state, "out of memory\n"); - break; - } - si->name = args[1]; - si->type = args[2]; - si->perm = strtoul(args[3], 0, 8); - if (nargs > 4) - si->uid = decode_uid(args[4]); - if (nargs > 5) - si->gid = decode_uid(args[5]); - si->next = svc->sockets; - svc->sockets = si; - break; - } - case K_user: - if (nargs != 2) { - parse_error(state, "user option requires a user id\n"); - } else { - svc->uid = decode_uid(args[1]); - } - break; - default: - parse_error(state, "invalid option '%s'\n", args[0]); - } -} - -static void *parse_action(struct parse_state *state, int nargs, char **args) -{ - struct action *act; - if (nargs < 2) { - parse_error(state, "actions must have a trigger\n"); - return 0; - } - if (nargs > 2) { - parse_error(state, "actions may not have extra parameters\n"); - return 0; - } - act = calloc(1, sizeof(*act)); - act->name = args[1]; - list_init(&act->commands); - list_add_tail(&action_list, &act->alist); - /* XXX add to hash */ - return act; -} - -static void parse_line_action(struct parse_state* state, int nargs, char **args) -{ - struct command *cmd; - struct action *act = state->context; - int (*func)(int nargs, char **args); - int kw, n; - - if (nargs == 0) { - return; - } - - kw = lookup_keyword(args[0]); - if (!kw_is(kw, COMMAND)) { - parse_error(state, "invalid command '%s'\n", args[0]); - return; - } - - n = kw_nargs(kw); - if (nargs < n) { - parse_error(state, "%s requires %d %s\n", args[0], n - 1, - n > 2 ? "arguments" : "argument"); - return; - } - cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); - cmd->func = kw_func(kw); - cmd->nargs = nargs; - memcpy(cmd->args, args, sizeof(char*) * nargs); - list_add_tail(&act->commands, &cmd->clist); -} diff --git a/vold/ums.h b/init/parser.h similarity index 51% rename from vold/ums.h rename to init/parser.h index 02cdec37c8a..be937580591 100644 --- a/vold/ums.h +++ b/init/parser.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,17 +14,27 @@ * limitations under the License. */ -#ifndef _UMS_H -#define _UMS_H +#ifndef PARSER_H_ +#define PARSER_H_ + +#define T_EOF 0 +#define T_TEXT 1 +#define T_NEWLINE 2 -// these must match the corresponding strings in java/android/android/os/UsbListener.java -#define VOLD_EVT_UMS_ENABLED "ums_enabled" -#define VOLD_EVT_UMS_DISABLED "ums_disabled" -#define VOLD_EVT_UMS_CONNECTED "ums_connected" -#define VOLD_EVT_UMS_DISCONNECTED "ums_disconnected" +struct parse_state +{ + char *ptr; + char *text; + int line; + int nexttoken; + void *context; + void (*parse_line)(struct parse_state *state, int nargs, char **args); + const char *filename; +}; +int lookup_keyword(const char *s); +void DUMP(void); +int next_token(struct parse_state *state); +void parse_error(struct parse_state *state, const char *fmt, ...); -int ums_send_status(void); -int ums_enable(char *device_file, char *lun_syspath); -int ums_disable(char *lun_syspath); -#endif +#endif /* PARSER_H_ */ diff --git a/init/property_service.c b/init/property_service.c index 7db7c2cbf17..14945fa7750 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -27,7 +27,6 @@ #include #include -#include #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include @@ -43,55 +42,76 @@ #include "property_service.h" #include "init.h" +#include "util.h" +#include "log.h" + +#include #define PERSISTENT_PROPERTY_DIR "/data/property" static int persistent_properties_loaded = 0; +static int property_area_inited = 0; + +static int property_set_fd = -1; /* White list of permissions for setting property services. */ +#ifndef PROPERTY_PERMS struct { const char *prefix; unsigned int uid; unsigned int gid; } property_perms[] = { - { "net.rmnet0.", AID_RADIO, 0 }, + { "net.rmnet", AID_RADIO, 0 }, { "net.gprs.", AID_RADIO, 0 }, { "net.ppp", AID_RADIO, 0 }, + { "net.qmi", AID_RADIO, 0 }, + { "net.lte", AID_RADIO, 0 }, + { "net.cdma", AID_RADIO, 0 }, { "ril.", AID_RADIO, 0 }, { "gsm.", AID_RADIO, 0 }, { "persist.radio", AID_RADIO, 0 }, { "net.dns", AID_RADIO, 0 }, + { "sys.usb.config", AID_RADIO, 0 }, { "net.", AID_SYSTEM, 0 }, { "dev.", AID_SYSTEM, 0 }, { "runtime.", AID_SYSTEM, 0 }, { "hw.", AID_SYSTEM, 0 }, { "sys.", AID_SYSTEM, 0 }, { "service.", AID_SYSTEM, 0 }, + { "service.", AID_RADIO, 0 }, { "wlan.", AID_SYSTEM, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, - { "vpn.", AID_SYSTEM, 0 }, - { "vpn.", AID_VPN, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, + { "service.adb.tcp.port", AID_SHELL, 0 }, { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, + { "persist.service.", AID_RADIO, 0 }, + { "persist.security.",AID_SYSTEM, 0 }, + { "net.pdp", AID_RADIO, AID_RADIO }, { NULL, 0, 0 } }; +/* Avoid extending this array. Check device_perms.h */ +#endif /* * White list of UID that are allowed to start/stop services. * Currently there are no user apps that require. */ +#ifndef CONTROL_PERMS struct { const char *service; unsigned int uid; unsigned int gid; } control_perms[] = { { "dumpstate",AID_SHELL, AID_LOG }, + { "ril-daemon",AID_RADIO, AID_RADIO }, {NULL, 0, 0 } }; +/* Avoid extending this array. Check device_perms.h */ +#endif typedef struct { void *data; @@ -104,21 +124,31 @@ static int init_workspace(workspace *w, size_t size) void *data; int fd; - fd = ashmem_create_region("system_properties", size); - if(fd < 0) + /* dev is a tmpfs that we can use to carve a shared workspace + * out of, so let's do that... + */ + fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600); + if (fd < 0) return -1; + if (ftruncate(fd, size) < 0) + goto out; + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(data == MAP_FAILED) goto out; - /* allow the wolves we share with to do nothing but read */ - ashmem_set_prot_region(fd, PROT_READ); + close(fd); + + fd = open("/dev/__properties__", O_RDONLY); + if (fd < 0) + return -1; + + unlink("/dev/__properties__"); w->data = data; w->size = size; w->fd = fd; - return 0; out: @@ -126,12 +156,12 @@ static int init_workspace(workspace *w, size_t size) return -1; } -/* (8 header words + 247 toc words) = 1020 bytes */ -/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */ +/* (8 header words + 372 toc words) = 1520 bytes */ +/* 1536 bytes header and toc + 372 prop_infos @ 128 bytes = 49152 bytes */ -#define PA_COUNT_MAX 247 -#define PA_INFO_START 1024 -#define PA_SIZE 32768 +#define PA_COUNT_MAX 372 +#define PA_INFO_START 1536 +#define PA_SIZE 49152 static workspace pa_workspace; static prop_info *pa_info_array; @@ -159,7 +189,7 @@ static int init_property_area(void) /* plug into the lib property services */ __system_property_area__ = pa; - + property_area_inited = 1; return 0; } @@ -171,22 +201,13 @@ static void update_prop_info(prop_info *pi, const char *value, unsigned len) __futex_wake(&pi->serial, INT32_MAX); } -static int property_write(prop_info *pi, const char *value) -{ - int valuelen = strlen(value); - if(valuelen >= PROP_VALUE_MAX) return -1; - update_prop_info(pi, value, valuelen); - return 0; -} - - /* * Checks permissions for starting/stoping system services. * AID_SYSTEM and AID_ROOT are always allowed. * * Returns 1 if uid allowed, 0 otherwise. */ -static int check_control_perms(const char *name, int uid, int gid) { +static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) { int i; if (uid == AID_SYSTEM || uid == AID_ROOT) return 1; @@ -207,7 +228,7 @@ static int check_control_perms(const char *name, int uid, int gid) { * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ -static int check_perms(const char *name, unsigned int uid, int gid) +static int check_perms(const char *name, unsigned int uid, unsigned int gid) { int i; if (uid == 0) @@ -343,7 +364,7 @@ static int property_list(void (*propfn)(const char *key, const char *value, void return 0; } -void handle_property_set_fd(int fd) +void handle_property_set_fd() { prop_msg msg; int s; @@ -354,7 +375,7 @@ void handle_property_set_fd(int fd) socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); - if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) { + if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } @@ -365,11 +386,11 @@ void handle_property_set_fd(int fd) return; } - r = recv(s, &msg, sizeof(msg), 0); - close(s); + r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { - ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n", - r, sizeof(prop_msg)); + ERROR("sys_prop: mis-match msg size recieved: %d expected: %d errno: %d\n", + r, sizeof(prop_msg), errno); + close(s); return; } @@ -379,11 +400,14 @@ void handle_property_set_fd(int fd) msg.value[PROP_VALUE_MAX-1] = 0; if(memcmp(msg.name,"ctl.",4) == 0) { + // Keep the old close-socket-early behavior when handling + // ctl.* properties. + close(s); if (check_control_perms(msg.value, cr.uid, cr.gid)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { - ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n", - msg.name + 4, msg.value, cr.uid, cr.pid); + ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", + msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { if (check_perms(msg.name, cr.uid, cr.gid)) { @@ -392,10 +416,16 @@ void handle_property_set_fd(int fd) ERROR("sys_prop: permission denied uid:%d name:%s\n", cr.uid, msg.name); } + + // Note: bionic's property client code assumes that the + // property server will not close the socket until *AFTER* + // the property is written to memory. + close(s); } break; default: + close(s); break; } } @@ -486,13 +516,31 @@ static void load_persistent_properties() persistent_properties_loaded = 1; } -void property_init(void) +void property_init(bool load_defaults) { init_property_area(); - load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); + if (load_defaults) + load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); +} + +int properties_inited(void) +{ + return property_area_inited; +} + +/* When booting an encrypted system, /data is not mounted when the + * property service is started, so any properties stored there are + * not loaded. Vold triggers init to load these properties once it + * has mounted /data. + */ +void load_persist_props(void) +{ + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + /* Read persistent properties after all default values have been loaded. */ + load_persistent_properties(); } -int start_property_service(void) +void start_property_service(void) { int fd; @@ -503,10 +551,15 @@ int start_property_service(void) load_persistent_properties(); fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); - if(fd < 0) return -1; + if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); - return fd; + property_set_fd = fd; +} + +int get_property_set_fd() +{ + return property_set_fd; } diff --git a/init/property_service.h b/init/property_service.h index d12f1f38bfa..37c27882f02 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -17,12 +17,16 @@ #ifndef _INIT_PROPERTY_H #define _INIT_PROPERTY_H -extern void handle_property_fd(int fd); -extern void handle_property_set_fd(int fd); -extern void property_init(void); -extern int start_property_service(void); +#include + +extern void handle_property_set_fd(void); +extern void property_init(bool load_defaults); +extern void load_persist_props(void); +extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); extern const char* property_get(const char *name); extern int property_set(const char *name, const char *value); +extern int properties_inited(); +int get_property_set_fd(void); #endif /* _INIT_PROPERTY_H */ diff --git a/init/readme.txt b/init/readme.txt index 665090bad7d..df524a6df2d 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -72,7 +72,7 @@ setenv socket [ [ ] ] Create a unix domain socket named /dev/socket/ and pass - its fd to the launched process. must be "dgram" or "stream". + its fd to the launched process. must be "dgram", "stream" or "seqpacket". User and group default to 0. user @@ -145,12 +145,18 @@ import hostname Set the host name. +chdir + Change working directory. + chmod Change file access permissions. chown Change file owner and group. +chroot + Change process root directory. + class_start Start all services of the specified class if they are not already running. diff --git a/init/signal_handler.c b/init/signal_handler.c new file mode 100644 index 00000000000..672904d4b8b --- /dev/null +++ b/init/signal_handler.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "util.h" +#include "log.h" + +static int signal_fd = -1; +static int signal_recv_fd = -1; + +static void sigchld_handler(int s) +{ + write(signal_fd, &s, 1); +} + +#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */ +#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/ + +static int wait_for_one_process(int block) +{ + pid_t pid; + int status; + struct service *svc; + struct socketinfo *si; + time_t now; + struct listnode *node; + struct command *cmd; + + while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); + if (pid <= 0) return -1; + INFO("waitpid returned pid %d, status = %08x\n", pid, status); + + svc = service_find_by_pid(pid); + if (!svc) { + ERROR("untracked pid %d exited\n", pid); + return 0; + } + + NOTICE("process '%s', pid %d exited\n", svc->name, pid); + + if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { + kill(-pid, SIGKILL); + NOTICE("process '%s' killing any children in process group\n", svc->name); + } + + /* remove any sockets we may have created */ + for (si = svc->sockets; si; si = si->next) { + char tmp[128]; + snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); + unlink(tmp); + } + + svc->pid = 0; + svc->flags &= (~SVC_RUNNING); + + /* oneshot processes go into the disabled state on exit, + * except when manually restarted. */ + if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) { + svc->flags |= SVC_DISABLED; + } + + /* disabled and reset processes do not get restarted automatically */ + if (svc->flags & (SVC_DISABLED | SVC_RESET) ) { + notify_service_state(svc->name, "stopped"); + return 0; + } + + now = gettime(); + if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) { + if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { + if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { + ERROR("critical process '%s' exited %d times in %d minutes; " + "rebooting into recovery mode\n", svc->name, + CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); + android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); + return 0; + } + } else { + svc->time_crashed = now; + svc->nr_crashed = 1; + } + } + + svc->flags &= (~SVC_RESTART); + svc->flags |= SVC_RESTARTING; + + /* Execute all onrestart commands for this service. */ + list_for_each(node, &svc->onrestart.commands) { + cmd = node_to_item(node, struct command, clist); + cmd->func(cmd->nargs, cmd->args); + } + notify_service_state(svc->name, "restarting"); + return 0; +} + +void handle_signal(void) +{ + char tmp[32]; + + /* we got a SIGCHLD - reap and restart as needed */ + read(signal_recv_fd, tmp, sizeof(tmp)); + while (!wait_for_one_process(0)) + ; +} + +void signal_init(void) +{ + int s[2]; + + struct sigaction act; + + act.sa_handler = sigchld_handler; + act.sa_flags = SA_NOCLDSTOP; + act.sa_mask = 0; + act.sa_restorer = NULL; + sigaction(SIGCHLD, &act, 0); + + /* create a signalling mechanism for the sigchld handler */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { + signal_fd = s[0]; + signal_recv_fd = s[1]; + fcntl(s[0], F_SETFD, FD_CLOEXEC); + fcntl(s[0], F_SETFL, O_NONBLOCK); + fcntl(s[1], F_SETFD, FD_CLOEXEC); + fcntl(s[1], F_SETFL, O_NONBLOCK); + } + + handle_signal(); +} + +int get_signal_fd() +{ + return signal_recv_fd; +} diff --git a/vold/switch.h b/init/signal_handler.h similarity index 74% rename from vold/switch.h rename to init/signal_handler.h index 6729f2df7be..b092ccb6e0a 100644 --- a/vold/switch.h +++ b/init/signal_handler.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +14,11 @@ * limitations under the License. */ -#ifndef _SWITCH_H -#define _SWITCH_H - -#include "vold.h" +#ifndef _INIT_SIGNAL_HANDLER_H_ +#define _INIT_SIGNAL_HANDLER_H_ -#define SYSFS_CLASS_SWITCH_PATH "/sys/class/switch" +void signal_init(void); +void handle_signal(void); +int get_signal_fd(void); #endif diff --git a/init/ueventd.c b/init/ueventd.c new file mode 100644 index 00000000000..ecf3b9beae3 --- /dev/null +++ b/init/ueventd.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ueventd.h" +#include "log.h" +#include "util.h" +#include "devices.h" +#include "ueventd_parser.h" + +static char hardware[32]; +static unsigned revision = 0; + +static void import_kernel_nv(char *name, int in_qemu) +{ + if (*name != '\0') { + char *value = strchr(name, '='); + if (value != NULL) { + *value++ = 0; + if (!strcmp(name,"androidboot.hardware")) + { + strlcpy(hardware, value, sizeof(hardware)); + } + } + } +} + +int ueventd_main(int argc, char **argv) +{ + struct pollfd ufd; + int nr; + char tmp[32]; + + /* Prevent fire-and-forget children from becoming zombies. + * If we should need to wait() for some children in the future + * (as opposed to none right now), double-forking here instead + * of ignoring SIGCHLD may be the better solution. + */ + signal(SIGCHLD, SIG_IGN); + + open_devnull_stdio(); + klog_init(); + + INFO("starting ueventd\n"); + + /* Respect hardware passed in through the kernel cmd line. Here we will look + * for androidboot.hardware param in kernel cmdline, and save its value in + * hardware[]. */ + import_kernel_cmdline(0, import_kernel_nv); + + get_hardware_name(hardware, &revision); + + ueventd_parse_config_file("/ueventd.rc"); + + snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware); + ueventd_parse_config_file(tmp); + + device_init(); + + ufd.events = POLLIN; + ufd.fd = get_device_fd(); + + while(1) { + ufd.revents = 0; + nr = poll(&ufd, 1, -1); + if (nr <= 0) + continue; + if (ufd.revents == POLLIN) + handle_device_fd(); + } +} + +static int get_android_id(const char *id) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(android_ids); i++) + if (!strcmp(id, android_ids[i].name)) + return android_ids[i].aid; + return 0; +} + +void set_device_permission(int nargs, char **args) +{ + char *name; + char *attr = 0; + mode_t perm; + uid_t uid; + gid_t gid; + int prefix = 0; + char *endptr; + int ret; + char *tmp = 0; + + if (nargs == 0) + return; + + if (args[0][0] == '#') + return; + + name = args[0]; + + if (!strncmp(name,"/sys/", 5) && (nargs == 5)) { + INFO("/sys/ rule %s %s\n",args[0],args[1]); + attr = args[1]; + args++; + nargs--; + } + + if (nargs != 4) { + ERROR("invalid line ueventd.rc line for '%s'\n", args[0]); + return; + } + + /* If path starts with mtd@ lookup the mount number. */ + if (!strncmp(name, "mtd@", 4)) { + int n = mtd_name_to_number(name + 4); + if (n >= 0) + asprintf(&tmp, "/dev/mtd/mtd%d", n); + name = tmp; + } else { + int len = strlen(name); + if (name[len - 1] == '*') { + prefix = 1; + name[len - 1] = '\0'; + } + } + + perm = strtol(args[1], &endptr, 8); + if (!endptr || *endptr != '\0') { + ERROR("invalid mode '%s'\n", args[1]); + free(tmp); + return; + } + + ret = get_android_id(args[2]); + if (ret < 0) { + ERROR("invalid uid '%s'\n", args[2]); + free(tmp); + return; + } + uid = ret; + + ret = get_android_id(args[3]); + if (ret < 0) { + ERROR("invalid gid '%s'\n", args[3]); + free(tmp); + return; + } + gid = ret; + + add_dev_perms(name, attr, perm, uid, gid, prefix); + free(tmp); +} diff --git a/vold/uevent.h b/init/ueventd.h similarity index 79% rename from vold/uevent.h rename to init/ueventd.h index 0e9e6716c73..9066e4780dc 100644 --- a/vold/uevent.h +++ b/init/ueventd.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +14,9 @@ * limitations under the License. */ -#ifndef _UEVENT_MSG_H -#define _UEVENT_MSG_H +#ifndef _INIT_UEVENTD_H_ +#define _INIT_UEVENTD_H_ + +int ueventd_main(int argc, char **argv); #endif diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c new file mode 100644 index 00000000000..3e60df5b8c2 --- /dev/null +++ b/init/ueventd_parser.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "ueventd_parser.h" +#include "parser.h" +#include "log.h" +#include "util.h" + +static void parse_line_device(struct parse_state *state, int nargs, char **args); + +static void parse_config(const char *fn, char *s) +{ + struct parse_state state; + char *args[UEVENTD_PARSER_MAXARGS]; + int nargs; + nargs = 0; + state.filename = fn; + state.line = 1; + state.ptr = s; + state.nexttoken = 0; + state.parse_line = parse_line_device; + for (;;) { + int token = next_token(&state); + switch (token) { + case T_EOF: + state.parse_line(&state, 0, 0); + return; + case T_NEWLINE: + if (nargs) { + state.parse_line(&state, nargs, args); + nargs = 0; + } + break; + case T_TEXT: + if (nargs < UEVENTD_PARSER_MAXARGS) { + args[nargs++] = state.text; + } + break; + } + } +} + +int ueventd_parse_config_file(const char *fn) +{ + char *data; + data = read_file(fn, 0); + if (!data) return -1; + + parse_config(fn, data); + DUMP(); + return 0; +} + +static void parse_line_device(struct parse_state* state, int nargs, char **args) +{ + set_device_permission(nargs, args); +} diff --git a/vold/format.h b/init/ueventd_parser.h similarity index 69% rename from vold/format.h rename to init/ueventd_parser.h index 73cc0126a18..3684285aa8a 100644 --- a/vold/format.h +++ b/init/ueventd_parser.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +14,12 @@ * limitations under the License. */ -#ifndef _FORMAT_H -#define _FORMAT_H +#ifndef _INIT_UEVENTD_PARSER_H_ +#define _INIT_UEVENTD_PARSER_H_ + +#define UEVENTD_PARSER_MAXARGS 5 -#define FORMAT_TYPE_EXT2 "ext2" -#define FORMAT_TYPE_FAT32 "fat32" +int ueventd_parse_config_file(const char *fn); +void set_device_permission(int nargs, char **args); -int format_partition(blkdev_t *part, char *type); -int initialize_mbr(blkdev_t *disk); #endif diff --git a/init/util.c b/init/util.c old mode 100644 new mode 100755 index 0b7667dae8d..de28ab92f33 --- a/init/util.c +++ b/init/util.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -32,43 +33,8 @@ #include -#include "init.h" - -static int log_fd = -1; -/* Inital log level before init.rc is parsed and this this is reset. */ -static int log_level = LOG_DEFAULT_LEVEL; - - -void log_set_level(int level) { - log_level = level; -} - -void log_init(void) -{ - static const char *name = "/dev/__kmsg__"; - if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { - log_fd = open(name, O_WRONLY); - fcntl(log_fd, F_SETFD, FD_CLOEXEC); - unlink(name); - } -} - -#define LOG_BUF_MAX 512 - -void log_write(int level, const char *fmt, ...) -{ - char buf[LOG_BUF_MAX]; - va_list ap; - - if (level > log_level) return; - if (log_fd < 0) return; - - va_start(ap, fmt); - vsnprintf(buf, LOG_BUF_MAX, fmt, ap); - buf[LOG_BUF_MAX - 1] = 0; - va_end(ap); - write(log_fd, buf, strlen(buf)); -} +#include "log.h" +#include "util.h" /* * android_name_to_id - returns the integer uid/gid associated with the given @@ -189,23 +155,276 @@ void *read_file(const char *fn, unsigned *_sz) return 0; } -void list_init(struct listnode *node) +#define MAX_MTD_PARTITIONS 16 + +static struct { + char name[16]; + int number; +} mtd_part_map[MAX_MTD_PARTITIONS]; + +static int mtd_part_count = -1; + +static void find_mtd_partitions(void) +{ + int fd; + char buf[1024]; + char *pmtdbufp; + ssize_t pmtdsize; + int r; + + fd = open("/proc/mtd", O_RDONLY); + if (fd < 0) + return; + + buf[sizeof(buf) - 1] = '\0'; + pmtdsize = read(fd, buf, sizeof(buf) - 1); + pmtdbufp = buf; + while (pmtdsize > 0) { + int mtdnum, mtdsize, mtderasesize; + char mtdname[16]; + mtdname[0] = '\0'; + mtdnum = -1; + r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", + &mtdnum, &mtdsize, &mtderasesize, mtdname); + if ((r == 4) && (mtdname[0] == '"')) { + char *x = strchr(mtdname + 1, '"'); + if (x) { + *x = 0; + } + INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); + if (mtd_part_count < MAX_MTD_PARTITIONS) { + strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); + mtd_part_map[mtd_part_count].number = mtdnum; + mtd_part_count++; + } else { + ERROR("too many mtd partitions\n"); + } + } + while (pmtdsize > 0 && *pmtdbufp != '\n') { + pmtdbufp++; + pmtdsize--; + } + if (pmtdsize > 0) { + pmtdbufp++; + pmtdsize--; + } + } + close(fd); +} + +int mtd_name_to_number(const char *name) +{ + int n; + if (mtd_part_count < 0) { + mtd_part_count = 0; + find_mtd_partitions(); + } + for (n = 0; n < mtd_part_count; n++) { + if (!strcmp(name, mtd_part_map[n].name)) { + return mtd_part_map[n].number; + } + } + return -1; +} + +/* + * gettime() - returns the time in seconds of the system's monotonic clock or + * zero on error. + */ +time_t gettime(void) +{ + struct timespec ts; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret < 0) { + ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); + return 0; + } + + return ts.tv_sec; +} + +int mkdir_recursive(const char *pathname, mode_t mode) +{ + char buf[128]; + const char *slash; + const char *p = pathname; + int width; + int ret; + struct stat info; + + while ((slash = strchr(p, '/')) != NULL) { + width = slash - pathname; + p = slash + 1; + if (width < 0) + break; + if (width == 0) + continue; + if ((unsigned int)width > sizeof(buf) - 1) { + ERROR("path too long for mkdir_recursive\n"); + return -1; + } + memcpy(buf, pathname, width); + buf[width] = 0; + if (stat(buf, &info) != 0) { + ret = mkdir(buf, mode); + if (ret && errno != EEXIST) + return ret; + } + } + ret = mkdir(pathname, mode); + if (ret && errno != EEXIST) + return ret; + return 0; +} + +void sanitize(char *s) +{ + if (!s) + return; + while (isalnum(*s)) + s++; + *s = 0; +} +void make_link(const char *oldpath, const char *newpath) +{ + int ret; + char buf[256]; + char *slash; + int width; + + slash = strrchr(newpath, '/'); + if (!slash) + return; + width = slash - newpath; + if (width <= 0 || width > (int)sizeof(buf) - 1) + return; + memcpy(buf, newpath, width); + buf[width] = 0; + ret = mkdir_recursive(buf, 0755); + if (ret) + ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno); + + ret = symlink(oldpath, newpath); + if (ret && errno != EEXIST) + ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno); +} + +void remove_link(const char *oldpath, const char *newpath) { - node->next = node; - node->prev = node; + char path[256]; + ssize_t ret; + ret = readlink(newpath, path, sizeof(path) - 1); + if (ret <= 0) + return; + path[ret] = 0; + if (!strcmp(path, oldpath)) + unlink(newpath); } -void list_add_tail(struct listnode *head, struct listnode *item) +int wait_for_file(const char *filename, int timeout) { - item->next = head; - item->prev = head->prev; - head->prev->next = item; - head->prev = item; + struct stat info; + time_t timeout_time = gettime() + timeout; + int ret = -1; + + while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) + usleep(10000); + + return ret; } -void list_remove(struct listnode *item) +void open_devnull_stdio(void) { - item->next->prev = item->prev; - item->prev->next = item->next; + int fd; + static const char *name = "/dev/__null__"; + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { + fd = open(name, O_RDWR); + unlink(name); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) { + close(fd); + } + return; + } + } + + exit(1); } +void get_hardware_name(char *hardware, unsigned int *revision) +{ + char data[1024]; + int fd, n; + char *x, *hw, *rev; + + fd = open("/proc/cpuinfo", O_RDONLY); + if (fd < 0) return; + + n = read(fd, data, 1023); + close(fd); + if (n < 0) return; + + data[n] = 0; + hw = strstr(data, "\nHardware"); + rev = strstr(data, "\nRevision"); + + /* Hardware string was provided on kernel command line */ + if (!hardware[0]) { + if (hw) { + x = strstr(hw, ": "); + if (x) { + x += 2; + n = 0; + while (*x && *x != '\n') { + if (!isspace(*x)) + hardware[n++] = tolower(*x); + x++; + if (n == 31) break; + } + hardware[n] = 0; + } + } + } + + if (rev) { + x = strstr(rev, ": "); + if (x) { + *revision = strtoul(x + 2, 0, 16); + } + } +} + +void import_kernel_cmdline(int in_qemu, + void (*import_kernel_nv)(char *name, int in_qemu)) +{ + char cmdline[1024]; + char *ptr; + int fd; + + fd = open("/proc/cmdline", O_RDONLY); + if (fd >= 0) { + int n = read(fd, cmdline, 1023); + if (n < 0) n = 0; + + /* get rid of trailing newline, it happens */ + if (n > 0 && cmdline[n-1] == '\n') n--; + + cmdline[n] = 0; + close(fd); + } else { + cmdline[0] = 0; + } + + ptr = cmdline; + while (ptr && *ptr) { + char *x = strchr(ptr, ' '); + if (x != 0) *x++ = 0; + import_kernel_nv(ptr, in_qemu); + ptr = x; + } +} diff --git a/init/util.h b/init/util.h new file mode 100644 index 00000000000..9247739b3e6 --- /dev/null +++ b/init/util.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _INIT_UTIL_H_ +#define _INIT_UTIL_H_ + +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static const char *coldboot_done = "/dev/.coldboot_done"; + +int mtd_name_to_number(const char *name); +int create_socket(const char *name, int type, mode_t perm, + uid_t uid, gid_t gid); +void *read_file(const char *fn, unsigned *_sz); +time_t gettime(void); +unsigned int decode_uid(const char *s); + +int mkdir_recursive(const char *pathname, mode_t mode); +void sanitize(char *p); +void make_link(const char *oldpath, const char *newpath); +void remove_link(const char *oldpath, const char *newpath); +int wait_for_file(const char *filename, int timeout); +void open_devnull_stdio(void); +void get_hardware_name(char *hardware, unsigned int *revision); +void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)); +#endif diff --git a/libacc/Android.mk b/libacc/Android.mk deleted file mode 100644 index 2b4998e5902..00000000000 --- a/libacc/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# Shared library for target -# ======================================================== - -LOCAL_MODULE:= libacc -LOCAL_SRC_FILES := acc.cpp - -LOCAL_SHARED_LIBRARIES := libdl libcutils - -include $(BUILD_SHARED_LIBRARY) - -# Shared library for host -# ======================================================== - -include $(CLEAR_VARS) -LOCAL_MODULE:= libacc -LOCAL_SRC_FILES := acc.cpp - -LOCAL_CFLAGS := -O0 -g - -LOCAL_STATIC_LIBRARIES := libcutils -LOCAL_LDLIBS := -ldl - -include $(BUILD_HOST_SHARED_LIBRARY) - -# Build children -# ======================================================== - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libacc/FEATURES b/libacc/FEATURES deleted file mode 100644 index 97a876d9e35..00000000000 --- a/libacc/FEATURES +++ /dev/null @@ -1,83 +0,0 @@ - -Supported C language subset: - - - Expressions: - - * binary operators, by decreasing priority order: '*' '/' '%', - '+' '-', '>>' '<<', '<' '<=' '>' '>=', '==' '!=', '&', - '^', '|', '=', '&&', '||'. - - * '&&' and '||' have the same semantics as C : left to right - evaluation and early exit. - - * Parenthesis are supported. - - * Comma operator is supported. - - * Trinary operator (?:) is not supported. - - * Unary operators: '&', '*' (pointer indirection), '-' - (negation), '+', '!', '~', '++' and '--'. - - * Pointer indirection ('*') is supported. - - * Square brackets can be used for pointer arithmetic. - - * '=' and = are supported. - - * Function calls are supported with standard Linux calling - convention. Function pointers are supported. - Functions can be used before being declared. - - - sizeof() is not supported. - - - Types: - + int, short, char, float, double - + pointers - + variables can be initialized in declarations. - + Only ANSI-style function declarations are supported. - - "..." is not supported. - - short is not supported - - const is not supported - - arrays are not supported - - long doubles are not supported - - structs are not supported - - - Unknown functions and variables are bound at compile time by calling - back to the caller. For the 'acc' command-line tool unknown functions - and variables are looked up using dlsym, to allow using many libc - functions and variables. - - - Instructions: blocks ('{' '}') are supported as in C. 'if' and - 'else' can be used for tests. The 'while' and 'for' C constructs - are supported for loops. 'break' can be used to exit - loops. 'return' is used for the return value of a function. - - - switch / case is not supported. - - goto and labels are not supported. - - continue is not supported. - - - Identifiers are parsed the same way as C. Local variables are - handled, but there is no local name space (not a problem if - different names are used for local and global variables). - - - Numbers can be entered in decimal, hexadecimal ('0x' or '0X' - prefix), or octal ('0' prefix). - - - Float and double constants are supported. - - - '#define' is supported without function like arguments. No macro - recursion is tolerated. Other preprocessor directives are - ignored. - - - C Strings and C character constants are supported. All ANSI C - character escapes are supported. - - - Both C comments ( /* */ ) and C++ comments ( // ... end-of-line ) are - supported. - - - Some syntax errors are reported, others may cause a crash. - - - Memory: the code, data, and symbol sizes are limited to 100KB - (it can be changed in the source code). - diff --git a/libacc/LICENSE b/libacc/LICENSE deleted file mode 100644 index aea41e08654..00000000000 --- a/libacc/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - Obfuscated Tiny C Compiler - - Copyright (C) 2001-2003 Fabrice Bellard - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product and its documentation - *is* required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - diff --git a/libacc/acc.cpp b/libacc/acc.cpp deleted file mode 100644 index 808752e3070..00000000000 --- a/libacc/acc.cpp +++ /dev/null @@ -1,6241 +0,0 @@ -/* - * Android "Almost" C Compiler. - * This is a compiler for a small subset of the C language, intended for use - * in scripting environments where speed and memory footprint are important. - * - * This code is based upon the "unobfuscated" version of the - * Obfuscated Tiny C compiler, see the file LICENSE for details. - * - */ - -#define LOG_TAG "acc" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#if defined(__i386__) -#include -#endif - - -#if defined(__arm__) -#define DEFAULT_ARM_CODEGEN -#define PROVIDE_ARM_CODEGEN -#elif defined(__i386__) -#define DEFAULT_X86_CODEGEN -#define PROVIDE_X86_CODEGEN -#elif defined(__x86_64__) -#define DEFAULT_X64_CODEGEN -#define PROVIDE_X64_CODEGEN -#endif - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) -#define ARM_USE_VFP -#endif - -#include - -#define LOG_API(...) do {} while(0) -// #define LOG_API(...) fprintf (stderr, __VA_ARGS__) - -#define LOG_STACK(...) do {} while(0) -// #define LOG_STACK(...) fprintf (stderr, __VA_ARGS__) - -// #define PROVIDE_TRACE_CODEGEN - -// Uncomment to disable ARM peephole optimizations -// #define DISABLE_ARM_PEEPHOLE - -// Uncomment to save input to a text file in DEBUG_DUMP_PATTERN -// #define DEBUG_SAVE_INPUT_TO_FILE - -#ifdef DEBUG_SAVE_INPUT_TO_FILE -#ifdef ARM_USE_VFP -#define DEBUG_DUMP_PATTERN "/data/misc/acc_dump/%d.c" -#else -#define DEBUG_DUMP_PATTERN "/tmp/acc_dump/%d.c" -#endif -#endif - -#define assert(b) assertImpl(b, __LINE__) - -namespace acc { - -// Subset of STL vector. -template class Vector { - public: - Vector() { - mpBase = 0; - mUsed = 0; - mSize = 0; - } - - ~Vector() { - if (mpBase) { - for(size_t i = 0; i < mUsed; i++) { - mpBase[mUsed].~E(); - } - free(mpBase); - } - } - - inline E& operator[](size_t i) { - return mpBase[i]; - } - - inline E& front() { - return mpBase[0]; - } - - inline E& back() { - return mpBase[mUsed - 1]; - } - - void pop_back() { - mUsed -= 1; - mpBase[mUsed].~E(); - } - - void push_back(const E& item) { - * ensure(1) = item; - } - - size_t size() { - return mUsed; - } - -private: - E* ensure(int n) { - size_t newUsed = mUsed + n; - if (newUsed > mSize) { - size_t newSize = mSize * 2 + 10; - if (newSize < newUsed) { - newSize = newUsed; - } - mpBase = (E*) realloc(mpBase, sizeof(E) * newSize); - mSize = newSize; - } - E* result = mpBase + mUsed; - mUsed = newUsed; - return result; - } - - E* mpBase; - size_t mUsed; - size_t mSize; -}; - -class ErrorSink { -public: - void error(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - verror(fmt, ap); - va_end(ap); - } - - virtual ~ErrorSink() {} - virtual void verror(const char* fmt, va_list ap) = 0; -}; - -class Compiler : public ErrorSink { - typedef int tokenid_t; - enum TypeTag { - TY_INT, // 0 - TY_CHAR, // 1 - TY_SHORT, // 2 - TY_VOID, // 3 - TY_FLOAT, // 4 - TY_DOUBLE, // 5 - TY_POINTER, // 6 - TY_ARRAY, // 7 - TY_STRUCT, // 8 - TY_FUNC, // 9 - TY_PARAM // 10 - }; - - struct Type { - TypeTag tag; - tokenid_t id; // For function arguments, global vars, local vars, struct elements - tokenid_t structTag; // For structs the name of the struct - int length; // length of array, offset of struct element. -1 means struct is forward defined - int alignment; // for structs only - Type* pHead; // For a struct this is the prototype struct. - Type* pTail; - }; - - enum ExpressionType { - ET_RVALUE, - ET_LVALUE - }; - - struct ExpressionValue { - ExpressionValue() { - et = ET_RVALUE; - pType = NULL; - } - ExpressionType et; - Type* pType; - }; - - class ICodeBuf { - public: - virtual ~ICodeBuf() {} - virtual void init(int size) = 0; - virtual void setErrorSink(ErrorSink* pErrorSink) = 0; - virtual void o4(int n) = 0; - virtual void ob(int n) = 0; - virtual void* getBase() = 0; - virtual intptr_t getSize() = 0; - virtual intptr_t getPC() = 0; - // Call this before trying to modify code in the buffer. - virtual void flush() = 0; - }; - - class CodeBuf : public ICodeBuf { - char* ind; // Output code pointer - char* pProgramBase; - ErrorSink* mErrorSink; - int mSize; - bool mOverflowed; - - void release() { - if (pProgramBase != 0) { - free(pProgramBase); - pProgramBase = 0; - } - } - - bool check(int n) { - int newSize = ind - pProgramBase + n; - bool overflow = newSize > mSize; - if (overflow && !mOverflowed) { - mOverflowed = true; - if (mErrorSink) { - mErrorSink->error("Code too large: %d bytes", newSize); - } - } - return overflow; - } - - public: - CodeBuf() { - pProgramBase = 0; - ind = 0; - mErrorSink = 0; - mSize = 0; - mOverflowed = false; - } - - virtual ~CodeBuf() { - release(); - } - - virtual void init(int size) { - release(); - mSize = size; - pProgramBase = (char*) calloc(1, size); - ind = pProgramBase; - } - - virtual void setErrorSink(ErrorSink* pErrorSink) { - mErrorSink = pErrorSink; - } - - virtual void o4(int n) { - if(check(4)) { - return; - } - * (int*) ind = n; - ind += 4; - } - - /* - * Output a byte. Handles all values, 0..ff. - */ - virtual void ob(int n) { - if(check(1)) { - return; - } - *ind++ = n; - } - - virtual void* getBase() { - return (void*) pProgramBase; - } - - virtual intptr_t getSize() { - return ind - pProgramBase; - } - - virtual intptr_t getPC() { - return (intptr_t) ind; - } - - virtual void flush() {} - }; - - /** - * A code generator creates an in-memory program, generating the code on - * the fly. There is one code generator implementation for each supported - * architecture. - * - * The code generator implements the following abstract machine: - * R0 - the accumulator. - * FP - a frame pointer for accessing function arguments and local - * variables. - * SP - a stack pointer for storing intermediate results while evaluating - * expressions. The stack pointer grows downwards. - * - * The function calling convention is that all arguments are placed on the - * stack such that the first argument has the lowest address. - * After the call, the result is in R0. The caller is responsible for - * removing the arguments from the stack. - * The R0 register is not saved across function calls. The - * FP and SP registers are saved. - */ - - class CodeGenerator { - public: - CodeGenerator() { - mErrorSink = 0; - pCodeBuf = 0; - pushType(); - } - virtual ~CodeGenerator() {} - - virtual void init(ICodeBuf* pCodeBuf) { - this->pCodeBuf = pCodeBuf; - pCodeBuf->setErrorSink(mErrorSink); - } - - virtual void setErrorSink(ErrorSink* pErrorSink) { - mErrorSink = pErrorSink; - if (pCodeBuf) { - pCodeBuf->setErrorSink(mErrorSink); - } - } - - /* Give the code generator some utility types so it can - * use its own types as needed for the results of some - * operations like gcmp. - */ - - void setTypes(Type* pInt) { - mkpInt = pInt; - } - - /* Emit a function prolog. - * pDecl is the function declaration, which gives the arguments. - * Save the old value of the FP. - * Set the new value of the FP. - * Convert from the native platform calling convention to - * our stack-based calling convention. This may require - * pushing arguments from registers to the stack. - * Allocate "N" bytes of stack space. N isn't known yet, so - * just emit the instructions for adjusting the stack, and return - * the address to patch up. The patching will be done in - * functionExit(). - * returns address to patch with local variable size. - */ - virtual int functionEntry(Type* pDecl) = 0; - - /* Emit a function epilog. - * Restore the old SP and FP register values. - * Return to the calling function. - * argCount - the number of arguments to the function. - * localVariableAddress - returned from functionEntry() - * localVariableSize - the size in bytes of the local variables. - */ - virtual void functionExit(Type* pDecl, int localVariableAddress, - int localVariableSize) = 0; - - /* load immediate value to R0 */ - virtual void li(int i) = 0; - - /* Load floating point value from global address. */ - virtual void loadFloat(int address, Type* pType) = 0; - - /* Add the struct offset in bytes to R0, change the type to pType */ - virtual void addStructOffsetR0(int offset, Type* pType) = 0; - - /* Jump to a target, and return the address of the word that - * holds the target data, in case it needs to be fixed up later. - */ - virtual int gjmp(int t) = 0; - - /* Test R0 and jump to a target if the test succeeds. - * l = 0: je, l == 1: jne - * Return the address of the word that holds the targed data, in - * case it needs to be fixed up later. - */ - virtual int gtst(bool l, int t) = 0; - - /* Compare TOS against R0, and store the boolean result in R0. - * Pops TOS. - * op specifies the comparison. - */ - virtual void gcmp(int op) = 0; - - /* Perform the arithmetic op specified by op. TOS is the - * left argument, R0 is the right argument. - * Pops TOS. - */ - virtual void genOp(int op) = 0; - - /* Compare 0 against R0, and store the boolean result in R0. - * op specifies the comparison. - */ - virtual void gUnaryCmp(int op) = 0; - - /* Perform the arithmetic op specified by op. 0 is the - * left argument, R0 is the right argument. - */ - virtual void genUnaryOp(int op) = 0; - - /* Push R0 onto the stack. (Also known as "dup" for duplicate.) - */ - virtual void pushR0() = 0; - - /* Turn R0, TOS into R0 TOS R0 */ - - virtual void over() = 0; - - /* Pop R0 from the stack. (Also known as "drop") - */ - virtual void popR0() = 0; - - /* Store R0 to the address stored in TOS. - * The TOS is popped. - */ - virtual void storeR0ToTOS() = 0; - - /* Load R0 from the address stored in R0. - */ - virtual void loadR0FromR0() = 0; - - /* Load the absolute address of a variable to R0. - * If ea <= LOCAL, then this is a local variable, or an - * argument, addressed relative to FP. - * else it is an absolute global address. - * - * et is ET_RVALUE for things like string constants, ET_LVALUE for - * variables. - */ - virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) = 0; - - /* Load the pc-relative address of a forward-referenced variable to R0. - * Return the address of the 4-byte constant so that it can be filled - * in later. - */ - virtual int leaForward(int ea, Type* pPointerType) = 0; - - /** - * Convert R0 to the given type. - */ - - void convertR0(Type* pType) { - convertR0Imp(pType, false); - } - - void castR0(Type* pType) { - convertR0Imp(pType, true); - } - - virtual void convertR0Imp(Type* pType, bool isCast) = 0; - - /* Emit code to adjust the stack for a function call. Return the - * label for the address of the instruction that adjusts the - * stack size. This will be passed as argument "a" to - * endFunctionCallArguments. - */ - virtual int beginFunctionCallArguments() = 0; - - /* Emit code to store R0 to the stack at byte offset l. - * Returns stack size of object (typically 4 or 8 bytes) - */ - virtual size_t storeR0ToArg(int l, Type* pArgType) = 0; - - /* Patch the function call preamble. - * a is the address returned from beginFunctionCallArguments - * l is the number of bytes the arguments took on the stack. - * Typically you would also emit code to convert the argument - * list into whatever the native function calling convention is. - * On ARM for example you would pop the first 5 arguments into - * R0..R4 - */ - virtual void endFunctionCallArguments(Type* pDecl, int a, int l) = 0; - - /* Emit a call to an unknown function. The argument "symbol" needs to - * be stored in the location where the address should go. It forms - * a chain. The address will be patched later. - * Return the address of the word that has to be patched. - */ - virtual int callForward(int symbol, Type* pFunc) = 0; - - /* Call a function pointer. L is the number of bytes the arguments - * take on the stack. The address of the function is stored at - * location SP + l. - */ - virtual void callIndirect(int l, Type* pFunc) = 0; - - /* Adjust SP after returning from a function call. l is the - * number of bytes of arguments stored on the stack. isIndirect - * is true if this was an indirect call. (In which case the - * address of the function is stored at location SP + l.) - */ - virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) = 0; - - /* Generate a symbol at the current PC. t is the head of a - * linked list of addresses to patch. - */ - virtual void gsym(int t) = 0; - - /* Resolve a forward reference function at the current PC. - * t is the head of a - * linked list of addresses to patch. - * (Like gsym, but using absolute address, not PC relative address.) - */ - virtual void resolveForward(int t) = 0; - - /* - * Do any cleanup work required at the end of a compile. - * For example, an instruction cache might need to be - * invalidated. - * Return non-zero if there is an error. - */ - virtual int finishCompile() = 0; - - /** - * Adjust relative branches by this amount. - */ - virtual int jumpOffset() = 0; - - /** - * Memory alignment (in bytes) for this type of data - */ - virtual size_t alignmentOf(Type* type) = 0; - - /** - * Array element alignment (in bytes) for this type of data. - */ - virtual size_t sizeOf(Type* type) = 0; - - virtual Type* getR0Type() { - return mExpressionStack.back().pType; - } - - virtual ExpressionType getR0ExpressionType() { - return mExpressionStack.back().et; - } - - virtual void setR0ExpressionType(ExpressionType et) { - mExpressionStack.back().et = et; - } - - virtual size_t getExpressionStackDepth() { - return mExpressionStack.size(); - } - - virtual void forceR0RVal() { - if (getR0ExpressionType() == ET_LVALUE) { - loadR0FromR0(); - } - } - - protected: - /* - * Output a byte. Handles all values, 0..ff. - */ - void ob(int n) { - pCodeBuf->ob(n); - } - - void o4(int data) { - pCodeBuf->o4(data); - } - - intptr_t getBase() { - return (intptr_t) pCodeBuf->getBase(); - } - - intptr_t getPC() { - return pCodeBuf->getPC(); - } - - intptr_t getSize() { - return pCodeBuf->getSize(); - } - - void flush() { - pCodeBuf->flush(); - } - - void error(const char* fmt,...) { - va_list ap; - va_start(ap, fmt); - mErrorSink->verror(fmt, ap); - va_end(ap); - } - - void assertImpl(bool test, int line) { - if (!test) { - error("code generator assertion failed at line %s:%d.", __FILE__, line); - LOGD("code generator assertion failed at line %s:%d.", __FILE__, line); - * (char*) 0 = 0; - } - } - - void setR0Type(Type* pType) { - assert(pType != NULL); - mExpressionStack.back().pType = pType; - mExpressionStack.back().et = ET_RVALUE; - } - - void setR0Type(Type* pType, ExpressionType et) { - assert(pType != NULL); - mExpressionStack.back().pType = pType; - mExpressionStack.back().et = et; - } - - Type* getTOSType() { - return mExpressionStack[mExpressionStack.size()-2].pType; - } - - void pushType() { - if (mExpressionStack.size()) { - mExpressionStack.push_back(mExpressionStack.back()); - } else { - mExpressionStack.push_back(ExpressionValue()); - } - - } - - void overType() { - size_t size = mExpressionStack.size(); - if (size >= 2) { - mExpressionStack.push_back(mExpressionStack.back()); - mExpressionStack[size-1] = mExpressionStack[size-2]; - mExpressionStack[size-2] = mExpressionStack[size]; - } - } - - void popType() { - mExpressionStack.pop_back(); - } - - bool bitsSame(Type* pA, Type* pB) { - return collapseType(pA->tag) == collapseType(pB->tag); - } - - TypeTag collapseType(TypeTag tag) { - static const TypeTag collapsedTag[] = { - TY_INT, - TY_INT, - TY_INT, - TY_VOID, - TY_FLOAT, - TY_DOUBLE, - TY_INT, - TY_INT, - TY_VOID, - TY_VOID, - TY_VOID - }; - return collapsedTag[tag]; - } - - TypeTag collapseTypeR0() { - return collapseType(getR0Type()->tag); - } - - static bool isFloatType(Type* pType) { - return isFloatTag(pType->tag); - } - - static bool isFloatTag(TypeTag tag) { - return tag == TY_FLOAT || tag == TY_DOUBLE; - } - - static bool isPointerType(Type* pType) { - return isPointerTag(pType->tag); - } - - static bool isPointerTag(TypeTag tag) { - return tag == TY_POINTER || tag == TY_ARRAY; - } - - Type* getPointerArithmeticResultType(Type* a, Type* b) { - TypeTag aTag = a->tag; - TypeTag bTag = b->tag; - if (aTag == TY_POINTER) { - return a; - } - if (bTag == TY_POINTER) { - return b; - } - if (aTag == TY_ARRAY) { - return a->pTail; - } - if (bTag == TY_ARRAY) { - return b->pTail; - } - return NULL; - } - Type* mkpInt; - - private: - Vector mExpressionStack; - ICodeBuf* pCodeBuf; - ErrorSink* mErrorSink; - }; - -#ifdef PROVIDE_ARM_CODEGEN - - static size_t rotateRight(size_t n, size_t rotate) { - return (n >> rotate) | (n << (32 - rotate)); - } - - static size_t rotateLeft(size_t n, size_t rotate) { - return (n << rotate) | (n >> (32 - rotate)); - } - - static bool encode12BitImmediate(size_t immediate, size_t* pResult) { - for(size_t i = 0; i < 16; i++) { - size_t rotate = i * 2; - size_t mask = rotateRight(0xff, rotate); - if ((immediate | mask) == mask) { - size_t bits8 = rotateLeft(immediate, rotate); - // assert(bits8 <= 0xff); - *pResult = (i << 8) | bits8; - return true; - } - } - return false; - } - - static size_t decode12BitImmediate(size_t immediate) { - size_t data = immediate & 0xff; - size_t rotate = 2 * ((immediate >> 8) & 0xf); - return rotateRight(data, rotate); - } - - static bool isPowerOfTwo(size_t n) { - return (n != 0) & ((n & (n-1)) == 0); - } - - static size_t log2(size_t n) { - int result = 0; - while (n >>= 1) { - result++; - } - return result; - } - - class ARMCodeBuf : public ICodeBuf { - ICodeBuf* mpBase; - ErrorSink* mErrorSink; - - class CircularQueue { - static const int SIZE = 16; // Must be power of 2 - static const int MASK = SIZE-1; - unsigned int mBuf[SIZE]; - int mHead; - int mCount; - - public: - CircularQueue() { - mHead = 0; - mCount = 0; - } - - void pushBack(unsigned int data) { - mBuf[(mHead + mCount) & MASK] = data; - mCount += 1; - } - - unsigned int popFront() { - unsigned int result = mBuf[mHead]; - mHead = (mHead + 1) & MASK; - mCount -= 1; - return result; - } - - void popBack(int n) { - mCount -= n; - } - - inline int count() { - return mCount; - } - - bool empty() { - return mCount == 0; - } - - bool full() { - return mCount == SIZE; - } - - // The valid indexes are 1 - count() to 0 - unsigned int operator[](int i) { - return mBuf[(mHead + mCount + i) & MASK]; - } - }; - - CircularQueue mQ; - - void error(const char* fmt,...) { - va_list ap; - va_start(ap, fmt); - mErrorSink->verror(fmt, ap); - va_end(ap); - } - - void flush() { - while (!mQ.empty()) { - mpBase->o4(mQ.popFront()); - } - mpBase->flush(); - } - - public: - ARMCodeBuf(ICodeBuf* pBase) { - mpBase = pBase; - } - - virtual ~ARMCodeBuf() { - delete mpBase; - } - - void init(int size) { - mpBase->init(size); - } - - void setErrorSink(ErrorSink* pErrorSink) { - mErrorSink = pErrorSink; - mpBase->setErrorSink(pErrorSink); - } - - void o4(int n) { - if (mQ.full()) { - mpBase->o4(mQ.popFront()); - } - mQ.pushBack(n); - -#ifndef DISABLE_ARM_PEEPHOLE - // Peephole check - bool didPeep; - do { - static const unsigned int opMask = 0x01e00000; - static const unsigned int immediateMask = 0x00000fff; - static const unsigned int BMask = 0x00400000; - didPeep = false; - if (mQ.count() >= 4) { - - // Operand by a small constant - // push;mov #imm;pop;op ==> op #imm - - if (mQ[-4] == 0xe92d0001 && // stmfd r13!, {r0} - (mQ[-3] & ~immediateMask) == 0xe3a00000 && // mov r0, #X - mQ[-2] == 0xe8bd0002 && // ldmea r13!, {r1} - (mQ[-1] & ~opMask) == (0xe0810000 & ~opMask)) { // OP r0, r1, r0 - unsigned int movConst = mQ[-3]; - unsigned int op = mQ[-1]; - unsigned int combined = 0xe2000000 | (op & opMask) | (movConst & immediateMask); - // fprintf(stderr, "op %x movConst %x combined %x\n", op, movConst, combined); - if (! (combined == 0xe2800000 || combined == 0xe2400000)) { // add/sub #0 - mQ.popBack(4); - mQ.pushBack(combined); - didPeep = true; - } else { - mQ.popBack(4); - didPeep = true; - } - } - } - - // Load local variable - // sub r0,r11,#imm;ldr/ldrb r0,[r0] ==> ldr/ldrb r0, [r11,#-imm] - if (mQ.count() >= 2) { - if ((mQ[-2] & ~immediateMask) == 0xe24b0000) { // sub r0,r11,#imm - const unsigned int encodedImmediate = mQ[-2] & immediateMask; - const unsigned int ld = mQ[-1]; - if ((ld & ~BMask) == 0xe5900000) { // ldr{b} r0, [r0] - unsigned int combined = encodedImmediate | (0xE51B0000 | (ld & BMask)); // ldr r0, [r11, #-0] - mQ.popBack(2); - mQ.pushBack(combined); - didPeep = true; - } else if (ld == 0xedd07a00) { // ldcl p10, c7, [r0, #0x000] - unsigned int decodedImmediate = decode12BitImmediate(encodedImmediate); - if (decodedImmediate <= 1020 && ((decodedImmediate & 3) == 0)) { - unsigned int combined = (decodedImmediate >> 2) | 0xed5b7a00; // ldcl p10, c7, [r11, #-0] - mQ.popBack(2); - mQ.pushBack(combined); - didPeep = true; - } - } - } - } - - // Constant array lookup - - if (mQ.count() >= 6 && - mQ[-6] == 0xe92d0001 && // stmfd r13!, {r0} - (mQ[-5] & ~immediateMask)== 0xe3a00000 && // mov r0, #0x00000001 - mQ[-4] == 0xe8bd0002 && // ldmea r13!, {r1} - (mQ[-3] & ~immediateMask)== 0xe3a02000 && // mov r2, #0x00000004 - mQ[-2] == 0xe0000092 && // mul r0, r2, r0 - mQ[-1] == 0xe0810000) { // add r0, r1, r0 - unsigned int mov1 = mQ[-5]; - unsigned int mov2 = mQ[-3]; - unsigned int const1 = decode12BitImmediate(mov1); - unsigned int const2 = decode12BitImmediate(mov2); - unsigned int comboConst = const1 * const2; - size_t immediate = 0; - if (encode12BitImmediate(comboConst, &immediate)) { - mQ.popBack(6); - unsigned int add = immediate | 0xE2800000; // add r0, r0, #n - if (comboConst) { - mQ.pushBack(add); - } - didPeep = true; - } - } - - // Pointer arithmetic with a stride that is a power of two - - if (mQ.count() >= 3 && - (mQ[-3] & ~ immediateMask) == 0xe3a02000 && // mov r2, #stride - mQ[-2] == 0xe0000092 && // mul r0, r2, r0 - mQ[-1] == 0xe0810000) { // add r0, r1, r0 - int stride = decode12BitImmediate(mQ[-3]); - if (isPowerOfTwo(stride)) { - mQ.popBack(3); - unsigned int add = 0xe0810000 | (log2(stride) << 7); // add r0, r1, r0, LSL #log2(stride) - mQ.pushBack(add); - didPeep = true; - } - } - - } while (didPeep); -#endif - } - - void ob(int n) { - error("ob() not supported."); - } - - void* getBase() { - flush(); - return mpBase->getBase(); - } - - intptr_t getSize() { - flush(); - return mpBase->getSize(); - } - - intptr_t getPC() { - flush(); - return mpBase->getPC(); - } - }; - - class ARMCodeGenerator : public CodeGenerator { - public: - ARMCodeGenerator() { -#ifdef ARM_USE_VFP - // LOGD("Using ARM VFP hardware floating point."); -#else - // LOGD("Using ARM soft floating point."); -#endif - } - - virtual ~ARMCodeGenerator() {} - - /* returns address to patch with local variable size - */ - virtual int functionEntry(Type* pDecl) { - mStackUse = 0; - // sp -> arg4 arg5 ... - // Push our register-based arguments back on the stack - int regArgCount = calcRegArgCount(pDecl); - if (regArgCount > 0) { - mStackUse += regArgCount * 4; - o4(0xE92D0000 | ((1 << regArgCount) - 1)); // stmfd sp!, {} - } - // sp -> arg0 arg1 ... - o4(0xE92D4800); // stmfd sp!, {fp, lr} - mStackUse += 2 * 4; - // sp, fp -> oldfp, retadr, arg0 arg1 .... - o4(0xE1A0B00D); // mov fp, sp - LOG_STACK("functionEntry: %d\n", mStackUse); - int pc = getPC(); - o4(0xE24DD000); // sub sp, sp, # - // We don't know how many local variables we are going to use, - // but we will round the allocation up to a multiple of - // STACK_ALIGNMENT, so it won't affect the stack alignment. - return pc; - } - - virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) { - // Round local variable size up to a multiple of stack alignment - localVariableSize = ((localVariableSize + STACK_ALIGNMENT - 1) / - STACK_ALIGNMENT) * STACK_ALIGNMENT; - // Patch local variable allocation code: - if (localVariableSize < 0 || localVariableSize > 255) { - error("localVariables out of range: %d", localVariableSize); - } - *(char*) (localVariableAddress) = localVariableSize; - -#ifdef ARM_USE_VFP - { - Type* pReturnType = pDecl->pHead; - switch(pReturnType->tag) { - case TY_FLOAT: - o4(0xEE170A90); // fmrs r0, s15 - break; - case TY_DOUBLE: - o4(0xEC510B17); // fmrrd r0, r1, d7 - break; - default: - break; - } - } -#endif - - // sp -> locals .... fp -> oldfp, retadr, arg0, arg1, ... - o4(0xE1A0E00B); // mov lr, fp - o4(0xE59BB000); // ldr fp, [fp] - o4(0xE28ED004); // add sp, lr, #4 - // sp -> retadr, arg0, ... - o4(0xE8BD4000); // ldmfd sp!, {lr} - // sp -> arg0 .... - - // We store the PC into the lr so we can adjust the sp before - // returning. We need to pull off the registers we pushed - // earlier. We don't need to actually store them anywhere, - // just adjust the stack. - int regArgCount = calcRegArgCount(pDecl); - if (regArgCount) { - o4(0xE28DD000 | (regArgCount << 2)); // add sp, sp, #argCount << 2 - } - o4(0xE12FFF1E); // bx lr - } - - /* load immediate value */ - virtual void li(int t) { - liReg(t, 0); - setR0Type(mkpInt); - } - - virtual void loadFloat(int address, Type* pType) { - setR0Type(pType); - // Global, absolute address - o4(0xE59F0000); // ldr r0, .L1 - o4(0xEA000000); // b .L99 - o4(address); // .L1: .word ea - // .L99: - - switch (pType->tag) { - case TY_FLOAT: -#ifdef ARM_USE_VFP - o4(0xEDD07A00); // flds s15, [r0] -#else - o4(0xE5900000); // ldr r0, [r0] -#endif - break; - case TY_DOUBLE: -#ifdef ARM_USE_VFP - o4(0xED907B00); // fldd d7, [r0] -#else - o4(0xE1C000D0); // ldrd r0, [r0] -#endif - break; - default: - assert(false); - break; - } - } - - - virtual void addStructOffsetR0(int offset, Type* pType) { - if (offset) { - size_t immediate = 0; - if (encode12BitImmediate(offset, &immediate)) { - o4(0xE2800000 | immediate); // add r0, r0, #offset - } else { - error("structure offset out of range: %d", offset); - } - } - setR0Type(pType, ET_LVALUE); - } - - virtual int gjmp(int t) { - int pc = getPC(); - o4(0xEA000000 | encodeAddress(t)); // b .L33 - return pc; - } - - /* l = 0: je, l == 1: jne */ - virtual int gtst(bool l, int t) { - Type* pR0Type = getR0Type(); - TypeTag tagR0 = pR0Type->tag; - switch(tagR0) { - case TY_FLOAT: -#ifdef ARM_USE_VFP - o4(0xEEF57A40); // fcmpzs s15 - o4(0xEEF1FA10); // fmstat -#else - callRuntime((void*) runtime_is_non_zero_f); - o4(0xE3500000); // cmp r0,#0 -#endif - break; - case TY_DOUBLE: -#ifdef ARM_USE_VFP - o4(0xEEB57B40); // fcmpzd d7 - o4(0xEEF1FA10); // fmstat -#else - callRuntime((void*) runtime_is_non_zero_d); - o4(0xE3500000); // cmp r0,#0 -#endif - break; - default: - o4(0xE3500000); // cmp r0,#0 - break; - } - int branch = l ? 0x1A000000 : 0x0A000000; // bne : beq - int pc = getPC(); - o4(branch | encodeAddress(t)); - return pc; - } - - virtual void gcmp(int op) { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = collapseType(pR0Type->tag); - TypeTag tagTOS = collapseType(pTOSType->tag); - if (tagR0 == TY_INT && tagTOS == TY_INT) { - setupIntPtrArgs(); - o4(0xE1510000); // cmp r1, r1 - switch(op) { - case OP_EQUALS: - o4(0x03A00001); // moveq r0,#1 - o4(0x13A00000); // movne r0,#0 - break; - case OP_NOT_EQUALS: - o4(0x03A00000); // moveq r0,#0 - o4(0x13A00001); // movne r0,#1 - break; - case OP_LESS_EQUAL: - o4(0xD3A00001); // movle r0,#1 - o4(0xC3A00000); // movgt r0,#0 - break; - case OP_GREATER: - o4(0xD3A00000); // movle r0,#0 - o4(0xC3A00001); // movgt r0,#1 - break; - case OP_GREATER_EQUAL: - o4(0xA3A00001); // movge r0,#1 - o4(0xB3A00000); // movlt r0,#0 - break; - case OP_LESS: - o4(0xA3A00000); // movge r0,#0 - o4(0xB3A00001); // movlt r0,#1 - break; - default: - error("Unknown comparison op %d", op); - break; - } - } else if (tagR0 == TY_DOUBLE || tagTOS == TY_DOUBLE) { - setupDoubleArgs(); -#ifdef ARM_USE_VFP - o4(0xEEB46BC7); // fcmped d6, d7 - o4(0xEEF1FA10); // fmstat - switch(op) { - case OP_EQUALS: - o4(0x03A00001); // moveq r0,#1 - o4(0x13A00000); // movne r0,#0 - break; - case OP_NOT_EQUALS: - o4(0x03A00000); // moveq r0,#0 - o4(0x13A00001); // movne r0,#1 - break; - case OP_LESS_EQUAL: - o4(0xD3A00001); // movle r0,#1 - o4(0xC3A00000); // movgt r0,#0 - break; - case OP_GREATER: - o4(0xD3A00000); // movle r0,#0 - o4(0xC3A00001); // movgt r0,#1 - break; - case OP_GREATER_EQUAL: - o4(0xA3A00001); // movge r0,#1 - o4(0xB3A00000); // movlt r0,#0 - break; - case OP_LESS: - o4(0xA3A00000); // movge r0,#0 - o4(0xB3A00001); // movlt r0,#1 - break; - default: - error("Unknown comparison op %d", op); - break; - } -#else - switch(op) { - case OP_EQUALS: - callRuntime((void*) runtime_cmp_eq_dd); - break; - case OP_NOT_EQUALS: - callRuntime((void*) runtime_cmp_ne_dd); - break; - case OP_LESS_EQUAL: - callRuntime((void*) runtime_cmp_le_dd); - break; - case OP_GREATER: - callRuntime((void*) runtime_cmp_gt_dd); - break; - case OP_GREATER_EQUAL: - callRuntime((void*) runtime_cmp_ge_dd); - break; - case OP_LESS: - callRuntime((void*) runtime_cmp_lt_dd); - break; - default: - error("Unknown comparison op %d", op); - break; - } -#endif - } else { - setupFloatArgs(); -#ifdef ARM_USE_VFP - o4(0xEEB47AE7); // fcmpes s14, s15 - o4(0xEEF1FA10); // fmstat - switch(op) { - case OP_EQUALS: - o4(0x03A00001); // moveq r0,#1 - o4(0x13A00000); // movne r0,#0 - break; - case OP_NOT_EQUALS: - o4(0x03A00000); // moveq r0,#0 - o4(0x13A00001); // movne r0,#1 - break; - case OP_LESS_EQUAL: - o4(0xD3A00001); // movle r0,#1 - o4(0xC3A00000); // movgt r0,#0 - break; - case OP_GREATER: - o4(0xD3A00000); // movle r0,#0 - o4(0xC3A00001); // movgt r0,#1 - break; - case OP_GREATER_EQUAL: - o4(0xA3A00001); // movge r0,#1 - o4(0xB3A00000); // movlt r0,#0 - break; - case OP_LESS: - o4(0xA3A00000); // movge r0,#0 - o4(0xB3A00001); // movlt r0,#1 - break; - default: - error("Unknown comparison op %d", op); - break; - } -#else - switch(op) { - case OP_EQUALS: - callRuntime((void*) runtime_cmp_eq_ff); - break; - case OP_NOT_EQUALS: - callRuntime((void*) runtime_cmp_ne_ff); - break; - case OP_LESS_EQUAL: - callRuntime((void*) runtime_cmp_le_ff); - break; - case OP_GREATER: - callRuntime((void*) runtime_cmp_gt_ff); - break; - case OP_GREATER_EQUAL: - callRuntime((void*) runtime_cmp_ge_ff); - break; - case OP_LESS: - callRuntime((void*) runtime_cmp_lt_ff); - break; - default: - error("Unknown comparison op %d", op); - break; - } -#endif - } - setR0Type(mkpInt); - } - - virtual void genOp(int op) { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = pR0Type->tag; - TypeTag tagTOS = pTOSType->tag; - bool isFloatR0 = isFloatTag(tagR0); - bool isFloatTOS = isFloatTag(tagTOS); - if (!isFloatR0 && !isFloatTOS) { - setupIntPtrArgs(); - bool isPtrR0 = isPointerTag(tagR0); - bool isPtrTOS = isPointerTag(tagTOS); - if (isPtrR0 || isPtrTOS) { - if (isPtrR0 && isPtrTOS) { - if (op != OP_MINUS) { - error("Unsupported pointer-pointer operation %d.", op); - } - if (! typeEqual(pR0Type, pTOSType)) { - error("Incompatible pointer types for subtraction."); - } - o4(0xE0410000); // sub r0,r1,r0 - setR0Type(mkpInt); - int size = sizeOf(pR0Type->pHead); - if (size != 1) { - pushR0(); - li(size); - // TODO: Optimize for power-of-two. - genOp(OP_DIV); - } - } else { - if (! (op == OP_PLUS || (op == OP_MINUS && isPtrR0))) { - error("Unsupported pointer-scalar operation %d", op); - } - Type* pPtrType = getPointerArithmeticResultType( - pR0Type, pTOSType); - int size = sizeOf(pPtrType->pHead); - if (size != 1) { - // TODO: Optimize for power-of-two. - liReg(size, 2); - if (isPtrR0) { - o4(0x0E0010192); // mul r1,r2,r1 - } else { - o4(0x0E0000092); // mul r0,r2,r0 - } - } - switch(op) { - case OP_PLUS: - o4(0xE0810000); // add r0,r1,r0 - break; - case OP_MINUS: - o4(0xE0410000); // sub r0,r1,r0 - break; - } - setR0Type(pPtrType); - } - } else { - switch(op) { - case OP_MUL: - o4(0x0E0000091); // mul r0,r1,r0 - break; - case OP_DIV: - callRuntime((void*) runtime_DIV); - break; - case OP_MOD: - callRuntime((void*) runtime_MOD); - break; - case OP_PLUS: - o4(0xE0810000); // add r0,r1,r0 - break; - case OP_MINUS: - o4(0xE0410000); // sub r0,r1,r0 - break; - case OP_SHIFT_LEFT: - o4(0xE1A00011); // lsl r0,r1,r0 - break; - case OP_SHIFT_RIGHT: - o4(0xE1A00051); // asr r0,r1,r0 - break; - case OP_BIT_AND: - o4(0xE0010000); // and r0,r1,r0 - break; - case OP_BIT_XOR: - o4(0xE0210000); // eor r0,r1,r0 - break; - case OP_BIT_OR: - o4(0xE1810000); // orr r0,r1,r0 - break; - case OP_BIT_NOT: - o4(0xE1E00000); // mvn r0, r0 - break; - default: - error("Unimplemented op %d\n", op); - break; - } - } - } else { - Type* pResultType = tagR0 > tagTOS ? pR0Type : pTOSType; - if (pResultType->tag == TY_DOUBLE) { - setupDoubleArgs(); - - switch(op) { - case OP_MUL: -#ifdef ARM_USE_VFP - o4(0xEE267B07); // fmuld d7, d6, d7 -#else - callRuntime((void*) runtime_op_mul_dd); -#endif - break; - case OP_DIV: -#ifdef ARM_USE_VFP - o4(0xEE867B07); // fdivd d7, d6, d7 -#else - callRuntime((void*) runtime_op_div_dd); -#endif - break; - case OP_PLUS: -#ifdef ARM_USE_VFP - o4(0xEE367B07); // faddd d7, d6, d7 -#else - callRuntime((void*) runtime_op_add_dd); -#endif - break; - case OP_MINUS: -#ifdef ARM_USE_VFP - o4(0xEE367B47); // fsubd d7, d6, d7 -#else - callRuntime((void*) runtime_op_sub_dd); -#endif - break; - default: - error("Unsupported binary floating operation %d\n", op); - break; - } - } else { - setupFloatArgs(); - switch(op) { - case OP_MUL: -#ifdef ARM_USE_VFP - o4(0xEE677A27); // fmuls s15, s14, s15 -#else - callRuntime((void*) runtime_op_mul_ff); -#endif - break; - case OP_DIV: -#ifdef ARM_USE_VFP - o4(0xEEC77A27); // fdivs s15, s14, s15 -#else - callRuntime((void*) runtime_op_div_ff); -#endif - break; - case OP_PLUS: -#ifdef ARM_USE_VFP - o4(0xEE777A27); // fadds s15, s14, s15 -#else - callRuntime((void*) runtime_op_add_ff); -#endif - break; - case OP_MINUS: -#ifdef ARM_USE_VFP - o4(0xEE777A67); // fsubs s15, s14, s15 -#else - callRuntime((void*) runtime_op_sub_ff); -#endif - break; - default: - error("Unsupported binary floating operation %d\n", op); - break; - } - } - setR0Type(pResultType); - } - } - - virtual void gUnaryCmp(int op) { - if (op != OP_LOGICAL_NOT) { - error("Unknown unary cmp %d", op); - } else { - Type* pR0Type = getR0Type(); - TypeTag tag = collapseType(pR0Type->tag); - switch(tag) { - case TY_INT: - o4(0xE3A01000); // mov r1, #0 - o4(0xE1510000); // cmp r1, r0 - o4(0x03A00001); // moveq r0,#1 - o4(0x13A00000); // movne r0,#0 - break; - case TY_FLOAT: -#ifdef ARM_USE_VFP - o4(0xEEF57A40); // fcmpzs s15 - o4(0xEEF1FA10); // fmstat - o4(0x03A00001); // moveq r0,#1 - o4(0x13A00000); // movne r0,#0 -#else - callRuntime((void*) runtime_is_zero_f); -#endif - break; - case TY_DOUBLE: -#ifdef ARM_USE_VFP - o4(0xEEB57B40); // fcmpzd d7 - o4(0xEEF1FA10); // fmstat - o4(0x03A00001); // moveq r0,#1 - o4(0x13A00000); // movne r0,#0 -#else - callRuntime((void*) runtime_is_zero_d); -#endif - break; - default: - error("gUnaryCmp unsupported type"); - break; - } - } - setR0Type(mkpInt); - } - - virtual void genUnaryOp(int op) { - Type* pR0Type = getR0Type(); - TypeTag tag = collapseType(pR0Type->tag); - switch(tag) { - case TY_INT: - switch(op) { - case OP_MINUS: - o4(0xE3A01000); // mov r1, #0 - o4(0xE0410000); // sub r0,r1,r0 - break; - case OP_BIT_NOT: - o4(0xE1E00000); // mvn r0, r0 - break; - default: - error("Unknown unary op %d\n", op); - break; - } - break; - case TY_FLOAT: - case TY_DOUBLE: - switch (op) { - case OP_MINUS: - if (tag == TY_FLOAT) { -#ifdef ARM_USE_VFP - o4(0xEEF17A67); // fnegs s15, s15 -#else - callRuntime((void*) runtime_op_neg_f); -#endif - } else { -#ifdef ARM_USE_VFP - o4(0xEEB17B47); // fnegd d7, d7 -#else - callRuntime((void*) runtime_op_neg_d); -#endif - } - break; - case OP_BIT_NOT: - error("Can't apply '~' operator to a float or double."); - break; - default: - error("Unknown unary op %d\n", op); - break; - } - break; - default: - error("genUnaryOp unsupported type"); - break; - } - } - - virtual void pushR0() { - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); - -#ifdef ARM_USE_VFP - switch (r0ct ) { - case TY_FLOAT: - o4(0xED6D7A01); // fstmfds sp!,{s15} - mStackUse += 4; - break; - case TY_DOUBLE: - o4(0xED2D7B02); // fstmfdd sp!,{d7} - mStackUse += 8; - break; - default: - o4(0xE92D0001); // stmfd sp!,{r0} - mStackUse += 4; - } -#else - - if (r0ct != TY_DOUBLE) { - o4(0xE92D0001); // stmfd sp!,{r0} - mStackUse += 4; - } else { - o4(0xE92D0003); // stmfd sp!,{r0,r1} - mStackUse += 8; - } -#endif - pushType(); - LOG_STACK("pushR0: %d\n", mStackUse); - } - - virtual void over() { - // We know it's only used for int-ptr ops (++/--) - - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); - - Type* pTOSType = getTOSType(); - TypeTag tosct = collapseType(pTOSType->tag); - - assert (r0ct == TY_INT && tosct == TY_INT); - - o4(0xE8BD0002); // ldmfd sp!,{r1} - o4(0xE92D0001); // stmfd sp!,{r0} - o4(0xE92D0002); // stmfd sp!,{r1} - overType(); - mStackUse += 4; - } - - virtual void popR0() { - Type* pTOSType = getTOSType(); - TypeTag tosct = collapseType(pTOSType->tag); -#ifdef ARM_USE_VFP - if (tosct == TY_FLOAT || tosct == TY_DOUBLE) { - error("Unsupported popR0 float/double"); - } -#endif - switch (tosct){ - case TY_INT: - case TY_FLOAT: - o4(0xE8BD0001); // ldmfd sp!,{r0} - mStackUse -= 4; - break; - case TY_DOUBLE: - o4(0xE8BD0003); // ldmfd sp!,{r0, r1} // Restore R0 - mStackUse -= 8; - break; - default: - error("Can't pop this type."); - break; - } - popType(); - LOG_STACK("popR0: %d\n", mStackUse); - } - - virtual void storeR0ToTOS() { - Type* pPointerType = getTOSType(); - assert(pPointerType->tag == TY_POINTER); - Type* pDestType = pPointerType->pHead; - convertR0(pDestType); - o4(0xE8BD0004); // ldmfd sp!,{r2} - popType(); - mStackUse -= 4; - switch (pDestType->tag) { - case TY_POINTER: - case TY_INT: - o4(0xE5820000); // str r0, [r2] - break; - case TY_FLOAT: -#ifdef ARM_USE_VFP - o4(0xEDC27A00); // fsts s15, [r2, #0] -#else - o4(0xE5820000); // str r0, [r2] -#endif - break; - case TY_SHORT: - o4(0xE1C200B0); // strh r0, [r2] - break; - case TY_CHAR: - o4(0xE5C20000); // strb r0, [r2] - break; - case TY_DOUBLE: -#ifdef ARM_USE_VFP - o4(0xED827B00); // fstd d7, [r2, #0] -#else - o4(0xE1C200F0); // strd r0, [r2] -#endif - break; - case TY_STRUCT: - { - int size = sizeOf(pDestType); - if (size > 0) { - liReg(size, 1); - callRuntime((void*) runtime_structCopy); - } - } - break; - default: - error("storeR0ToTOS: unimplemented type %d", - pDestType->tag); - break; - } - setR0Type(pDestType); - } - - virtual void loadR0FromR0() { - Type* pPointerType = getR0Type(); - assert(pPointerType->tag == TY_POINTER); - Type* pNewType = pPointerType->pHead; - TypeTag tag = pNewType->tag; - switch (tag) { - case TY_POINTER: - case TY_INT: - o4(0xE5900000); // ldr r0, [r0] - break; - case TY_FLOAT: -#ifdef ARM_USE_VFP - o4(0xEDD07A00); // flds s15, [r0, #0] -#else - o4(0xE5900000); // ldr r0, [r0] -#endif - break; - case TY_SHORT: - o4(0xE1D000F0); // ldrsh r0, [r0] - break; - case TY_CHAR: - o4(0xE5D00000); // ldrb r0, [r0] - break; - case TY_DOUBLE: -#ifdef ARM_USE_VFP - o4(0xED907B00); // fldd d7, [r0, #0] -#else - o4(0xE1C000D0); // ldrd r0, [r0] -#endif - break; - case TY_ARRAY: - pNewType = pNewType->pTail; - break; - case TY_STRUCT: - break; - default: - error("loadR0FromR0: unimplemented type %d", tag); - break; - } - setR0Type(pNewType); - } - - virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) { - if (ea > -LOCAL && ea < LOCAL) { - // Local, fp relative - - size_t immediate = 0; - bool inRange = false; - if (ea < 0) { - inRange = encode12BitImmediate(-ea, &immediate); - o4(0xE24B0000 | immediate); // sub r0, fp, #ea - } else { - inRange = encode12BitImmediate(ea, &immediate); - o4(0xE28B0000 | immediate); // add r0, fp, #ea - } - if (! inRange) { - error("Offset out of range: %08x", ea); - } - } else { - // Global, absolute. - o4(0xE59F0000); // ldr r0, .L1 - o4(0xEA000000); // b .L99 - o4(ea); // .L1: .word 0 - // .L99: - } - setR0Type(pPointerType, et); - } - - virtual int leaForward(int ea, Type* pPointerType) { - setR0Type(pPointerType); - int result = ea; - int pc = getPC(); - int offset = 0; - if (ea) { - offset = (pc - ea - 8) >> 2; - if ((offset & 0xffff) != offset) { - error("function forward reference out of bounds"); - } - } else { - offset = 0; - } - o4(0xE59F0000 | offset); // ldr r0, .L1 - - if (ea == 0) { - o4(0xEA000000); // b .L99 - result = getPC(); - o4(ea); // .L1: .word 0 - // .L99: - } - return result; - } - - virtual void convertR0Imp(Type* pType, bool isCast){ - Type* pR0Type = getR0Type(); - if (isPointerType(pType) && isPointerType(pR0Type)) { - Type* pA = pR0Type; - Type* pB = pType; - // Array decays to pointer - if (pA->tag == TY_ARRAY && pB->tag == TY_POINTER) { - pA = pA->pTail; - } - if (! (typeEqual(pA, pB) - || pB->pHead->tag == TY_VOID - || (pA->tag == TY_POINTER && pB->tag == TY_POINTER && isCast) - )) { - error("Incompatible pointer or array types"); - } - } else if (bitsSame(pType, pR0Type)) { - // do nothing special - } else { - TypeTag r0Tag = collapseType(pR0Type->tag); - TypeTag destTag = collapseType(pType->tag); - if (r0Tag == TY_INT) { - if (destTag == TY_FLOAT) { -#ifdef ARM_USE_VFP - o4(0xEE070A90); // fmsr s15, r0 - o4(0xEEF87AE7); // fsitos s15, s15 - -#else - callRuntime((void*) runtime_int_to_float); -#endif - } else { - assert(destTag == TY_DOUBLE); -#ifdef ARM_USE_VFP - o4(0xEE070A90); // fmsr s15, r0 - o4(0xEEB87BE7); // fsitod d7, s15 - -#else - callRuntime((void*) runtime_int_to_double); -#endif - } - } else if (r0Tag == TY_FLOAT) { - if (destTag == TY_INT) { -#ifdef ARM_USE_VFP - o4(0xEEFD7AE7); // ftosizs s15, s15 - o4(0xEE170A90); // fmrs r0, s15 -#else - callRuntime((void*) runtime_float_to_int); -#endif - } else { - assert(destTag == TY_DOUBLE); -#ifdef ARM_USE_VFP - o4(0xEEB77AE7); // fcvtds d7, s15 -#else - callRuntime((void*) runtime_float_to_double); -#endif - } - } else { - if (r0Tag == TY_DOUBLE) { - if (destTag == TY_INT) { -#ifdef ARM_USE_VFP - o4(0xEEFD7BC7); // ftosizd s15, d7 - o4(0xEE170A90); // fmrs r0, s15 -#else - callRuntime((void*) runtime_double_to_int); -#endif - } else { - if(destTag == TY_FLOAT) { -#ifdef ARM_USE_VFP - o4(0xEEF77BC7); // fcvtsd s15, d7 -#else - callRuntime((void*) runtime_double_to_float); -#endif - } else { - incompatibleTypes(pR0Type, pType); - } - } - } else { - incompatibleTypes(pR0Type, pType); - } - } - } - setR0Type(pType); - } - - virtual int beginFunctionCallArguments() { - int pc = getPC(); - o4(0xE24DDF00); // Placeholder sub sp, sp, #0 - return pc; - } - - virtual size_t storeR0ToArg(int l, Type* pArgType) { - convertR0(pArgType); - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); -#ifdef ARM_USE_VFP - switch(r0ct) { - case TY_INT: - if (l < 0 || l > 4096-4) { - error("l out of range for stack offset: 0x%08x", l); - } - o4(0xE58D0000 | l); // str r0, [sp, #l] - return 4; - case TY_FLOAT: - if (l < 0 || l > 1020 || (l & 3)) { - error("l out of range for stack offset: 0x%08x", l); - } - o4(0xEDCD7A00 | (l >> 2)); // fsts s15, [sp, #l] - return 4; - case TY_DOUBLE: { - // Align to 8 byte boundary - int l2 = (l + 7) & ~7; - if (l2 < 0 || l2 > 1020 || (l2 & 3)) { - error("l out of range for stack offset: 0x%08x", l); - } - o4(0xED8D7B00 | (l2 >> 2)); // fstd d7, [sp, #l2] - return (l2 - l) + 8; - } - default: - assert(false); - return 0; - } -#else - switch(r0ct) { - case TY_INT: - case TY_FLOAT: - if (l < 0 || l > 4096-4) { - error("l out of range for stack offset: 0x%08x", l); - } - o4(0xE58D0000 + l); // str r0, [sp, #l] - return 4; - case TY_DOUBLE: { - // Align to 8 byte boundary - int l2 = (l + 7) & ~7; - if (l2 < 0 || l2 > 4096-8) { - error("l out of range for stack offset: 0x%08x", l); - } - o4(0xE58D0000 + l2); // str r0, [sp, #l] - o4(0xE58D1000 + l2 + 4); // str r1, [sp, #l+4] - return (l2 - l) + 8; - } - default: - assert(false); - return 0; - } -#endif - } - - virtual void endFunctionCallArguments(Type* pDecl, int a, int l) { - int argumentStackUse = l; - // Have to calculate register arg count from actual stack size, - // in order to properly handle ... functions. - int regArgCount = l >> 2; - if (regArgCount > 4) { - regArgCount = 4; - } - if (regArgCount > 0) { - argumentStackUse -= regArgCount * 4; - o4(0xE8BD0000 | ((1 << regArgCount) - 1)); // ldmfd sp!,{} - } - mStackUse += argumentStackUse; - - // Align stack. - int missalignment = mStackUse - ((mStackUse / STACK_ALIGNMENT) - * STACK_ALIGNMENT); - mStackAlignmentAdjustment = 0; - if (missalignment > 0) { - mStackAlignmentAdjustment = STACK_ALIGNMENT - missalignment; - } - l += mStackAlignmentAdjustment; - - if (l < 0 || l > 0x3FC) { - error("L out of range for stack adjustment: 0x%08x", l); - } - flush(); - * (int*) a = 0xE24DDF00 | (l >> 2); // sub sp, sp, #0 << 2 - mStackUse += mStackAlignmentAdjustment; - LOG_STACK("endFunctionCallArguments mStackUse: %d, mStackAlignmentAdjustment %d\n", - mStackUse, mStackAlignmentAdjustment); - } - - virtual int callForward(int symbol, Type* pFunc) { - setR0Type(pFunc->pHead); - // Forward calls are always short (local) - int pc = getPC(); - o4(0xEB000000 | encodeAddress(symbol)); - return pc; - } - - virtual void callIndirect(int l, Type* pFunc) { - assert(pFunc->tag == TY_FUNC); - popType(); // Get rid of indirect fn pointer type - int argCount = l >> 2; - int poppedArgs = argCount > 4 ? 4 : argCount; - int adjustedL = l - (poppedArgs << 2) + mStackAlignmentAdjustment; - if (adjustedL < 0 || adjustedL > 4096-4) { - error("l out of range for stack offset: 0x%08x", l); - } - o4(0xE59DC000 | (0xfff & adjustedL)); // ldr r12, [sp,#adjustedL] - o4(0xE12FFF3C); // blx r12 - Type* pReturnType = pFunc->pHead; - setR0Type(pReturnType); -#ifdef ARM_USE_VFP - switch(pReturnType->tag) { - case TY_FLOAT: - o4(0xEE070A90); // fmsr s15, r0 - break; - case TY_DOUBLE: - o4(0xEC410B17); // fmdrr d7, r0, r1 - break; - default: - break; - } -#endif - } - - virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) { - int argCount = l >> 2; - // Have to calculate register arg count from actual stack size, - // in order to properly handle ... functions. - int regArgCount = l >> 2; - if (regArgCount > 4) { - regArgCount = 4; - } - int stackArgs = argCount - regArgCount; - int stackUse = stackArgs + (isIndirect ? 1 : 0) - + (mStackAlignmentAdjustment >> 2); - if (stackUse) { - if (stackUse < 0 || stackUse > 255) { - error("L out of range for stack adjustment: 0x%08x", l); - } - o4(0xE28DDF00 | stackUse); // add sp, sp, #stackUse << 2 - mStackUse -= stackUse * 4; - LOG_STACK("adjustStackAfterCall: %d\n", mStackUse); - } - } - - virtual int jumpOffset() { - return 8; - } - - /* output a symbol and patch all calls to it */ - virtual void gsym(int t) { - int n; - int base = getBase(); - int pc = getPC(); - while (t) { - int data = * (int*) t; - int decodedOffset = ((BRANCH_REL_ADDRESS_MASK & data) << 2); - if (decodedOffset == 0) { - n = 0; - } else { - n = base + decodedOffset; /* next value */ - } - *(int *) t = (data & ~BRANCH_REL_ADDRESS_MASK) - | encodeRelAddress(pc - t - 8); - t = n; - } - } - - /* output a symbol and patch all calls to it */ - virtual void resolveForward(int t) { - if (t) { - int pc = getPC(); - *(int *) t = pc; - } - } - - virtual int finishCompile() { -#if defined(__arm__) - const long base = long(getBase()); - const long curr = long(getPC()); - int err = cacheflush(base, curr, 0); - return err; -#else - return 0; -#endif - } - - /** - * alignment (in bytes) for this type of data - */ - virtual size_t alignmentOf(Type* pType){ - switch(pType->tag) { - case TY_CHAR: - return 1; - case TY_SHORT: - return 2; - case TY_DOUBLE: - return 8; - case TY_ARRAY: - return alignmentOf(pType->pHead); - case TY_STRUCT: - return pType->pHead->alignment & 0x7fffffff; - case TY_FUNC: - error("alignment of func not supported"); - return 1; - default: - return 4; - } - } - - /** - * Array element alignment (in bytes) for this type of data. - */ - virtual size_t sizeOf(Type* pType){ - switch(pType->tag) { - case TY_INT: - return 4; - case TY_SHORT: - return 2; - case TY_CHAR: - return 1; - case TY_FLOAT: - return 4; - case TY_DOUBLE: - return 8; - case TY_POINTER: - return 4; - case TY_ARRAY: - return pType->length * sizeOf(pType->pHead); - case TY_STRUCT: - return pType->pHead->length; - default: - error("Unsupported type %d", pType->tag); - return 0; - } - } - - private: - - static const int BRANCH_REL_ADDRESS_MASK = 0x00ffffff; - - /** Encode a relative address that might also be - * a label. - */ - int encodeAddress(int value) { - int base = getBase(); - if (value >= base && value <= getPC() ) { - // This is a label, encode it relative to the base. - value = value - base; - } - return encodeRelAddress(value); - } - - int encodeRelAddress(int value) { - return BRANCH_REL_ADDRESS_MASK & (value >> 2); - } - - int calcRegArgCount(Type* pDecl) { - int reg = 0; - Type* pArgs = pDecl->pTail; - while (pArgs && reg < 4) { - Type* pArg = pArgs->pHead; - if ( pArg->tag == TY_DOUBLE) { - int evenReg = (reg + 1) & ~1; - if (evenReg >= 4) { - break; - } - reg = evenReg + 2; - } else { - reg++; - } - pArgs = pArgs->pTail; - } - return reg; - } - - void setupIntPtrArgs() { - o4(0xE8BD0002); // ldmfd sp!,{r1} - mStackUse -= 4; - popType(); - } - - /* Pop TOS to R1 (use s14 if VFP) - * Make sure both R0 and TOS are floats. (Could be ints) - * We know that at least one of R0 and TOS is already a float - */ - void setupFloatArgs() { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = collapseType(pR0Type->tag); - TypeTag tagTOS = collapseType(pTOSType->tag); - if (tagR0 != TY_FLOAT) { - assert(tagR0 == TY_INT); -#ifdef ARM_USE_VFP - o4(0xEE070A90); // fmsr s15, r0 - o4(0xEEF87AE7); // fsitos s15, s15 -#else - callRuntime((void*) runtime_int_to_float); -#endif - } - if (tagTOS != TY_FLOAT) { - assert(tagTOS == TY_INT); - assert(tagR0 == TY_FLOAT); -#ifdef ARM_USE_VFP - o4(0xECBD7A01); // fldmfds sp!, {s14} - o4(0xEEB87AC7); // fsitos s14, s14 -#else - o4(0xE92D0001); // stmfd sp!,{r0} // push R0 - o4(0xE59D0004); // ldr r0, [sp, #4] - callRuntime((void*) runtime_int_to_float); - o4(0xE1A01000); // mov r1, r0 - o4(0xE8BD0001); // ldmfd sp!,{r0} // pop R0 - o4(0xE28DD004); // add sp, sp, #4 // Pop sp -#endif - } else { - // Pop TOS -#ifdef ARM_USE_VFP - o4(0xECBD7A01); // fldmfds sp!, {s14} - -#else - o4(0xE8BD0002); // ldmfd sp!,{r1} -#endif - } - mStackUse -= 4; - popType(); - } - - /* Pop TOS into R2..R3 (use D6 if VFP) - * Make sure both R0 and TOS are doubles. Could be floats or ints. - * We know that at least one of R0 and TOS are already a double. - */ - - void setupDoubleArgs() { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = collapseType(pR0Type->tag); - TypeTag tagTOS = collapseType(pTOSType->tag); - if (tagR0 != TY_DOUBLE) { - if (tagR0 == TY_INT) { -#ifdef ARM_USE_VFP - o4(0xEE070A90); // fmsr s15, r0 - o4(0xEEB87BE7); // fsitod d7, s15 - -#else - callRuntime((void*) runtime_int_to_double); -#endif - } else { - assert(tagR0 == TY_FLOAT); -#ifdef ARM_USE_VFP - o4(0xEEB77AE7); // fcvtds d7, s15 -#else - callRuntime((void*) runtime_float_to_double); -#endif - } - } - if (tagTOS != TY_DOUBLE) { -#ifdef ARM_USE_VFP - if (tagTOS == TY_INT) { - o4(0xECFD6A01); // fldmfds sp!,{s13} - o4(0xEEB86BE6); // fsitod d6, s13 - } else { - assert(tagTOS == TY_FLOAT); - o4(0xECFD6A01); // fldmfds sp!,{s13} - o4(0xEEB76AE6); // fcvtds d6, s13 - } -#else - o4(0xE92D0003); // stmfd sp!,{r0,r1} // push r0,r1 - o4(0xE59D0008); // ldr r0, [sp, #8] - if (tagTOS == TY_INT) { - callRuntime((void*) runtime_int_to_double); - } else { - assert(tagTOS == TY_FLOAT); - callRuntime((void*) runtime_float_to_double); - } - o4(0xE1A02000); // mov r2, r0 - o4(0xE1A03001); // mov r3, r1 - o4(0xE8BD0003); // ldmfd sp!,{r0, r1} // Restore R0 - o4(0xE28DD004); // add sp, sp, #4 // Pop sp -#endif - mStackUse -= 4; - } else { -#ifdef ARM_USE_VFP - o4(0xECBD6B02); // fldmfdd sp!, {d6} -#else - o4(0xE8BD000C); // ldmfd sp!,{r2,r3} -#endif - mStackUse -= 8; - } - popType(); - } - - void liReg(int t, int reg) { - assert(reg >= 0 && reg < 16); - int rN = (reg & 0xf) << 12; - size_t encodedImmediate; - if (encode12BitImmediate(t, &encodedImmediate)) { - o4(0xE3A00000 | encodedImmediate | rN); // mov rN, #0 - } else if (encode12BitImmediate(-(t+1), &encodedImmediate)) { - // mvn means move constant ^ ~0 - o4(0xE3E00000 | encodedImmediate | rN); // mvn rN, #0 - } else { - o4(0xE51F0000 | rN); // ldr rN, .L3 - o4(0xEA000000); // b .L99 - o4(t); // .L3: .word 0 - // .L99: - } - } - - void incompatibleTypes(Type* pR0Type, Type* pType) { - error("Incompatible types old: %d new: %d", pR0Type->tag, pType->tag); - } - - void callRuntime(void* fn) { - o4(0xE59FC000); // ldr r12, .L1 - o4(0xEA000000); // b .L99 - o4((int) fn); //.L1: .word fn - o4(0xE12FFF3C); //.L99: blx r12 - } - - // Integer math: - - static int runtime_DIV(int b, int a) { - return a / b; - } - - static int runtime_MOD(int b, int a) { - return a % b; - } - - static void runtime_structCopy(void* src, size_t size, void* dest) { - memcpy(dest, src, size); - } - -#ifndef ARM_USE_VFP - - // Comparison to zero - - static int runtime_is_non_zero_f(float a) { - return a != 0; - } - - static int runtime_is_non_zero_d(double a) { - return a != 0; - } - - // Comparison to zero - - static int runtime_is_zero_f(float a) { - return a == 0; - } - - static int runtime_is_zero_d(double a) { - return a == 0; - } - - // Type conversion - - static int runtime_float_to_int(float a) { - return (int) a; - } - - static double runtime_float_to_double(float a) { - return (double) a; - } - - static int runtime_double_to_int(double a) { - return (int) a; - } - - static float runtime_double_to_float(double a) { - return (float) a; - } - - static float runtime_int_to_float(int a) { - return (float) a; - } - - static double runtime_int_to_double(int a) { - return (double) a; - } - - // Comparisons float - - static int runtime_cmp_eq_ff(float b, float a) { - return a == b; - } - - static int runtime_cmp_ne_ff(float b, float a) { - return a != b; - } - - static int runtime_cmp_lt_ff(float b, float a) { - return a < b; - } - - static int runtime_cmp_le_ff(float b, float a) { - return a <= b; - } - - static int runtime_cmp_ge_ff(float b, float a) { - return a >= b; - } - - static int runtime_cmp_gt_ff(float b, float a) { - return a > b; - } - - // Comparisons double - - static int runtime_cmp_eq_dd(double b, double a) { - return a == b; - } - - static int runtime_cmp_ne_dd(double b, double a) { - return a != b; - } - - static int runtime_cmp_lt_dd(double b, double a) { - return a < b; - } - - static int runtime_cmp_le_dd(double b, double a) { - return a <= b; - } - - static int runtime_cmp_ge_dd(double b, double a) { - return a >= b; - } - - static int runtime_cmp_gt_dd(double b, double a) { - return a > b; - } - - // Math float - - static float runtime_op_add_ff(float b, float a) { - return a + b; - } - - static float runtime_op_sub_ff(float b, float a) { - return a - b; - } - - static float runtime_op_mul_ff(float b, float a) { - return a * b; - } - - static float runtime_op_div_ff(float b, float a) { - return a / b; - } - - static float runtime_op_neg_f(float a) { - return -a; - } - - // Math double - - static double runtime_op_add_dd(double b, double a) { - return a + b; - } - - static double runtime_op_sub_dd(double b, double a) { - return a - b; - } - - static double runtime_op_mul_dd(double b, double a) { - return a * b; - } - - static double runtime_op_div_dd(double b, double a) { - return a / b; - } - - static double runtime_op_neg_d(double a) { - return -a; - } - -#endif - - static const int STACK_ALIGNMENT = 8; - int mStackUse; - // This variable holds the amount we adjusted the stack in the most - // recent endFunctionCallArguments call. It's examined by the - // following adjustStackAfterCall call. - int mStackAlignmentAdjustment; - }; - -#endif // PROVIDE_ARM_CODEGEN - -#ifdef PROVIDE_X86_CODEGEN - - class X86CodeGenerator : public CodeGenerator { - public: - X86CodeGenerator() {} - virtual ~X86CodeGenerator() {} - - /* returns address to patch with local variable size - */ - virtual int functionEntry(Type* pDecl) { - o(0xe58955); /* push %ebp, mov %esp, %ebp */ - return oad(0xec81, 0); /* sub $xxx, %esp */ - } - - virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) { - o(0xc3c9); /* leave, ret */ - *(int *) localVariableAddress = localVariableSize; /* save local variables */ - } - - /* load immediate value */ - virtual void li(int i) { - oad(0xb8, i); /* mov $xx, %eax */ - setR0Type(mkpInt); - } - - virtual void loadFloat(int address, Type* pType) { - setR0Type(pType); - switch (pType->tag) { - case TY_FLOAT: - oad(0x05D9, address); // flds - break; - case TY_DOUBLE: - oad(0x05DD, address); // fldl - break; - default: - assert(false); - break; - } - } - - virtual void addStructOffsetR0(int offset, Type* pType) { - if (offset) { - oad(0x05, offset); // addl offset, %eax - } - setR0Type(pType, ET_LVALUE); - } - - virtual int gjmp(int t) { - return psym(0xe9, t); - } - - /* l = 0: je, l == 1: jne */ - virtual int gtst(bool l, int t) { - Type* pR0Type = getR0Type(); - TypeTag tagR0 = pR0Type->tag; - bool isFloatR0 = isFloatTag(tagR0); - if (isFloatR0) { - o(0xeed9); // fldz - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x9e); // sahf - } else { - o(0xc085); // test %eax, %eax - } - // Use two output statements to generate one instruction. - o(0x0f); // je/jne xxx - return psym(0x84 + l, t); - } - - virtual void gcmp(int op) { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = pR0Type->tag; - TypeTag tagTOS = pTOSType->tag; - bool isFloatR0 = isFloatTag(tagR0); - bool isFloatTOS = isFloatTag(tagTOS); - if (!isFloatR0 && !isFloatTOS) { - int t = decodeOp(op); - o(0x59); /* pop %ecx */ - o(0xc139); /* cmp %eax,%ecx */ - li(0); - o(0x0f); /* setxx %al */ - o(t + 0x90); - o(0xc0); - popType(); - } else { - setupFloatOperands(); - switch (op) { - case OP_EQUALS: - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x9e); // sahf - o(0xc0940f); // sete %al - o(0xc29b0f); // setnp %dl - o(0xd021); // andl %edx, %eax - break; - case OP_NOT_EQUALS: - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x9e); // sahf - o(0xc0950f); // setne %al - o(0xc29a0f); // setp %dl - o(0xd009); // orl %edx, %eax - break; - case OP_GREATER_EQUAL: - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x05c4f6); // testb $5, %ah - o(0xc0940f); // sete %al - break; - case OP_LESS: - o(0xc9d9); // fxch %st(1) - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x9e); // sahf - o(0xc0970f); // seta %al - break; - case OP_LESS_EQUAL: - o(0xc9d9); // fxch %st(1) - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x9e); // sahf - o(0xc0930f); // setea %al - break; - case OP_GREATER: - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x45c4f6); // testb $69, %ah - o(0xc0940f); // sete %al - break; - default: - error("Unknown comparison op"); - } - o(0xc0b60f); // movzbl %al, %eax - } - setR0Type(mkpInt); - } - - virtual void genOp(int op) { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = pR0Type->tag; - TypeTag tagTOS = pTOSType->tag; - bool isFloatR0 = isFloatTag(tagR0); - bool isFloatTOS = isFloatTag(tagTOS); - if (!isFloatR0 && !isFloatTOS) { - bool isPtrR0 = isPointerTag(tagR0); - bool isPtrTOS = isPointerTag(tagTOS); - if (isPtrR0 || isPtrTOS) { - if (isPtrR0 && isPtrTOS) { - if (op != OP_MINUS) { - error("Unsupported pointer-pointer operation %d.", op); - } - if (! typeEqual(pR0Type, pTOSType)) { - error("Incompatible pointer types for subtraction."); - } - o(0x59); /* pop %ecx */ - o(decodeOp(op)); - popType(); - setR0Type(mkpInt); - int size = sizeOf(pR0Type->pHead); - if (size != 1) { - pushR0(); - li(size); - // TODO: Optimize for power-of-two. - genOp(OP_DIV); - } - } else { - if (! (op == OP_PLUS || (op == OP_MINUS && isPtrR0))) { - error("Unsupported pointer-scalar operation %d", op); - } - Type* pPtrType = getPointerArithmeticResultType( - pR0Type, pTOSType); - o(0x59); /* pop %ecx */ - int size = sizeOf(pPtrType->pHead); - if (size != 1) { - // TODO: Optimize for power-of-two. - if (isPtrR0) { - oad(0xC969, size); // imull $size, %ecx - } else { - oad(0xC069, size); // mul $size, %eax - } - } - o(decodeOp(op)); - popType(); - setR0Type(pPtrType); - } - } else { - o(0x59); /* pop %ecx */ - o(decodeOp(op)); - if (op == OP_MOD) - o(0x92); /* xchg %edx, %eax */ - popType(); - } - } else { - Type* pResultType = tagR0 > tagTOS ? pR0Type : pTOSType; - setupFloatOperands(); - // Both float. x87 R0 == left hand, x87 R1 == right hand - switch (op) { - case OP_MUL: - o(0xc9de); // fmulp - break; - case OP_DIV: - o(0xf1de); // fdivp - break; - case OP_PLUS: - o(0xc1de); // faddp - break; - case OP_MINUS: - o(0xe1de); // fsubp - break; - default: - error("Unsupported binary floating operation."); - break; - } - setR0Type(pResultType); - } - } - - virtual void gUnaryCmp(int op) { - if (op != OP_LOGICAL_NOT) { - error("Unknown unary cmp %d", op); - } else { - Type* pR0Type = getR0Type(); - TypeTag tag = collapseType(pR0Type->tag); - switch(tag) { - case TY_INT: { - oad(0xb9, 0); /* movl $0, %ecx */ - int t = decodeOp(op); - o(0xc139); /* cmp %eax,%ecx */ - li(0); - o(0x0f); /* setxx %al */ - o(t + 0x90); - o(0xc0); - } - break; - case TY_FLOAT: - case TY_DOUBLE: - o(0xeed9); // fldz - o(0xe9da); // fucompp - o(0xe0df); // fnstsw %ax - o(0x9e); // sahf - o(0xc0950f); // setne %al - o(0xc29a0f); // setp %dl - o(0xd009); // orl %edx, %eax - o(0xc0b60f); // movzbl %al, %eax - o(0x01f083); // xorl $1, %eax - break; - default: - error("gUnaryCmp unsupported type"); - break; - } - } - setR0Type(mkpInt); - } - - virtual void genUnaryOp(int op) { - Type* pR0Type = getR0Type(); - TypeTag tag = collapseType(pR0Type->tag); - switch(tag) { - case TY_INT: - oad(0xb9, 0); /* movl $0, %ecx */ - o(decodeOp(op)); - break; - case TY_FLOAT: - case TY_DOUBLE: - switch (op) { - case OP_MINUS: - o(0xe0d9); // fchs - break; - case OP_BIT_NOT: - error("Can't apply '~' operator to a float or double."); - break; - default: - error("Unknown unary op %d\n", op); - break; - } - break; - default: - error("genUnaryOp unsupported type"); - break; - } - } - - virtual void pushR0() { - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); - switch(r0ct) { - case TY_INT: - o(0x50); /* push %eax */ - break; - case TY_FLOAT: - o(0x50); /* push %eax */ - o(0x241cd9); // fstps 0(%esp) - break; - case TY_DOUBLE: - o(0x50); /* push %eax */ - o(0x50); /* push %eax */ - o(0x241cdd); // fstpl 0(%esp) - break; - default: - error("pushR0 unsupported type %d", r0ct); - break; - } - pushType(); - } - - virtual void over() { - // We know it's only used for int-ptr ops (++/--) - - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); - - Type* pTOSType = getTOSType(); - TypeTag tosct = collapseType(pTOSType->tag); - - assert (r0ct == TY_INT && tosct == TY_INT); - - o(0x59); /* pop %ecx */ - o(0x50); /* push %eax */ - o(0x51); /* push %ecx */ - - overType(); - } - - virtual void popR0() { - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); - switch(r0ct) { - case TY_INT: - o(0x58); /* popl %eax */ - break; - case TY_FLOAT: - o(0x2404d9); // flds (%esp) - o(0x58); /* popl %eax */ - break; - case TY_DOUBLE: - o(0x2404dd); // fldl (%esp) - o(0x58); /* popl %eax */ - o(0x58); /* popl %eax */ - break; - default: - error("popR0 unsupported type %d", r0ct); - break; - } - popType(); - } - - virtual void storeR0ToTOS() { - Type* pPointerType = getTOSType(); - assert(pPointerType->tag == TY_POINTER); - Type* pTargetType = pPointerType->pHead; - convertR0(pTargetType); - o(0x59); /* pop %ecx */ - popType(); - switch (pTargetType->tag) { - case TY_POINTER: - case TY_INT: - o(0x0189); /* movl %eax/%al, (%ecx) */ - break; - case TY_SHORT: - o(0x018966); /* movw %ax, (%ecx) */ - break; - case TY_CHAR: - o(0x0188); /* movl %eax/%al, (%ecx) */ - break; - case TY_FLOAT: - o(0x19d9); /* fstps (%ecx) */ - break; - case TY_DOUBLE: - o(0x19dd); /* fstpl (%ecx) */ - break; - case TY_STRUCT: - { - // TODO: use alignment information to use movsw/movsl instead of movsb - int size = sizeOf(pTargetType); - if (size > 0) { - o(0x9c); // pushf - o(0x57); // pushl %edi - o(0x56); // pushl %esi - o(0xcf89); // movl %ecx, %edi - o(0xc689); // movl %eax, %esi - oad(0xb9, size); // mov #size, %ecx - o(0xfc); // cld - o(0xf3); // rep - o(0xa4); // movsb - o(0x5e); // popl %esi - o(0x5f); // popl %edi - o(0x9d); // popf - } - } - break; - default: - error("storeR0ToTOS: unsupported type %d", - pTargetType->tag); - break; - } - setR0Type(pTargetType); - } - - virtual void loadR0FromR0() { - Type* pPointerType = getR0Type(); - assert(pPointerType->tag == TY_POINTER); - Type* pNewType = pPointerType->pHead; - TypeTag tag = pNewType->tag; - switch (tag) { - case TY_POINTER: - case TY_INT: - o2(0x008b); /* mov (%eax), %eax */ - break; - case TY_SHORT: - o(0xbf0f); /* movswl (%eax), %eax */ - ob(0); - break; - case TY_CHAR: - o(0xbe0f); /* movsbl (%eax), %eax */ - ob(0); /* add zero in code */ - break; - case TY_FLOAT: - o2(0x00d9); // flds (%eax) - break; - case TY_DOUBLE: - o2(0x00dd); // fldl (%eax) - break; - case TY_ARRAY: - pNewType = pNewType->pTail; - break; - case TY_STRUCT: - break; - default: - error("loadR0FromR0: unsupported type %d", tag); - break; - } - setR0Type(pNewType); - } - - virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) { - gmov(10, ea); /* leal EA, %eax */ - setR0Type(pPointerType, et); - } - - virtual int leaForward(int ea, Type* pPointerType) { - oad(0xb8, ea); /* mov $xx, %eax */ - setR0Type(pPointerType); - return getPC() - 4; - } - - virtual void convertR0Imp(Type* pType, bool isCast){ - Type* pR0Type = getR0Type(); - if (pR0Type == NULL) { - assert(false); - setR0Type(pType); - return; - } - if (isPointerType(pType) && isPointerType(pR0Type)) { - Type* pA = pR0Type; - Type* pB = pType; - // Array decays to pointer - if (pA->tag == TY_ARRAY && pB->tag == TY_POINTER) { - pA = pA->pTail; - } - if (! (typeEqual(pA, pB) - || pB->pHead->tag == TY_VOID - || (pA->tag == TY_POINTER && pB->tag == TY_POINTER && isCast) - )) { - error("Incompatible pointer or array types"); - } - } else if (bitsSame(pType, pR0Type)) { - // do nothing special - } else if (isFloatType(pType) && isFloatType(pR0Type)) { - // do nothing special, both held in same register on x87. - } else { - TypeTag r0Tag = collapseType(pR0Type->tag); - TypeTag destTag = collapseType(pType->tag); - if (r0Tag == TY_INT && isFloatTag(destTag)) { - // Convert R0 from int to float - o(0x50); // push %eax - o(0x2404DB); // fildl 0(%esp) - o(0x58); // pop %eax - } else if (isFloatTag(r0Tag) && destTag == TY_INT) { - // Convert R0 from float to int. Complicated because - // need to save and restore the rounding mode. - o(0x50); // push %eax - o(0x50); // push %eax - o(0x02247cD9); // fnstcw 2(%esp) - o(0x2444b70f); // movzwl 2(%esp), %eax - o(0x02); - o(0x0cb4); // movb $12, %ah - o(0x24048966); // movw %ax, 0(%esp) - o(0x242cd9); // fldcw 0(%esp) - o(0x04245cdb); // fistpl 4(%esp) - o(0x02246cd9); // fldcw 2(%esp) - o(0x58); // pop %eax - o(0x58); // pop %eax - } else { - error("Incompatible types old: %d new: %d", - pR0Type->tag, pType->tag); - } - } - setR0Type(pType); - } - - virtual int beginFunctionCallArguments() { - return oad(0xec81, 0); /* sub $xxx, %esp */ - } - - virtual size_t storeR0ToArg(int l, Type* pArgType) { - convertR0(pArgType); - Type* pR0Type = getR0Type(); - TypeTag r0ct = collapseType(pR0Type->tag); - switch(r0ct) { - case TY_INT: - oad(0x248489, l); /* movl %eax, xxx(%esp) */ - return 4; - case TY_FLOAT: - oad(0x249CD9, l); /* fstps xxx(%esp) */ - return 4; - case TY_DOUBLE: - oad(0x249CDD, l); /* fstpl xxx(%esp) */ - return 8; - default: - assert(false); - return 0; - } - } - - virtual void endFunctionCallArguments(Type* pDecl, int a, int l) { - * (int*) a = l; - } - - virtual int callForward(int symbol, Type* pFunc) { - assert(pFunc->tag == TY_FUNC); - setR0Type(pFunc->pHead); - return psym(0xe8, symbol); /* call xxx */ - } - - virtual void callIndirect(int l, Type* pFunc) { - assert(pFunc->tag == TY_FUNC); - popType(); // Get rid of indirect fn pointer type - setR0Type(pFunc->pHead); - oad(0x2494ff, l); /* call *xxx(%esp) */ - } - - virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) { - assert(pDecl->tag == TY_FUNC); - if (isIndirect) { - l += 4; - } - if (l > 0) { - oad(0xc481, l); /* add $xxx, %esp */ - } - } - - virtual int jumpOffset() { - return 5; - } - - /* output a symbol and patch all calls to it */ - virtual void gsym(int t) { - int n; - int pc = getPC(); - while (t) { - n = *(int *) t; /* next value */ - *(int *) t = pc - t - 4; - t = n; - } - } - - /* output a symbol and patch all calls to it, using absolute address */ - virtual void resolveForward(int t) { - int n; - int pc = getPC(); - while (t) { - n = *(int *) t; /* next value */ - *(int *) t = pc; - t = n; - } - } - - virtual int finishCompile() { - size_t pagesize = 4096; - size_t base = (size_t) getBase() & ~ (pagesize - 1); - size_t top = ((size_t) getPC() + pagesize - 1) & ~ (pagesize - 1); - int err = mprotect((void*) base, top - base, PROT_READ | PROT_WRITE | PROT_EXEC); - if (err) { - error("mprotect() failed: %d", errno); - } - return err; - } - - /** - * Alignment (in bytes) for this type of data - */ - virtual size_t alignmentOf(Type* pType){ - switch (pType->tag) { - case TY_CHAR: - return 1; - case TY_SHORT: - return 2; - case TY_ARRAY: - return alignmentOf(pType->pHead); - case TY_STRUCT: - return pType->pHead->alignment & 0x7fffffff; - case TY_FUNC: - error("alignment of func not supported"); - return 1; - default: - return 4; - } - } - - /** - * Array element alignment (in bytes) for this type of data. - */ - virtual size_t sizeOf(Type* pType){ - switch(pType->tag) { - case TY_INT: - return 4; - case TY_SHORT: - return 2; - case TY_CHAR: - return 1; - case TY_FLOAT: - return 4; - case TY_DOUBLE: - return 8; - case TY_POINTER: - return 4; - case TY_ARRAY: - return pType->length * sizeOf(pType->pHead); - case TY_STRUCT: - return pType->pHead->length; - default: - error("Unsupported type %d", pType->tag); - return 0; - } - } - - private: - - /** Output 1 to 4 bytes. - * - */ - void o(int n) { - /* cannot use unsigned, so we must do a hack */ - while (n && n != -1) { - ob(n & 0xff); - n = n >> 8; - } - } - - /* Output exactly 2 bytes - */ - void o2(int n) { - ob(n & 0xff); - ob(0xff & (n >> 8)); - } - - /* psym is used to put an instruction with a data field which is a - reference to a symbol. It is in fact the same as oad ! */ - int psym(int n, int t) { - return oad(n, t); - } - - /* instruction + address */ - int oad(int n, int t) { - o(n); - int result = getPC(); - o4(t); - return result; - } - - static const int operatorHelper[]; - - int decodeOp(int op) { - if (op < 0 || op > OP_COUNT) { - error("Out-of-range operator: %d\n", op); - op = 0; - } - return operatorHelper[op]; - } - - void gmov(int l, int t) { - o(l + 0x83); - oad((t > -LOCAL && t < LOCAL) << 7 | 5, t); - } - - void setupFloatOperands() { - Type* pR0Type = getR0Type(); - Type* pTOSType = getTOSType(); - TypeTag tagR0 = pR0Type->tag; - TypeTag tagTOS = pTOSType->tag; - bool isFloatR0 = isFloatTag(tagR0); - bool isFloatTOS = isFloatTag(tagTOS); - if (! isFloatR0) { - // Convert R0 from int to float - o(0x50); // push %eax - o(0x2404DB); // fildl 0(%esp) - o(0x58); // pop %eax - } - if (! isFloatTOS){ - o(0x2404DB); // fildl 0(%esp); - o(0x58); // pop %eax - } else { - if (tagTOS == TY_FLOAT) { - o(0x2404d9); // flds (%esp) - o(0x58); // pop %eax - } else { - o(0x2404dd); // fldl (%esp) - o(0x58); // pop %eax - o(0x58); // pop %eax - } - } - popType(); - } - }; - -#endif // PROVIDE_X86_CODEGEN - -#ifdef PROVIDE_TRACE_CODEGEN - class TraceCodeGenerator : public CodeGenerator { - private: - CodeGenerator* mpBase; - - public: - TraceCodeGenerator(CodeGenerator* pBase) { - mpBase = pBase; - } - - virtual ~TraceCodeGenerator() { - delete mpBase; - } - - virtual void init(ICodeBuf* pCodeBuf) { - mpBase->init(pCodeBuf); - } - - void setErrorSink(ErrorSink* pErrorSink) { - mpBase->setErrorSink(pErrorSink); - } - - /* returns address to patch with local variable size - */ - virtual int functionEntry(Type* pDecl) { - int result = mpBase->functionEntry(pDecl); - fprintf(stderr, "functionEntry(pDecl) -> %d\n", result); - return result; - } - - virtual void functionExit(Type* pDecl, int localVariableAddress, int localVariableSize) { - fprintf(stderr, "functionExit(pDecl, %d, %d)\n", - localVariableAddress, localVariableSize); - mpBase->functionExit(pDecl, localVariableAddress, localVariableSize); - } - - /* load immediate value */ - virtual void li(int t) { - fprintf(stderr, "li(%d)\n", t); - mpBase->li(t); - } - - virtual void loadFloat(int address, Type* pType) { - fprintf(stderr, "loadFloat(%d, type=%d)\n", address, pType->tag); - mpBase->loadFloat(address, pType); - } - - virtual void addStructOffsetR0(int offset, Type* pType) { - fprintf(stderr, "addStructOffsetR0(%d, type=%d)\n", offset, pType->tag); - mpBase->addStructOffsetR0(offset, pType); - } - - virtual int gjmp(int t) { - int result = mpBase->gjmp(t); - fprintf(stderr, "gjmp(%d) = %d\n", t, result); - return result; - } - - /* l = 0: je, l == 1: jne */ - virtual int gtst(bool l, int t) { - int result = mpBase->gtst(l, t); - fprintf(stderr, "gtst(%d,%d) = %d\n", l, t, result); - return result; - } - - virtual void gcmp(int op) { - fprintf(stderr, "gcmp(%d)\n", op); - mpBase->gcmp(op); - } - - virtual void genOp(int op) { - fprintf(stderr, "genOp(%d)\n", op); - mpBase->genOp(op); - } - - - virtual void gUnaryCmp(int op) { - fprintf(stderr, "gUnaryCmp(%d)\n", op); - mpBase->gUnaryCmp(op); - } - - virtual void genUnaryOp(int op) { - fprintf(stderr, "genUnaryOp(%d)\n", op); - mpBase->genUnaryOp(op); - } - - virtual void pushR0() { - fprintf(stderr, "pushR0()\n"); - mpBase->pushR0(); - } - - virtual void over() { - fprintf(stderr, "over()\n"); - mpBase->over(); - } - - virtual void popR0() { - fprintf(stderr, "popR0()\n"); - mpBase->popR0(); - } - - virtual void storeR0ToTOS() { - fprintf(stderr, "storeR0ToTOS()\n"); - mpBase->storeR0ToTOS(); - } - - virtual void loadR0FromR0() { - fprintf(stderr, "loadR0FromR0()\n"); - mpBase->loadR0FromR0(); - } - - virtual void leaR0(int ea, Type* pPointerType, ExpressionType et) { - fprintf(stderr, "leaR0(%d, %d, %d)\n", ea, - pPointerType->pHead->tag, et); - mpBase->leaR0(ea, pPointerType, et); - } - - virtual int leaForward(int ea, Type* pPointerType) { - fprintf(stderr, "leaForward(%d)\n", ea); - return mpBase->leaForward(ea, pPointerType); - } - - virtual void convertR0Imp(Type* pType, bool isCast){ - fprintf(stderr, "convertR0(pType tag=%d, %d)\n", pType->tag, isCast); - mpBase->convertR0Imp(pType, isCast); - } - - virtual int beginFunctionCallArguments() { - int result = mpBase->beginFunctionCallArguments(); - fprintf(stderr, "beginFunctionCallArguments() = %d\n", result); - return result; - } - - virtual size_t storeR0ToArg(int l, Type* pArgType) { - fprintf(stderr, "storeR0ToArg(%d, pArgType=%d)\n", l, - pArgType->tag); - return mpBase->storeR0ToArg(l, pArgType); - } - - virtual void endFunctionCallArguments(Type* pDecl, int a, int l) { - fprintf(stderr, "endFunctionCallArguments(%d, %d)\n", a, l); - mpBase->endFunctionCallArguments(pDecl, a, l); - } - - virtual int callForward(int symbol, Type* pFunc) { - int result = mpBase->callForward(symbol, pFunc); - fprintf(stderr, "callForward(%d) = %d\n", symbol, result); - return result; - } - - virtual void callIndirect(int l, Type* pFunc) { - fprintf(stderr, "callIndirect(%d returntype = %d)\n", l, - pFunc->pHead->tag); - mpBase->callIndirect(l, pFunc); - } - - virtual void adjustStackAfterCall(Type* pDecl, int l, bool isIndirect) { - fprintf(stderr, "adjustStackAfterCall(pType, %d, %d)\n", l, isIndirect); - mpBase->adjustStackAfterCall(pDecl, l, isIndirect); - } - - virtual int jumpOffset() { - return mpBase->jumpOffset(); - } - - /* output a symbol and patch all calls to it */ - virtual void gsym(int t) { - fprintf(stderr, "gsym(%d)\n", t); - mpBase->gsym(t); - } - - virtual void resolveForward(int t) { - mpBase->resolveForward(t); - } - - virtual int finishCompile() { - int result = mpBase->finishCompile(); - fprintf(stderr, "finishCompile() = %d\n", result); - return result; - } - - /** - * Alignment (in bytes) for this type of data - */ - virtual size_t alignmentOf(Type* pType){ - return mpBase->alignmentOf(pType); - } - - /** - * Array element alignment (in bytes) for this type of data. - */ - virtual size_t sizeOf(Type* pType){ - return mpBase->sizeOf(pType); - } - - virtual Type* getR0Type() { - return mpBase->getR0Type(); - } - - virtual ExpressionType getR0ExpressionType() { - return mpBase->getR0ExpressionType(); - } - - virtual void setR0ExpressionType(ExpressionType et) { - mpBase->setR0ExpressionType(et); - } - - virtual size_t getExpressionStackDepth() { - return mpBase->getExpressionStackDepth(); - } - - virtual void forceR0RVal() { - return mpBase->forceR0RVal(); - } - }; - -#endif // PROVIDE_TRACE_CODEGEN - - class Arena { - public: - // Used to record a given allocation amount. - // Used: - // Mark mark = arena.mark(); - // ... lots of arena.allocate() - // arena.free(mark); - - struct Mark { - size_t chunk; - size_t offset; - }; - - Arena() { - mCurrentChunk = 0; - Chunk start(CHUNK_SIZE); - mData.push_back(start); - } - - ~Arena() { - for(size_t i = 0; i < mData.size(); i++) { - mData[i].free(); - } - } - - // Alloc using the standard alignment size safe for any variable - void* alloc(size_t size) { - return alloc(size, 8); - } - - Mark mark(){ - Mark result; - result.chunk = mCurrentChunk; - result.offset = mData[mCurrentChunk].mOffset; - return result; - } - - void freeToMark(const Mark& mark) { - mCurrentChunk = mark.chunk; - mData[mCurrentChunk].mOffset = mark.offset; - } - - private: - // Allocate memory aligned to a given size - // and a given power-of-two-sized alignment (e.g. 1,2,4,8,...) - // Memory is not zero filled. - - void* alloc(size_t size, size_t alignment) { - while (size > mData[mCurrentChunk].remainingCapacity(alignment)) { - if (mCurrentChunk + 1 < mData.size()) { - mCurrentChunk++; - } else { - size_t allocSize = CHUNK_SIZE; - if (allocSize < size + alignment - 1) { - allocSize = size + alignment - 1; - } - Chunk chunk(allocSize); - mData.push_back(chunk); - mCurrentChunk++; - } - } - return mData[mCurrentChunk].allocate(size, alignment); - } - - static const size_t CHUNK_SIZE = 128*1024; - // Note: this class does not deallocate its - // memory when it's destroyed. It depends upon - // its parent to deallocate the memory. - struct Chunk { - Chunk() { - mpData = 0; - mSize = 0; - mOffset = 0; - } - - Chunk(size_t size) { - mSize = size; - mpData = (char*) malloc(size); - mOffset = 0; - } - - ~Chunk() { - // Doesn't deallocate memory. - } - - void* allocate(size_t size, size_t alignment) { - size_t alignedOffset = aligned(mOffset, alignment); - void* result = mpData + alignedOffset; - mOffset = alignedOffset + size; - return result; - } - - void free() { - if (mpData) { - ::free(mpData); - mpData = 0; - } - } - - size_t remainingCapacity(size_t alignment) { - return aligned(mSize, alignment) - aligned(mOffset, alignment); - } - - // Assume alignment is a power of two - inline size_t aligned(size_t v, size_t alignment) { - size_t mask = alignment-1; - return (v + mask) & ~mask; - } - - char* mpData; - size_t mSize; - size_t mOffset; - }; - - size_t mCurrentChunk; - - Vector mData; - }; - - struct VariableInfo; - - struct Token { - int hash; - size_t length; - char* pText; - tokenid_t id; - - // Current values for the token - char* mpMacroDefinition; - VariableInfo* mpVariableInfo; - VariableInfo* mpStructInfo; - }; - - class TokenTable { - public: - // Don't use 0..0xff, allows characters and operators to be tokens too. - - static const int TOKEN_BASE = 0x100; - TokenTable() { - mpMap = hashmapCreate(128, hashFn, equalsFn); - } - - ~TokenTable() { - hashmapFree(mpMap); - } - - void setArena(Arena* pArena) { - mpArena = pArena; - } - - // Returns a token for a given string of characters. - tokenid_t intern(const char* pText, size_t length) { - Token probe; - int hash = hashmapHash((void*) pText, length); - { - Token probe; - probe.hash = hash; - probe.length = length; - probe.pText = (char*) pText; - Token* pValue = (Token*) hashmapGet(mpMap, &probe); - if (pValue) { - return pValue->id; - } - } - - Token* pToken = (Token*) mpArena->alloc(sizeof(Token)); - memset(pToken, 0, sizeof(*pToken)); - pToken->hash = hash; - pToken->length = length; - pToken->pText = (char*) mpArena->alloc(length + 1); - memcpy(pToken->pText, pText, length); - pToken->pText[length] = 0; - pToken->id = mTokens.size() + TOKEN_BASE; - mTokens.push_back(pToken); - hashmapPut(mpMap, pToken, pToken); - return pToken->id; - } - - // Return the Token for a given tokenid. - Token& operator[](tokenid_t id) { - return *mTokens[id - TOKEN_BASE]; - } - - inline size_t size() { - return mTokens.size(); - } - - private: - - static int hashFn(void* pKey) { - Token* pToken = (Token*) pKey; - return pToken->hash; - } - - static bool equalsFn(void* keyA, void* keyB) { - Token* pTokenA = (Token*) keyA; - Token* pTokenB = (Token*) keyB; - // Don't need to compare hash values, they should always be equal - return pTokenA->length == pTokenB->length - && strcmp(pTokenA->pText, pTokenB->pText) == 0; - } - - Hashmap* mpMap; - Vector mTokens; - Arena* mpArena; - }; - - class InputStream { - public: - virtual ~InputStream() {} - virtual int getChar() = 0; - }; - - class TextInputStream : public InputStream { - public: - TextInputStream(const char* text, size_t textLength) - : pText(text), mTextLength(textLength), mPosition(0) { - } - - virtual int getChar() { - return mPosition < mTextLength ? pText[mPosition++] : EOF; - } - - private: - const char* pText; - size_t mTextLength; - size_t mPosition; - }; - - class String { - public: - String() { - mpBase = 0; - mUsed = 0; - mSize = 0; - } - - String(const char* item, int len, bool adopt) { - if (len < 0) { - len = strlen(item); - } - if (adopt) { - mpBase = (char*) item; - mUsed = len; - mSize = len + 1; - } else { - mpBase = 0; - mUsed = 0; - mSize = 0; - appendBytes(item, len); - } - } - - String(const String& other) { - mpBase = 0; - mUsed = 0; - mSize = 0; - appendBytes(other.getUnwrapped(), other.len()); - } - - ~String() { - if (mpBase) { - free(mpBase); - } - } - - String& operator=(const String& other) { - clear(); - appendBytes(other.getUnwrapped(), other.len()); - return *this; - } - - inline char* getUnwrapped() const { - return mpBase; - } - - void clear() { - mUsed = 0; - if (mSize > 0) { - mpBase[0] = 0; - } - } - - void appendCStr(const char* s) { - appendBytes(s, strlen(s)); - } - - void appendBytes(const char* s, int n) { - memcpy(ensure(n), s, n + 1); - } - - void append(char c) { - * ensure(1) = c; - } - - void append(String& other) { - appendBytes(other.getUnwrapped(), other.len()); - } - - char* orphan() { - char* result = mpBase; - mpBase = 0; - mUsed = 0; - mSize = 0; - return result; - } - - void printf(const char* fmt,...) { - va_list ap; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - } - - void vprintf(const char* fmt, va_list ap) { - char* temp; - int numChars = vasprintf(&temp, fmt, ap); - memcpy(ensure(numChars), temp, numChars+1); - free(temp); - } - - inline size_t len() const { - return mUsed; - } - - private: - char* ensure(int n) { - size_t newUsed = mUsed + n; - if (newUsed > mSize) { - size_t newSize = mSize * 2 + 10; - if (newSize < newUsed) { - newSize = newUsed; - } - mpBase = (char*) realloc(mpBase, newSize + 1); - mSize = newSize; - } - mpBase[newUsed] = '\0'; - char* result = mpBase + mUsed; - mUsed = newUsed; - return result; - } - - char* mpBase; - size_t mUsed; - size_t mSize; - }; - - void internKeywords() { - // Note: order has to match TOK_ constants - static const char* keywords[] = { - "int", - "char", - "void", - "if", - "else", - "while", - "break", - "return", - "for", - "auto", - "case", - "const", - "continue", - "default", - "do", - "double", - "enum", - "extern", - "float", - "goto", - "long", - "register", - "short", - "signed", - "sizeof", - "static", - "struct", - "switch", - "typedef", - "union", - "unsigned", - "volatile", - "_Bool", - "_Complex", - "_Imaginary", - "inline", - "restrict", - - // predefined tokens that can also be symbols start here: - "pragma", - "define", - "line", - 0}; - - for(int i = 0; keywords[i]; i++) { - mTokenTable.intern(keywords[i], strlen(keywords[i])); - } - } - - struct InputState { - InputStream* pStream; - int oldCh; - }; - - struct VariableInfo { - void* pAddress; - void* pForward; // For a forward direction, linked list of data to fix up - tokenid_t tok; - size_t level; - VariableInfo* pOldDefinition; - Type* pType; - bool isStructTag; - }; - - class SymbolStack { - public: - SymbolStack() { - mpArena = 0; - mpTokenTable = 0; - } - - void setArena(Arena* pArena) { - mpArena = pArena; - } - - void setTokenTable(TokenTable* pTokenTable) { - mpTokenTable = pTokenTable; - } - - void pushLevel() { - Mark mark; - mark.mArenaMark = mpArena->mark(); - mark.mSymbolHead = mStack.size(); - mLevelStack.push_back(mark); - } - - void popLevel() { - // Undo any shadowing that was done: - Mark mark = mLevelStack.back(); - mLevelStack.pop_back(); - while (mStack.size() > mark.mSymbolHead) { - VariableInfo* pV = mStack.back(); - mStack.pop_back(); - if (pV->isStructTag) { - (*mpTokenTable)[pV->tok].mpStructInfo = pV->pOldDefinition; - } else { - (*mpTokenTable)[pV->tok].mpVariableInfo = pV->pOldDefinition; - } - } - mpArena->freeToMark(mark.mArenaMark); - } - - bool isDefinedAtCurrentLevel(tokenid_t tok) { - VariableInfo* pV = (*mpTokenTable)[tok].mpVariableInfo; - return pV && pV->level == level(); - } - - bool isStructTagDefinedAtCurrentLevel(tokenid_t tok) { - VariableInfo* pV = (*mpTokenTable)[tok].mpStructInfo; - return pV && pV->level == level(); - } - - VariableInfo* add(tokenid_t tok) { - Token& token = (*mpTokenTable)[tok]; - VariableInfo* pOldV = token.mpVariableInfo; - VariableInfo* pNewV = - (VariableInfo*) mpArena->alloc(sizeof(VariableInfo)); - memset(pNewV, 0, sizeof(VariableInfo)); - pNewV->tok = tok; - pNewV->level = level(); - pNewV->pOldDefinition = pOldV; - token.mpVariableInfo = pNewV; - mStack.push_back(pNewV); - return pNewV; - } - - VariableInfo* addStructTag(tokenid_t tok) { - Token& token = (*mpTokenTable)[tok]; - VariableInfo* pOldS = token.mpStructInfo; - VariableInfo* pNewS = - (VariableInfo*) mpArena->alloc(sizeof(VariableInfo)); - memset(pNewS, 0, sizeof(VariableInfo)); - pNewS->tok = tok; - pNewS->level = level(); - pNewS->isStructTag = true; - pNewS->pOldDefinition = pOldS; - token.mpStructInfo = pNewS; - mStack.push_back(pNewS); - return pNewS; - } - - VariableInfo* add(Type* pType) { - VariableInfo* pVI = add(pType->id); - pVI->pType = pType; - return pVI; - } - - void forEach(bool (*fn)(VariableInfo*, void*), void* context) { - for (size_t i = 0; i < mStack.size(); i++) { - if (! fn(mStack[i], context)) { - break; - } - } - } - - private: - inline size_t level() { - return mLevelStack.size(); - } - - struct Mark { - Arena::Mark mArenaMark; - size_t mSymbolHead; - }; - - Arena* mpArena; - TokenTable* mpTokenTable; - Vector mStack; - Vector mLevelStack; - }; - - int ch; // Current input character, or EOF - tokenid_t tok; // token - intptr_t tokc; // token extra info - double tokd; // floating point constant value - int tokl; // token operator level - intptr_t rsym; // return symbol - Type* pReturnType; // type of the current function's return. - intptr_t loc; // local variable index - char* glo; // global variable index - String mTokenString; - bool mbSuppressMacroExpansion; - char* dptr; // Macro state: Points to macro text during macro playback. - int dch; // Macro state: Saves old value of ch during a macro playback. - char* pGlobalBase; - ACCSymbolLookupFn mpSymbolLookupFn; - void* mpSymbolLookupContext; - - // Arena for the duration of the compile - Arena mGlobalArena; - // Arena for data that's only needed when compiling a single function - Arena mLocalArena; - - Arena* mpCurrentArena; - - TokenTable mTokenTable; - SymbolStack mGlobals; - SymbolStack mLocals; - - SymbolStack* mpCurrentSymbolStack; - - // Prebuilt types, makes things slightly faster. - Type* mkpInt; // int - Type* mkpShort; // short - Type* mkpChar; // char - Type* mkpVoid; // void - Type* mkpFloat; - Type* mkpDouble; - Type* mkpIntFn; - Type* mkpIntPtr; - Type* mkpCharPtr; - Type* mkpFloatPtr; - Type* mkpDoublePtr; - Type* mkpPtrIntFn; - - InputStream* file; - int mLineNumber; - bool mbBumpLine; - - ICodeBuf* pCodeBuf; - CodeGenerator* pGen; - - String mErrorBuf; - - String mPragmas; - int mPragmaStringCount; - int mCompileResult; - - static const int ALLOC_SIZE = 99999; - - static const int TOK_DUMMY = 1; - static const int TOK_NUM = 2; - static const int TOK_NUM_FLOAT = 3; - static const int TOK_NUM_DOUBLE = 4; - static const int TOK_OP_ASSIGNMENT = 5; - static const int TOK_OP_ARROW = 6; - - // 3..255 are character and/or operators - - // Keywords start at 0x100 and increase by 1 - // Order has to match string list in "internKeywords". - enum { - TOK_KEYWORD = TokenTable::TOKEN_BASE, - TOK_INT = TOK_KEYWORD, - TOK_CHAR, - TOK_VOID, - TOK_IF, - TOK_ELSE, - TOK_WHILE, - TOK_BREAK, - TOK_RETURN, - TOK_FOR, - TOK_AUTO, - TOK_CASE, - TOK_CONST, - TOK_CONTINUE, - TOK_DEFAULT, - TOK_DO, - TOK_DOUBLE, - TOK_ENUM, - TOK_EXTERN, - TOK_FLOAT, - TOK_GOTO, - TOK_LONG, - TOK_REGISTER, - TOK_SHORT, - TOK_SIGNED, - TOK_SIZEOF, - TOK_STATIC, - TOK_STRUCT, - TOK_SWITCH, - TOK_TYPEDEF, - TOK_UNION, - TOK_UNSIGNED, - TOK_VOLATILE, - TOK__BOOL, - TOK__COMPLEX, - TOK__IMAGINARY, - TOK_INLINE, - TOK_RESTRICT, - - // Symbols start after keywords - - TOK_SYMBOL, - TOK_PRAGMA = TOK_SYMBOL, - TOK_DEFINE, - TOK_LINE - }; - - static const int LOCAL = 0x200; - - static const int SYM_FORWARD = 0; - static const int SYM_DEFINE = 1; - - /* tokens in string heap */ - static const int TAG_TOK = ' '; - - static const int OP_INCREMENT = 0; - static const int OP_DECREMENT = 1; - static const int OP_MUL = 2; - static const int OP_DIV = 3; - static const int OP_MOD = 4; - static const int OP_PLUS = 5; - static const int OP_MINUS = 6; - static const int OP_SHIFT_LEFT = 7; - static const int OP_SHIFT_RIGHT = 8; - static const int OP_LESS_EQUAL = 9; - static const int OP_GREATER_EQUAL = 10; - static const int OP_LESS = 11; - static const int OP_GREATER = 12; - static const int OP_EQUALS = 13; - static const int OP_NOT_EQUALS = 14; - static const int OP_LOGICAL_AND = 15; - static const int OP_LOGICAL_OR = 16; - static const int OP_BIT_AND = 17; - static const int OP_BIT_XOR = 18; - static const int OP_BIT_OR = 19; - static const int OP_BIT_NOT = 20; - static const int OP_LOGICAL_NOT = 21; - static const int OP_COUNT = 22; - - /* Operators are searched from front, the two-character operators appear - * before the single-character operators with the same first character. - * @ is used to pad out single-character operators. - */ - static const char* operatorChars; - static const char operatorLevel[]; - - /* Called when we detect an internal problem. Does nothing in production. - * - */ - void internalError() { - * (char*) 0 = 0; - } - - void assertImpl(bool isTrue, int line) { - if (!isTrue) { - LOGD("%d: assertion failed at line %s:%d.", mLineNumber, __FILE__, line); - internalError(); - } - } - - bool isSymbol(tokenid_t t) { - return t >= TOK_SYMBOL && - ((size_t) (t-TOK_SYMBOL)) < mTokenTable.size(); - } - - bool isSymbolOrKeyword(tokenid_t t) { - return t >= TOK_KEYWORD && - ((size_t) (t-TOK_KEYWORD)) < mTokenTable.size(); - } - - VariableInfo* VI(tokenid_t t) { - assert(isSymbol(t)); - VariableInfo* pV = mTokenTable[t].mpVariableInfo; - if (pV && pV->tok != t) { - internalError(); - } - return pV; - } - - inline bool isDefined(tokenid_t t) { - return t >= TOK_SYMBOL && VI(t) != 0; - } - - const char* nameof(tokenid_t t) { - assert(isSymbolOrKeyword(t)); - return mTokenTable[t].pText; - } - - void pdef(int t) { - mTokenString.append(t); - } - - void inp() { - if (dptr) { - ch = *dptr++; - if (ch == 0) { - dptr = 0; - ch = dch; - } - } else { - if (mbBumpLine) { - mLineNumber++; - mbBumpLine = false; - } - ch = file->getChar(); - if (ch == '\n') { - mbBumpLine = true; - } - } -#if 0 - printf("ch='%c' 0x%x\n", ch, ch); -#endif - } - - int isid() { - return isalnum(ch) | (ch == '_'); - } - - int decodeHex(int c) { - if (isdigit(c)) { - c -= '0'; - } else if (c <= 'F') { - c = c - 'A' + 10; - } else { - c =c - 'a' + 10; - } - return c; - } - - /* read a character constant, advances ch to after end of constant */ - int getq() { - int val = ch; - if (ch == '\\') { - inp(); - if (isoctal(ch)) { - // 1 to 3 octal characters. - val = 0; - for(int i = 0; i < 3; i++) { - if (isoctal(ch)) { - val = (val << 3) + ch - '0'; - inp(); - } - } - return val; - } else if (ch == 'x' || ch == 'X') { - // N hex chars - inp(); - if (! isxdigit(ch)) { - error("'x' character escape requires at least one digit."); - } else { - val = 0; - while (isxdigit(ch)) { - val = (val << 4) + decodeHex(ch); - inp(); - } - } - } else { - int val = ch; - switch (ch) { - case 'a': - val = '\a'; - break; - case 'b': - val = '\b'; - break; - case 'f': - val = '\f'; - break; - case 'n': - val = '\n'; - break; - case 'r': - val = '\r'; - break; - case 't': - val = '\t'; - break; - case 'v': - val = '\v'; - break; - case '\\': - val = '\\'; - break; - case '\'': - val = '\''; - break; - case '"': - val = '"'; - break; - case '?': - val = '?'; - break; - default: - error("Undefined character escape %c", ch); - break; - } - inp(); - return val; - } - } else { - inp(); - } - return val; - } - - static bool isoctal(int ch) { - return ch >= '0' && ch <= '7'; - } - - bool acceptCh(int c) { - bool result = c == ch; - if (result) { - pdef(ch); - inp(); - } - return result; - } - - bool acceptDigitsCh() { - bool result = false; - while (isdigit(ch)) { - result = true; - pdef(ch); - inp(); - } - return result; - } - - void parseFloat() { - tok = TOK_NUM_DOUBLE; - // mTokenString already has the integral part of the number. - if(mTokenString.len() == 0) { - mTokenString.append('0'); - } - acceptCh('.'); - acceptDigitsCh(); - if (acceptCh('e') || acceptCh('E')) { - acceptCh('-') || acceptCh('+'); - acceptDigitsCh(); - } - if (ch == 'f' || ch == 'F') { - tok = TOK_NUM_FLOAT; - inp(); - } else if (ch == 'l' || ch == 'L') { - inp(); - error("Long floating point constants not supported."); - } - char* pText = mTokenString.getUnwrapped(); - char* pEnd = pText + strlen(pText); - char* pEndPtr = 0; - errno = 0; - if (tok == TOK_NUM_FLOAT) { - tokd = strtof(pText, &pEndPtr); - } else { - tokd = strtod(pText, &pEndPtr); - } - if (errno || pEndPtr != pEnd) { - error("Can't parse constant: %s", pText); - } - // fprintf(stderr, "float constant: %s (%d) %g\n", pText, tok, tokd); - } - - void next() { - int l, a; - - while (isspace(ch) | (ch == '#')) { - if (ch == '#') { - inp(); - next(); - if (tok == TOK_DEFINE) { - doDefine(); - } else if (tok == TOK_PRAGMA) { - doPragma(); - } else if (tok == TOK_LINE) { - doLine(); - } else { - error("Unsupported preprocessor directive \"%s\"", - mTokenString.getUnwrapped()); - } - } - inp(); - } - tokl = 0; - tok = ch; - /* encode identifiers & numbers */ - if (isdigit(ch) || ch == '.') { - // Start of a numeric constant. Could be integer, float, or - // double, won't know until we look further. - mTokenString.clear(); - pdef(ch); - inp(); - if (tok == '.' && !isdigit(ch)) { - goto done; - } - int base = 10; - if (tok == '0') { - if (ch == 'x' || ch == 'X') { - base = 16; - tok = TOK_NUM; - tokc = 0; - inp(); - while ( isxdigit(ch) ) { - tokc = (tokc << 4) + decodeHex(ch); - inp(); - } - } else if (isoctal(ch)){ - base = 8; - tok = TOK_NUM; - tokc = 0; - while ( isoctal(ch) ) { - tokc = (tokc << 3) + (ch - '0'); - inp(); - } - } - } else if (isdigit(tok)){ - acceptDigitsCh(); - } - if (base == 10) { - if (tok == '.' || ch == '.' || ch == 'e' || ch == 'E') { - parseFloat(); - } else { - // It's an integer constant - char* pText = mTokenString.getUnwrapped(); - char* pEnd = pText + strlen(pText); - char* pEndPtr = 0; - errno = 0; - tokc = strtol(pText, &pEndPtr, base); - if (errno || pEndPtr != pEnd) { - error("Can't parse constant: %s %d %d", pText, base, errno); - } - tok = TOK_NUM; - } - } - } else if (isid()) { - mTokenString.clear(); - while (isid()) { - pdef(ch); - inp(); - } - tok = mTokenTable.intern(mTokenString.getUnwrapped(), mTokenString.len()); - if (! mbSuppressMacroExpansion) { - // Is this a macro? - char* pMacroDefinition = mTokenTable[tok].mpMacroDefinition; - if (pMacroDefinition) { - // Yes, it is a macro - dptr = pMacroDefinition; - dch = ch; - inp(); - next(); - } - } - } else { - inp(); - if (tok == '\'') { - tok = TOK_NUM; - tokc = getq(); - if (ch != '\'') { - error("Expected a ' character, got %c", ch); - } else { - inp(); - } - } else if ((tok == '/') & (ch == '*')) { - inp(); - while (ch && ch != EOF) { - while (ch != '*' && ch != EOF) - inp(); - inp(); - if (ch == '/') - ch = 0; - } - if (ch == EOF) { - error("End of file inside comment."); - } - inp(); - next(); - } else if ((tok == '/') & (ch == '/')) { - inp(); - while (ch && (ch != '\n') && (ch != EOF)) { - inp(); - } - inp(); - next(); - } else if ((tok == '-') & (ch == '>')) { - inp(); - tok = TOK_OP_ARROW; - } else { - const char* t = operatorChars; - int opIndex = 0; - while ((l = *t++) != 0) { - a = *t++; - tokl = operatorLevel[opIndex]; - tokc = opIndex; - if ((l == tok) & ((a == ch) | (a == '@'))) { -#if 0 - printf("%c%c -> tokl=%d tokc=0x%x\n", - l, a, tokl, tokc); -#endif - if (a == ch) { - inp(); - tok = TOK_DUMMY; /* dummy token for double tokens */ - } - /* check for op=, valid for * / % + - << >> & ^ | */ - if (ch == '=' && - ((tokl >= 1 && tokl <= 3) - || (tokl >=6 && tokl <= 8)) ) { - inp(); - tok = TOK_OP_ASSIGNMENT; - } - break; - } - opIndex++; - } - if (l == 0) { - tokl = 0; - tokc = 0; - } - } - } - - done: ; -#if 0 - { - String buf; - decodeToken(buf, tok, true); - fprintf(stderr, "%s\n", buf.getUnwrapped()); - } -#endif - } - - void doDefine() { - mbSuppressMacroExpansion = true; - next(); - mbSuppressMacroExpansion = false; - tokenid_t name = tok; - String* pName = new String(); - if (ch == '(') { - delete pName; - error("Defines with arguments not supported"); - return; - } - while (isspace(ch)) { - inp(); - } - String value; - bool appendToValue = true; - while (ch != '\n' && ch != EOF) { - // Check for '//' comments. - if (appendToValue && ch == '/') { - inp(); - if (ch == '/') { - appendToValue = false; - } else { - value.append('/'); - } - } - if (appendToValue && ch != EOF) { - value.append(ch); - } - inp(); - } - char* pDefn = (char*)mGlobalArena.alloc(value.len() + 1); - memcpy(pDefn, value.getUnwrapped(), value.len()); - pDefn[value.len()] = 0; - mTokenTable[name].mpMacroDefinition = pDefn; - } - - void doPragma() { - // # pragma name(val) - int state = 0; - while(ch != EOF && ch != '\n' && state < 10) { - switch(state) { - case 0: - if (isspace(ch)) { - inp(); - } else { - state++; - } - break; - case 1: - if (isalnum(ch)) { - mPragmas.append(ch); - inp(); - } else if (ch == '(') { - mPragmas.append(0); - inp(); - state++; - } else { - state = 11; - } - break; - case 2: - if (isalnum(ch)) { - mPragmas.append(ch); - inp(); - } else if (ch == ')') { - mPragmas.append(0); - inp(); - state = 10; - } else { - state = 11; - } - break; - } - } - if(state != 10) { - error("Unexpected pragma syntax"); - } - mPragmaStringCount += 2; - } - - void doLine() { - // # line number { "filename "} - next(); - if (tok != TOK_NUM) { - error("Expected a line-number"); - } else { - mLineNumber = tokc-1; // The end-of-line will increment it. - } - while(ch != EOF && ch != '\n') { - inp(); - } - } - - virtual void verror(const char* fmt, va_list ap) { - mErrorBuf.printf("%ld: ", mLineNumber); - mErrorBuf.vprintf(fmt, ap); - mErrorBuf.printf("\n"); - } - - void skip(intptr_t c) { - if (!accept(c)) { - error("'%c' expected", c); - } - } - - bool accept(intptr_t c) { - if (tok == c) { - next(); - return true; - } - return false; - } - - bool acceptStringLiteral() { - if (tok == '"') { - pGen->leaR0((int) glo, mkpCharPtr, ET_RVALUE); - // This while loop merges multiple adjacent string constants. - while (tok == '"') { - while (ch != '"' && ch != EOF) { - *allocGlobalSpace(1,1) = getq(); - } - if (ch != '"') { - error("Unterminated string constant."); - } - inp(); - next(); - } - /* Null terminate */ - *glo = 0; - /* align heap */ - allocGlobalSpace(1,(char*) (((intptr_t) glo + 4) & -4) - glo); - - return true; - } - return false; - } - - void linkGlobal(tokenid_t t, bool isFunction) { - VariableInfo* pVI = VI(t); - void* n = NULL; - if (mpSymbolLookupFn) { - n = mpSymbolLookupFn(mpSymbolLookupContext, nameof(t)); - } - if (pVI->pType == NULL) { - if (isFunction) { - pVI->pType = mkpIntFn; - } else { - pVI->pType = mkpInt; - } - } - pVI->pAddress = n; - } - - void unaryOrAssignment() { - unary(); - if (accept('=')) { - checkLVal(); - pGen->pushR0(); - expr(); - pGen->forceR0RVal(); - pGen->storeR0ToTOS(); - } else if (tok == TOK_OP_ASSIGNMENT) { - int t = tokc; - next(); - checkLVal(); - pGen->pushR0(); - pGen->forceR0RVal(); - pGen->pushR0(); - expr(); - pGen->forceR0RVal(); - pGen->genOp(t); - pGen->storeR0ToTOS(); - } - } - - /* Parse and evaluate a unary expression. - */ - void unary() { - tokenid_t t; - intptr_t a; - t = 0; - if (acceptStringLiteral()) { - // Nothing else to do. - } else { - int c = tokl; - a = tokc; - double ad = tokd; - t = tok; - next(); - if (t == TOK_NUM) { - pGen->li(a); - } else if (t == TOK_NUM_FLOAT) { - // Align to 4-byte boundary - glo = (char*) (((intptr_t) glo + 3) & -4); - * (float*) glo = (float) ad; - pGen->loadFloat((int) glo, mkpFloat); - glo += 4; - } else if (t == TOK_NUM_DOUBLE) { - // Align to 8-byte boundary - glo = (char*) (((intptr_t) glo + 7) & -8); - * (double*) glo = ad; - pGen->loadFloat((int) glo, mkpDouble); - glo += 8; - } else if (c == 2) { - /* -, +, !, ~ */ - unary(); - pGen->forceR0RVal(); - if (t == '!') - pGen->gUnaryCmp(a); - else if (t == '+') { - // ignore unary plus. - } else { - pGen->genUnaryOp(a); - } - } else if (c == 11) { - // pre increment / pre decrement - unary(); - doIncDec(a == OP_INCREMENT, 0); - } - else if (t == '(') { - // It's either a cast or an expression - Type* pCast = acceptCastTypeDeclaration(); - if (pCast) { - skip(')'); - unary(); - pGen->forceR0RVal(); - pGen->castR0(pCast); - } else { - commaExpr(); - skip(')'); - } - } else if (t == '*') { - /* This is a pointer dereference. - */ - unary(); - doPointer(); - } else if (t == '&') { - unary(); - doAddressOf(); - } else if (t == EOF ) { - error("Unexpected EOF."); - } else if (t == ';') { - error("Unexpected ';'"); - } else if (!checkSymbol(t)) { - // Don't have to do anything special here, the error - // message was printed by checkSymbol() above. - } else { - if (!isDefined(t)) { - mGlobals.add(t); - // printf("Adding new global function %s\n", nameof(t)); - } - VariableInfo* pVI = VI(t); - int n = (intptr_t) pVI->pAddress; - /* forward reference: try our lookup function */ - if (!n) { - linkGlobal(t, tok == '('); - n = (intptr_t) pVI->pAddress; - if (!n && tok != '(') { - error("Undeclared variable %s", nameof(t)); - } - } - if (tok != '(') { - /* variable or function name */ - if (!n) { - linkGlobal(t, false); - n = (intptr_t) pVI->pAddress; - if (!n) { - error("Undeclared variable %s", nameof(t)); - } - } - } - // load a variable - Type* pVal; - ExpressionType et; - if (pVI->pType->tag == TY_ARRAY) { - pVal = pVI->pType; - et = ET_RVALUE; - } else { - pVal = createPtrType(pVI->pType); - et = ET_LVALUE; - } - if (n) { - int tag = pVal->pHead->tag; - if (tag == TY_FUNC) { - et = ET_RVALUE; - } - pGen->leaR0(n, pVal, et); - } else { - pVI->pForward = (void*) pGen->leaForward( - (int) pVI->pForward, pVal); - } - } - } - - /* Now handle postfix operators */ - for(;;) { - if (tokl == 11) { - // post inc / post dec - doIncDec(tokc == OP_INCREMENT, true); - next(); - } else if (accept('[')) { - // Array reference - pGen->forceR0RVal(); - pGen->pushR0(); - commaExpr(); - pGen->forceR0RVal(); - pGen->genOp(OP_PLUS); - doPointer(); - skip(']'); - } else if (accept('.')) { - // struct element - pGen->forceR0RVal(); - Type* pStruct = pGen->getR0Type(); - if (pStruct->tag == TY_STRUCT) { - doStructMember(pStruct, true); - } else { - error("expected a struct value to the left of '.'"); - } - } else if (accept(TOK_OP_ARROW)) { - pGen->forceR0RVal(); - Type* pPtr = pGen->getR0Type(); - if (pPtr->tag == TY_POINTER && pPtr->pHead->tag == TY_STRUCT) { - pGen->loadR0FromR0(); - doStructMember(pPtr->pHead, false); - } else { - error("Expected a pointer to a struct to the left of '->'"); - } - } else if (accept('(')) { - /* function call */ - Type* pDecl = NULL; - VariableInfo* pVI = NULL; - Type* pFn = pGen->getR0Type(); - assert(pFn->tag == TY_POINTER); - assert(pFn->pHead->tag == TY_FUNC); - pDecl = pFn->pHead; - pGen->pushR0(); - Type* pArgList = pDecl->pTail; - bool varArgs = pArgList == NULL; - /* push args and invert order */ - a = pGen->beginFunctionCallArguments(); - int l = 0; - int argCount = 0; - while (tok != ')' && tok != EOF) { - if (! varArgs && !pArgList) { - error("Unexpected argument."); - } - expr(); - pGen->forceR0RVal(); - Type* pTargetType; - if (pArgList) { - pTargetType = pArgList->pHead; - pArgList = pArgList->pTail; - } else { - // This is a ... function, just pass arguments in their - // natural type. - pTargetType = pGen->getR0Type(); - if (pTargetType->tag == TY_FLOAT) { - pTargetType = mkpDouble; - } else if (pTargetType->tag == TY_ARRAY) { - // Pass arrays by pointer. - pTargetType = pTargetType->pTail; - } - } - if (pTargetType->tag == TY_VOID) { - error("Can't pass void value for argument %d", - argCount + 1); - } else { - l += pGen->storeR0ToArg(l, pTargetType); - } - if (accept(',')) { - // fine - } else if ( tok != ')') { - error("Expected ',' or ')'"); - } - argCount += 1; - } - if (! varArgs && pArgList) { - error("Expected more argument(s). Saw %d", argCount); - } - pGen->endFunctionCallArguments(pDecl, a, l); - skip(')'); - pGen->callIndirect(l, pDecl); - pGen->adjustStackAfterCall(pDecl, l, true); - } else { - break; - } - } - } - - void doStructMember(Type* pStruct, bool isDot) { - Type* pStructElement = lookupStructMember(pStruct, tok); - if (pStructElement) { - next(); - pGen->addStructOffsetR0(pStructElement->length, createPtrType(pStructElement->pHead)); - } else { - String buf; - decodeToken(buf, tok, true); - error("Expected a struct member to the right of '%s', got %s", - isDot ? "." : "->", buf.getUnwrapped()); - } - } - - void doIncDec(int isInc, int isPost) { - // R0 already has the lval - checkLVal(); - int lit = isInc ? 1 : -1; - pGen->pushR0(); - pGen->loadR0FromR0(); - int tag = pGen->getR0Type()->tag; - if (!(tag == TY_INT || tag == TY_SHORT || tag == TY_CHAR || - tag == TY_POINTER)) { - error("++/-- illegal for this type. %d", tag); - } - if (isPost) { - pGen->over(); - pGen->pushR0(); - pGen->li(lit); - pGen->genOp(OP_PLUS); - pGen->storeR0ToTOS(); - pGen->popR0(); - } else { - pGen->pushR0(); - pGen->li(lit); - pGen->genOp(OP_PLUS); - pGen->over(); - pGen->storeR0ToTOS(); - pGen->popR0(); - } - } - - void doPointer() { - pGen->forceR0RVal(); - Type* pR0Type = pGen->getR0Type(); - if (pR0Type->tag != TY_POINTER) { - error("Expected a pointer type."); - } else { - if (pR0Type->pHead->tag != TY_FUNC) { - pGen->setR0ExpressionType(ET_LVALUE); - } - } - } - - void doAddressOf() { - Type* pR0 = pGen->getR0Type(); - bool isFuncPtr = pR0->tag == TY_POINTER && pR0->pHead->tag == TY_FUNC; - if ((! isFuncPtr) && pGen->getR0ExpressionType() != ET_LVALUE) { - error("Expected an lvalue"); - } - Type* pR0Type = pGen->getR0Type(); - pGen->setR0ExpressionType(ET_RVALUE); - } - - /* Recursive descent parser for binary operations. - */ - void binaryOp(int level) { - intptr_t t, a; - t = 0; - if (level-- == 1) - unaryOrAssignment(); - else { - binaryOp(level); - a = 0; - while (level == tokl) { - t = tokc; - next(); - pGen->forceR0RVal(); - if (level > 8) { - a = pGen->gtst(t == OP_LOGICAL_OR, a); /* && and || output code generation */ - binaryOp(level); - } else { - pGen->pushR0(); - binaryOp(level); - // Check for syntax error. - if (pGen->getR0Type() == NULL) { - // We failed to parse a right-hand argument. - // Push a dummy value so we don't fail - pGen->li(0); - } - pGen->forceR0RVal(); - if ((level == 4) | (level == 5)) { - pGen->gcmp(t); - } else { - pGen->genOp(t); - } - } - } - /* && and || output code generation */ - if (a && level > 8) { - pGen->forceR0RVal(); - a = pGen->gtst(t == OP_LOGICAL_OR, a); - pGen->li(t != OP_LOGICAL_OR); - int b = pGen->gjmp(0); - pGen->gsym(a); - pGen->li(t == OP_LOGICAL_OR); - pGen->gsym(b); - } - } - } - - void commaExpr() { - for(;;) { - expr(); - if (!accept(',')) { - break; - } - } - } - - void expr() { - binaryOp(11); - } - - int test_expr() { - commaExpr(); - pGen->forceR0RVal(); - return pGen->gtst(0, 0); - } - - void block(intptr_t l, bool outermostFunctionBlock) { - intptr_t a, n, t; - - Type* pBaseType; - if ((pBaseType = acceptPrimitiveType())) { - /* declarations */ - localDeclarations(pBaseType); - } else if (tok == TOK_IF) { - next(); - skip('('); - a = test_expr(); - skip(')'); - block(l, false); - if (tok == TOK_ELSE) { - next(); - n = pGen->gjmp(0); /* jmp */ - pGen->gsym(a); - block(l, false); - pGen->gsym(n); /* patch else jmp */ - } else { - pGen->gsym(a); /* patch if test */ - } - } else if ((tok == TOK_WHILE) | (tok == TOK_FOR)) { - t = tok; - next(); - skip('('); - if (t == TOK_WHILE) { - n = pCodeBuf->getPC(); // top of loop, target of "next" iteration - a = test_expr(); - } else { - if (tok != ';') - commaExpr(); - skip(';'); - n = pCodeBuf->getPC(); - a = 0; - if (tok != ';') - a = test_expr(); - skip(';'); - if (tok != ')') { - t = pGen->gjmp(0); - commaExpr(); - pGen->gjmp(n - pCodeBuf->getPC() - pGen->jumpOffset()); - pGen->gsym(t); - n = t + 4; - } - } - skip(')'); - block((intptr_t) &a, false); - pGen->gjmp(n - pCodeBuf->getPC() - pGen->jumpOffset()); /* jmp */ - pGen->gsym(a); - } else if (tok == '{') { - if (! outermostFunctionBlock) { - mLocals.pushLevel(); - } - next(); - while (tok != '}' && tok != EOF) - block(l, false); - skip('}'); - if (! outermostFunctionBlock) { - mLocals.popLevel(); - } - } else { - if (accept(TOK_RETURN)) { - if (tok != ';') { - commaExpr(); - pGen->forceR0RVal(); - if (pReturnType->tag == TY_VOID) { - error("Must not return a value from a void function"); - } else { - pGen->convertR0(pReturnType); - } - } else { - if (pReturnType->tag != TY_VOID) { - error("Must specify a value here"); - } - } - rsym = pGen->gjmp(rsym); /* jmp */ - } else if (accept(TOK_BREAK)) { - *(int *) l = pGen->gjmp(*(int *) l); - } else if (tok != ';') - commaExpr(); - skip(';'); - } - } - - static bool typeEqual(Type* a, Type* b) { - if (a == b) { - return true; - } - if (a == NULL || b == NULL) { - return false; - } - TypeTag at = a->tag; - if (at != b->tag) { - return false; - } - if (at == TY_POINTER) { - return typeEqual(a->pHead, b->pHead); - } else if (at == TY_ARRAY) { - return a->length == b->length && typeEqual(a->pHead, b->pHead); - } else if (at == TY_FUNC || at == TY_PARAM) { - return typeEqual(a->pHead, b->pHead) - && typeEqual(a->pTail, b->pTail); - } else if (at == TY_STRUCT) { - return a->pHead == b->pHead; - } - return true; - } - - Type* createType(TypeTag tag, Type* pHead, Type* pTail) { - assert(tag >= TY_INT && tag <= TY_PARAM); - Type* pType = (Type*) mpCurrentArena->alloc(sizeof(Type)); - memset(pType, 0, sizeof(*pType)); - pType->tag = tag; - pType->pHead = pHead; - pType->pTail = pTail; - return pType; - } - - Type* createPtrType(Type* pType) { - return createType(TY_POINTER, pType, NULL); - } - - /** - * Try to print a type in declaration order - */ - void decodeType(String& buffer, Type* pType) { - buffer.clear(); - if (pType == NULL) { - buffer.appendCStr("null"); - return; - } - decodeTypeImp(buffer, pType); - } - - void decodeTypeImp(String& buffer, Type* pType) { - decodeTypeImpPrefix(buffer, pType); - decodeId(buffer, pType->id); - decodeTypeImpPostfix(buffer, pType); - } - - void decodeId(String& buffer, tokenid_t id) { - if (id) { - String temp; - decodeToken(temp, id, false); - buffer.append(temp); - } - } - - void decodeTypeImpPrefix(String& buffer, Type* pType) { - TypeTag tag = pType->tag; - - if ((tag >= TY_INT && tag <= TY_DOUBLE) || tag == TY_STRUCT) { - switch (tag) { - case TY_INT: - buffer.appendCStr("int"); - break; - case TY_SHORT: - buffer.appendCStr("short"); - break; - case TY_CHAR: - buffer.appendCStr("char"); - break; - case TY_VOID: - buffer.appendCStr("void"); - break; - case TY_FLOAT: - buffer.appendCStr("float"); - break; - case TY_DOUBLE: - buffer.appendCStr("double"); - break; - case TY_STRUCT: - { - bool isStruct = (pType->pHead->alignment & 0x80000000) != 0; - buffer.appendCStr(isStruct ? "struct" : "union"); - if (pType->pHead && pType->pHead->structTag) { - buffer.append(' '); - decodeId(buffer, pType->pHead->structTag); - } - } - break; - default: - break; - } - buffer.append(' '); - } - - switch (tag) { - case TY_INT: - break; - case TY_SHORT: - break; - case TY_CHAR: - break; - case TY_VOID: - break; - case TY_FLOAT: - break; - case TY_DOUBLE: - break; - case TY_POINTER: - decodeTypeImpPrefix(buffer, pType->pHead); - if(pType->pHead && pType->pHead->tag == TY_FUNC) { - buffer.append('('); - } - buffer.append('*'); - break; - case TY_ARRAY: - decodeTypeImpPrefix(buffer, pType->pHead); - break; - case TY_STRUCT: - break; - case TY_FUNC: - decodeTypeImp(buffer, pType->pHead); - break; - case TY_PARAM: - decodeTypeImp(buffer, pType->pHead); - break; - default: - String temp; - temp.printf("Unknown tag %d", pType->tag); - buffer.append(temp); - break; - } - } - - void decodeTypeImpPostfix(String& buffer, Type* pType) { - TypeTag tag = pType->tag; - - switch(tag) { - case TY_POINTER: - if(pType->pHead && pType->pHead->tag == TY_FUNC) { - buffer.append(')'); - } - decodeTypeImpPostfix(buffer, pType->pHead); - break; - case TY_ARRAY: - { - String temp; - temp.printf("[%d]", pType->length); - buffer.append(temp); - } - break; - case TY_STRUCT: - if (pType->pHead->length >= 0) { - buffer.appendCStr(" {"); - for(Type* pArg = pType->pTail; pArg; pArg = pArg->pTail) { - decodeTypeImp(buffer, pArg->pHead); - buffer.appendCStr(";"); - } - buffer.append('}'); - } - break; - case TY_FUNC: - buffer.append('('); - for(Type* pArg = pType->pTail; pArg; pArg = pArg->pTail) { - decodeTypeImp(buffer, pArg); - if (pArg->pTail) { - buffer.appendCStr(", "); - } - } - buffer.append(')'); - break; - default: - break; - } - } - - void printType(Type* pType) { - String buffer; - decodeType(buffer, pType); - fprintf(stderr, "%s\n", buffer.getUnwrapped()); - } - - Type* acceptPrimitiveType() { - Type* pType; - if (tok == TOK_INT) { - pType = mkpInt; - } else if (tok == TOK_SHORT) { - pType = mkpShort; - } else if (tok == TOK_CHAR) { - pType = mkpChar; - } else if (tok == TOK_VOID) { - pType = mkpVoid; - } else if (tok == TOK_FLOAT) { - pType = mkpFloat; - } else if (tok == TOK_DOUBLE) { - pType = mkpDouble; - } else if (tok == TOK_STRUCT || tok == TOK_UNION) { - return acceptStruct(); - } else { - return NULL; - } - next(); - return pType; - } - - Type* acceptStruct() { - assert(tok == TOK_STRUCT || tok == TOK_UNION); - bool isStruct = tok == TOK_STRUCT; - next(); - tokenid_t structTag = acceptSymbol(); - bool isDeclaration = accept('{'); - bool fail = false; - - Type* pStructType = createType(TY_STRUCT, NULL, NULL); - if (structTag) { - Token* pToken = &mTokenTable[structTag]; - VariableInfo* pStructInfo = pToken->mpStructInfo; - bool needToDeclare = !pStructInfo; - if (pStructInfo) { - if (isDeclaration) { - if (mpCurrentSymbolStack->isStructTagDefinedAtCurrentLevel(structTag)) { - if (pStructInfo->pType->pHead->length == -1) { - // we're filling in a forward declaration. - needToDeclare = false; - } else { - error("A struct with the same name is already defined at this level."); - fail = true; - } - } else { - needToDeclare = true; - } - } - if (!fail) { - assert(pStructInfo->isStructTag); - pStructType->pHead = pStructInfo->pType; - pStructType->pTail = pStructType->pHead->pTail; - } - } - - if (needToDeclare) { - // This is a new struct name - pToken->mpStructInfo = mpCurrentSymbolStack->addStructTag(structTag); - pStructType = createType(TY_STRUCT, NULL, NULL); - pStructType->structTag = structTag; - pStructType->pHead = pStructType; - if (! isDeclaration) { - // A forward declaration - pStructType->length = -1; - } - pToken->mpStructInfo->pType = pStructType; - } - } else { - // An anonymous struct - pStructType->pHead = pStructType; - } - - if (isDeclaration) { - size_t offset = 0; - size_t structSize = 0; - size_t structAlignment = 0; - Type** pParamHolder = & pStructType->pHead->pTail; - while (tok != '}' && tok != EOF) { - Type* pPrimitiveType = expectPrimitiveType(); - if (pPrimitiveType) { - while (tok != ';' && tok != EOF) { - Type* pItem = acceptDeclaration(pPrimitiveType, true, false); - if (!pItem) { - break; - } - if (lookupStructMember(pStructType, pItem->id)) { - String buf; - decodeToken(buf, pItem->id, false); - error("Duplicate struct member %s", buf.getUnwrapped()); - } - Type* pStructElement = createType(TY_PARAM, pItem, NULL); - size_t alignment = pGen->alignmentOf(pItem); - if (alignment > structAlignment) { - structAlignment = alignment; - } - size_t alignmentMask = alignment - 1; - offset = (offset + alignmentMask) & ~alignmentMask; - pStructElement->length = offset; - size_t size = pGen->sizeOf(pItem); - if (isStruct) { - offset += size; - structSize = offset; - } else { - if (size >= structSize) { - structSize = size; - } - } - *pParamHolder = pStructElement; - pParamHolder = &pStructElement->pTail; - accept(','); - } - skip(';'); - } else { - // Some sort of syntax error, skip token and keep trying - next(); - } - } - if (!fail) { - pStructType->pHead->length = structSize; - pStructType->pHead->alignment = structAlignment | (isStruct << 31); - } - skip('}'); - } - if (fail) { - pStructType = NULL; - } - return pStructType; - } - - Type* lookupStructMember(Type* pStruct, tokenid_t memberId) { - for(Type* pStructElement = pStruct->pHead->pTail; pStructElement; pStructElement = pStructElement->pTail) { - if (pStructElement->pHead->id == memberId) { - return pStructElement; - } - } - return NULL; - } - - Type* acceptDeclaration(Type* pType, bool nameAllowed, bool nameRequired) { - tokenid_t declName = 0; - bool reportFailure = false; - pType = acceptDecl2(pType, declName, nameAllowed, - nameRequired, reportFailure); - if (declName) { - // Clone the parent type so we can set a unique ID - Type* pOldType = pType; - pType = createType(pType->tag, pType->pHead, pType->pTail); - *pType = *pOldType; - pType->id = declName; - } else if (nameRequired) { - error("Expected a variable name"); - } -#if 0 - fprintf(stderr, "Parsed a declaration: "); - printType(pType); -#endif - if (reportFailure) { - return NULL; - } - return pType; - } - - Type* expectDeclaration(Type* pBaseType) { - bool nameRequired = pBaseType->tag != TY_STRUCT; - Type* pType = acceptDeclaration(pBaseType, true, nameRequired); - if (! pType) { - error("Expected a declaration"); - } - return pType; - } - - /* Used for accepting types that appear in casts */ - Type* acceptCastTypeDeclaration() { - Type* pType = acceptPrimitiveType(); - if (pType) { - pType = acceptDeclaration(pType, false, false); - } - return pType; - } - - Type* expectCastTypeDeclaration() { - Type* pType = acceptCastTypeDeclaration(); - if (! pType) { - error("Expected a declaration"); - } - return pType; - } - - Type* acceptDecl2(Type* pType, tokenid_t& declName, - bool nameAllowed, bool nameRequired, - bool& reportFailure) { - while (accept('*')) { - pType = createType(TY_POINTER, pType, NULL); - } - pType = acceptDecl3(pType, declName, nameAllowed, nameRequired, - reportFailure); - return pType; - } - - Type* acceptDecl3(Type* pType, tokenid_t& declName, - bool nameAllowed, bool nameRequired, - bool& reportFailure) { - // direct-dcl : - // name - // (dcl) - // direct-dcl() - // direct-dcl[] - Type* pNewHead = NULL; - if (accept('(')) { - pNewHead = acceptDecl2(pNewHead, declName, nameAllowed, - nameRequired, reportFailure); - skip(')'); - } else if ((declName = acceptSymbol()) != 0) { - if (nameAllowed == false && declName) { - error("Symbol %s not allowed here", nameof(declName)); - reportFailure = true; - } - } else if (nameRequired && ! declName) { - String temp; - decodeToken(temp, tok, true); - error("Expected name. Got %s", temp.getUnwrapped()); - reportFailure = true; - } - for(;;) { - if (accept('(')) { - // Function declaration - Type* pTail = acceptArgs(nameAllowed); - pType = createType(TY_FUNC, pType, pTail); - skip(')'); - } if (accept('[')) { - if (tok != ']') { - if (tok != TOK_NUM || tokc <= 0) { - error("Expected positive integer constant"); - } else { - Type* pDecayType = createPtrType(pType); - pType = createType(TY_ARRAY, pType, pDecayType); - pType->length = tokc; - } - next(); - } - skip(']'); - } else { - break; - } - } - - if (pNewHead) { - Type* pA = pNewHead; - while (pA->pHead) { - pA = pA->pHead; - } - pA->pHead = pType; - pType = pNewHead; - } - return pType; - } - - Type* acceptArgs(bool nameAllowed) { - Type* pHead = NULL; - Type* pTail = NULL; - for(;;) { - Type* pBaseArg = acceptPrimitiveType(); - if (pBaseArg) { - Type* pArg = acceptDeclaration(pBaseArg, nameAllowed, false); - if (pArg) { - Type* pParam = createType(TY_PARAM, pArg, NULL); - if (!pHead) { - pHead = pParam; - pTail = pParam; - } else { - pTail->pTail = pParam; - pTail = pParam; - } - } - } - if (! accept(',')) { - break; - } - } - return pHead; - } - - Type* expectPrimitiveType() { - Type* pType = acceptPrimitiveType(); - if (!pType) { - String buf; - decodeToken(buf, tok, true); - error("Expected a type, got %s", buf.getUnwrapped()); - } - return pType; - } - - void checkLVal() { - if (pGen->getR0ExpressionType() != ET_LVALUE) { - error("Expected an lvalue"); - } - } - - void addGlobalSymbol(Type* pDecl) { - tokenid_t t = pDecl->id; - VariableInfo* pVI = VI(t); - if(pVI && pVI->pAddress) { - reportDuplicate(t); - } - mGlobals.add(pDecl); - } - - void reportDuplicate(tokenid_t t) { - error("Duplicate definition of %s", nameof(t)); - } - - void addLocalSymbol(Type* pDecl) { - tokenid_t t = pDecl->id; - if (mLocals.isDefinedAtCurrentLevel(t)) { - reportDuplicate(t); - } - mLocals.add(pDecl); - } - - bool checkUndeclaredStruct(Type* pBaseType) { - if (pBaseType->tag == TY_STRUCT && pBaseType->length < 0) { - String temp; - decodeToken(temp, pBaseType->structTag, false); - error("Undeclared struct %s", temp.getUnwrapped()); - return true; - } - return false; - } - - void localDeclarations(Type* pBaseType) { - intptr_t a; - - while (pBaseType) { - while (tok != ';' && tok != EOF) { - Type* pDecl = expectDeclaration(pBaseType); - if (!pDecl) { - break; - } - if (!pDecl->id) { - break; - } - if (checkUndeclaredStruct(pDecl)) { - break; - } - addLocalSymbol(pDecl); - if (pDecl->tag == TY_FUNC) { - if (tok == '{') { - error("Nested functions are not allowed. Did you forget a '}' ?"); - break; - } - // Else it's a forward declaration of a function. - } else { - int variableAddress = 0; - size_t alignment = pGen->alignmentOf(pDecl); - assert(alignment > 0); - size_t alignmentMask = ~ (alignment - 1); - size_t sizeOf = pGen->sizeOf(pDecl); - assert(sizeOf > 0); - loc = (loc + alignment - 1) & alignmentMask; - size_t alignedSize = (sizeOf + alignment - 1) & alignmentMask; - loc = loc + alignedSize; - variableAddress = -loc; - VI(pDecl->id)->pAddress = (void*) variableAddress; - if (accept('=')) { - /* assignment */ - pGen->leaR0(variableAddress, createPtrType(pDecl), ET_LVALUE); - pGen->pushR0(); - expr(); - pGen->forceR0RVal(); - pGen->storeR0ToTOS(); - } - } - if (tok == ',') - next(); - } - skip(';'); - pBaseType = acceptPrimitiveType(); - } - } - - bool checkSymbol() { - return checkSymbol(tok); - } - - void decodeToken(String& buffer, tokenid_t token, bool quote) { - if (token == EOF ) { - buffer.printf("EOF"); - } else if (token == TOK_NUM) { - buffer.printf("numeric constant"); - } else if (token >= 0 && token < 256) { - if (token < 32) { - buffer.printf("'\\x%02x'", token); - } else { - buffer.printf("'%c'", token); - } - } else { - if (quote) { - if (token >= TOK_KEYWORD && token < TOK_SYMBOL) { - buffer.printf("keyword \"%s\"", nameof(token)); - } else { - buffer.printf("symbol \"%s\"", nameof(token)); - } - } else { - buffer.printf("%s", nameof(token)); - } - } - } - - void printToken(tokenid_t token) { - String buffer; - decodeToken(buffer, token, true); - fprintf(stderr, "%s\n", buffer.getUnwrapped()); - } - - bool checkSymbol(tokenid_t token) { - bool result = token >= TOK_SYMBOL; - if (!result) { - String temp; - decodeToken(temp, token, true); - error("Expected symbol. Got %s", temp.getUnwrapped()); - } - return result; - } - - tokenid_t acceptSymbol() { - tokenid_t result = 0; - if (tok >= TOK_SYMBOL) { - result = tok; - next(); - } - return result; - } - - void globalDeclarations() { - mpCurrentSymbolStack = &mGlobals; - while (tok != EOF) { - Type* pBaseType = expectPrimitiveType(); - if (!pBaseType) { - break; - } - Type* pDecl = expectDeclaration(pBaseType); - if (!pDecl) { - break; - } - if (!pDecl->id) { - skip(';'); - continue; - } - - if (checkUndeclaredStruct(pDecl)) { - skip(';'); - continue; - } - - if (! isDefined(pDecl->id)) { - addGlobalSymbol(pDecl); - } - VariableInfo* name = VI(pDecl->id); - if (name && name->pAddress) { - error("Already defined global %s", nameof(pDecl->id)); - } - if (pDecl->tag < TY_FUNC) { - // it's a variable declaration - for(;;) { - if (name && !name->pAddress) { - name->pAddress = (int*) allocGlobalSpace( - pGen->alignmentOf(name->pType), - pGen->sizeOf(name->pType)); - } - if (accept('=')) { - if (tok == TOK_NUM) { - if (name) { - * (int*) name->pAddress = tokc; - } - next(); - } else { - error("Expected an integer constant"); - } - } - if (!accept(',')) { - break; - } - pDecl = expectDeclaration(pBaseType); - if (!pDecl) { - break; - } - if (! isDefined(pDecl->id)) { - addGlobalSymbol(pDecl); - } - name = VI(pDecl->id); - } - skip(';'); - } else { - // Function declaration - if (accept(';')) { - // forward declaration. - } else if (tok != '{') { - error("expected '{'"); - } else { - mpCurrentArena = &mLocalArena; - mpCurrentSymbolStack = &mLocals; - if (name) { - /* patch forward references */ - pGen->resolveForward((int) name->pForward); - /* put function address */ - name->pAddress = (void*) pCodeBuf->getPC(); - } - // Calculate stack offsets for parameters - mLocals.pushLevel(); - intptr_t a = 8; - int argCount = 0; - for (Type* pP = pDecl->pTail; pP; pP = pP->pTail) { - Type* pArg = pP->pHead; - if (pArg->id) { - addLocalSymbol(pArg); - } - /* read param name and compute offset */ - Type* pPassingType = passingType(pArg); - size_t alignment = pGen->alignmentOf(pPassingType); - a = (a + alignment - 1) & ~ (alignment-1); - if (pArg->id) { - VI(pArg->id)->pAddress = (void*) a; - } - a = a + pGen->sizeOf(pPassingType); - argCount++; - } - rsym = loc = 0; - pReturnType = pDecl->pHead; - a = pGen->functionEntry(pDecl); - block(0, true); - pGen->gsym(rsym); - pGen->functionExit(pDecl, a, loc); - mLocals.popLevel(); - mpCurrentArena = &mGlobalArena; - mpCurrentSymbolStack = &mGlobals; - } - } - } - } - - Type* passingType(Type* pType) { - switch (pType->tag) { - case TY_CHAR: - case TY_SHORT: - return mkpInt; - default: - return pType; - } - } - - char* allocGlobalSpace(size_t alignment, size_t bytes) { - size_t base = (((size_t) glo) + alignment - 1) & ~(alignment-1); - size_t end = base + bytes; - if ((end - (size_t) pGlobalBase) > (size_t) ALLOC_SIZE) { - error("Global space exhausted"); - assert(false); - return NULL; - } - char* result = (char*) base; - glo = (char*) end; - return result; - } - - void cleanup() { - if (pGlobalBase != 0) { - free(pGlobalBase); - pGlobalBase = 0; - } - if (pGen) { - delete pGen; - pGen = 0; - } - if (pCodeBuf) { - delete pCodeBuf; - pCodeBuf = 0; - } - if (file) { - delete file; - file = 0; - } - } - - // One-time initialization, when class is constructed. - void init() { - mpSymbolLookupFn = 0; - mpSymbolLookupContext = 0; - } - - void clear() { - tok = 0; - tokc = 0; - tokl = 0; - ch = 0; - rsym = 0; - loc = 0; - glo = 0; - dptr = 0; - dch = 0; - file = 0; - pGlobalBase = 0; - pCodeBuf = 0; - pGen = 0; - mPragmaStringCount = 0; - mCompileResult = 0; - mLineNumber = 1; - mbBumpLine = false; - mbSuppressMacroExpansion = false; - } - - void setArchitecture(const char* architecture) { - delete pGen; - pGen = 0; - - delete pCodeBuf; - pCodeBuf = new CodeBuf(); - - if (architecture != NULL) { -#ifdef PROVIDE_ARM_CODEGEN - if (! pGen && strcmp(architecture, "arm") == 0) { - pGen = new ARMCodeGenerator(); - pCodeBuf = new ARMCodeBuf(pCodeBuf); - } -#endif -#ifdef PROVIDE_X86_CODEGEN - if (! pGen && strcmp(architecture, "x86") == 0) { - pGen = new X86CodeGenerator(); - } -#endif - if (!pGen ) { - error("Unknown architecture %s\n", architecture); - } - } - - if (pGen == NULL) { -#if defined(DEFAULT_ARM_CODEGEN) - pGen = new ARMCodeGenerator(); - pCodeBuf = new ARMCodeBuf(pCodeBuf); -#elif defined(DEFAULT_X86_CODEGEN) - pGen = new X86CodeGenerator(); -#endif - } - if (pGen == NULL) { - error("No code generator defined."); - } else { - pGen->setErrorSink(this); - pGen->setTypes(mkpInt); - } - } - -public: - struct args { - args() { - architecture = 0; - } - const char* architecture; - }; - - Compiler() { - init(); - clear(); - } - - ~Compiler() { - cleanup(); - } - - void registerSymbolCallback(ACCSymbolLookupFn pFn, ACCvoid* pContext) { - mpSymbolLookupFn = pFn; - mpSymbolLookupContext = pContext; - } - - int compile(const char* text, size_t textLength) { - int result; - - mpCurrentArena = &mGlobalArena; - createPrimitiveTypes(); - cleanup(); - clear(); - mTokenTable.setArena(&mGlobalArena); - mGlobals.setArena(&mGlobalArena); - mGlobals.setTokenTable(&mTokenTable); - mLocals.setArena(&mLocalArena); - mLocals.setTokenTable(&mTokenTable); - - internKeywords(); - setArchitecture(NULL); - if (!pGen) { - return -1; - } -#ifdef PROVIDE_TRACE_CODEGEN - pGen = new TraceCodeGenerator(pGen); -#endif - pGen->setErrorSink(this); - - if (pCodeBuf) { - pCodeBuf->init(ALLOC_SIZE); - } - pGen->init(pCodeBuf); - file = new TextInputStream(text, textLength); - pGlobalBase = (char*) calloc(1, ALLOC_SIZE); - glo = pGlobalBase; - inp(); - next(); - globalDeclarations(); - checkForUndefinedForwardReferences(); - result = pGen->finishCompile(); - if (result == 0) { - if (mErrorBuf.len()) { - result = -2; - } - } - mCompileResult = result; - return result; - } - - void createPrimitiveTypes() { - mkpInt = createType(TY_INT, NULL, NULL); - mkpShort = createType(TY_SHORT, NULL, NULL); - mkpChar = createType(TY_CHAR, NULL, NULL); - mkpVoid = createType(TY_VOID, NULL, NULL); - mkpFloat = createType(TY_FLOAT, NULL, NULL); - mkpDouble = createType(TY_DOUBLE, NULL, NULL); - mkpIntFn = createType(TY_FUNC, mkpInt, NULL); - mkpIntPtr = createPtrType(mkpInt); - mkpCharPtr = createPtrType(mkpChar); - mkpFloatPtr = createPtrType(mkpFloat); - mkpDoublePtr = createPtrType(mkpDouble); - mkpPtrIntFn = createPtrType(mkpIntFn); - } - - void checkForUndefinedForwardReferences() { - mGlobals.forEach(static_ufrcFn, this); - } - - static bool static_ufrcFn(VariableInfo* value, void* context) { - Compiler* pCompiler = (Compiler*) context; - return pCompiler->undefinedForwardReferenceCheck(value); - } - - bool undefinedForwardReferenceCheck(VariableInfo* value) { - if (!value->pAddress && value->pForward) { - error("Undefined forward reference: %s", - mTokenTable[value->tok].pText); - } - return true; - } - - /* Look through the symbol table to find a symbol. - * If found, return its value. - */ - void* lookup(const char* name) { - if (mCompileResult == 0) { - tokenid_t tok = mTokenTable.intern(name, strlen(name)); - VariableInfo* pVariableInfo = VI(tok); - if (pVariableInfo) { - return pVariableInfo->pAddress; - } - } - return NULL; - } - - void getPragmas(ACCsizei* actualStringCount, - ACCsizei maxStringCount, ACCchar** strings) { - int stringCount = mPragmaStringCount; - if (actualStringCount) { - *actualStringCount = stringCount; - } - if (stringCount > maxStringCount) { - stringCount = maxStringCount; - } - if (strings) { - char* pPragmas = mPragmas.getUnwrapped(); - while (stringCount-- > 0) { - *strings++ = pPragmas; - pPragmas += strlen(pPragmas) + 1; - } - } - } - - void getProgramBinary(ACCvoid** base, ACCsizei* length) { - *base = pCodeBuf->getBase(); - *length = (ACCsizei) pCodeBuf->getSize(); - } - - char* getErrorMessage() { - return mErrorBuf.getUnwrapped(); - } -}; - -const char* Compiler::operatorChars = - "++--*@/@%@+@-@<<>><=>=<@>@==!=&&||&@^@|@~@!@"; - -const char Compiler::operatorLevel[] = - {11, 11, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, - 5, 5, /* ==, != */ - 9, 10, /* &&, || */ - 6, 7, 8, /* & ^ | */ - 2, 2 /* ~ ! */ - }; - -#ifdef PROVIDE_X86_CODEGEN -const int Compiler::X86CodeGenerator::operatorHelper[] = { - 0x1, // ++ - 0xff, // -- - 0xc1af0f, // * - 0xf9f79991, // / - 0xf9f79991, // % (With manual assist to swap results) - 0xc801, // + - 0xd8f7c829, // - - 0xe0d391, // << - 0xf8d391, // >> - 0xe, // <= - 0xd, // >= - 0xc, // < - 0xf, // > - 0x4, // == - 0x5, // != - 0x0, // && - 0x1, // || - 0xc821, // & - 0xc831, // ^ - 0xc809, // | - 0xd0f7, // ~ - 0x4 // ! -}; -#endif - -struct ACCscript { - ACCscript() { - text = 0; - textLength = 0; - accError = ACC_NO_ERROR; - } - - ~ACCscript() { - delete text; - } - - void registerSymbolCallback(ACCSymbolLookupFn pFn, ACCvoid* pContext) { - compiler.registerSymbolCallback(pFn, pContext); - } - - void setError(ACCenum error) { - if (accError == ACC_NO_ERROR && error != ACC_NO_ERROR) { - accError = error; - } - } - - ACCenum getError() { - ACCenum result = accError; - accError = ACC_NO_ERROR; - return result; - } - - Compiler compiler; - char* text; - int textLength; - ACCenum accError; -}; - - -extern "C" -ACCscript* accCreateScript() { - return new ACCscript(); -} - -extern "C" -ACCenum accGetError( ACCscript* script ) { - return script->getError(); -} - -extern "C" -void accDeleteScript(ACCscript* script) { - delete script; -} - -extern "C" -void accRegisterSymbolCallback(ACCscript* script, ACCSymbolLookupFn pFn, - ACCvoid* pContext) { - script->registerSymbolCallback(pFn, pContext); -} - -extern "C" -void accScriptSource(ACCscript* script, - ACCsizei count, - const ACCchar ** string, - const ACCint * length) { - int totalLength = 0; - for(int i = 0; i < count; i++) { - int len = -1; - const ACCchar* s = string[i]; - if (length) { - len = length[i]; - } - if (len < 0) { - len = strlen(s); - } - totalLength += len; - } - delete script->text; - char* text = new char[totalLength + 1]; - script->text = text; - script->textLength = totalLength; - char* dest = text; - for(int i = 0; i < count; i++) { - int len = -1; - const ACCchar* s = string[i]; - if (length) { - len = length[i]; - } - if (len < 0) { - len = strlen(s); - } - memcpy(dest, s, len); - dest += len; - } - text[totalLength] = '\0'; - -#ifdef DEBUG_SAVE_INPUT_TO_FILE - LOGD("Saving input to file..."); - int counter; - char path[PATH_MAX]; - for (counter = 0; counter < 4096; counter++) { - sprintf(path, DEBUG_DUMP_PATTERN, counter); - if(access(path, F_OK) != 0) { - break; - } - } - if (counter < 4096) { - LOGD("Saving input to file %s", path); - FILE* fd = fopen(path, "w"); - if (fd) { - fwrite(text, totalLength, 1, fd); - fclose(fd); - LOGD("Saved input to file %s", path); - } else { - LOGD("Could not save. errno: %d", errno); - } - } -#endif -} - -extern "C" -void accCompileScript(ACCscript* script) { - int result = script->compiler.compile(script->text, script->textLength); - if (result) { - script->setError(ACC_INVALID_OPERATION); - } -} - -extern "C" -void accGetScriptiv(ACCscript* script, - ACCenum pname, - ACCint * params) { - switch (pname) { - case ACC_INFO_LOG_LENGTH: - *params = 0; - break; - } -} - -extern "C" -void accGetScriptInfoLog(ACCscript* script, - ACCsizei maxLength, - ACCsizei * length, - ACCchar * infoLog) { - char* message = script->compiler.getErrorMessage(); - int messageLength = strlen(message) + 1; - if (length) { - *length = messageLength; - } - if (infoLog && maxLength > 0) { - int trimmedLength = maxLength < messageLength ? - maxLength : messageLength; - memcpy(infoLog, message, trimmedLength); - infoLog[trimmedLength] = 0; - } -} - -extern "C" -void accGetScriptLabel(ACCscript* script, const ACCchar * name, - ACCvoid ** address) { - void* value = script->compiler.lookup(name); - if (value) { - *address = value; - } else { - script->setError(ACC_INVALID_VALUE); - } -} - -extern "C" -void accGetPragmas(ACCscript* script, ACCsizei* actualStringCount, - ACCsizei maxStringCount, ACCchar** strings){ - script->compiler.getPragmas(actualStringCount, maxStringCount, strings); -} - -extern "C" -void accGetProgramBinary(ACCscript* script, - ACCvoid** base, ACCsizei* length) { - script->compiler.getProgramBinary(base, length); -} - - -} // namespace acc - diff --git a/libacc/tests/.gitignore b/libacc/tests/.gitignore deleted file mode 100644 index a26b298632c..00000000000 --- a/libacc/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -test-acc -*.out diff --git a/libacc/tests/Android.mk b/libacc/tests/Android.mk deleted file mode 100644 index e9fbe03199d..00000000000 --- a/libacc/tests/Android.mk +++ /dev/null @@ -1,66 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# Executable for host -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE:= acc - -LOCAL_SRC_FILES:= \ - main.cpp - -LOCAL_SHARED_LIBRARIES := \ - libacc - -LOCAL_MODULE_TAGS := tests - -include $(BUILD_HOST_EXECUTABLE) - -# Executable for target -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE:= acc - -LOCAL_SRC_FILES:= \ - main.cpp \ - disassem.cpp - -LOCAL_SHARED_LIBRARIES := \ - libacc - -LOCAL_CFLAGS := -O0 -g - -LOCAL_MODULE_TAGS := tests - -include $(BUILD_EXECUTABLE) - -# Runtime tests for host -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE:= accRuntimeTest - -LOCAL_SRC_FILES:= \ - runtimeTest.cpp - -LOCAL_SHARED_LIBRARIES := \ - libacc - -LOCAL_MODULE_TAGS := tests - -include $(BUILD_HOST_EXECUTABLE) - -# Runtime tests for target -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE:= accRuntimeTest - -LOCAL_SRC_FILES:= \ - runtimeTest.cpp - -LOCAL_SHARED_LIBRARIES := \ - libacc - -LOCAL_CFLAGS := -O0 -g - -LOCAL_MODULE_TAGS := tests - -include $(BUILD_EXECUTABLE) diff --git a/libacc/tests/accarm b/libacc/tests/accarm deleted file mode 100755 index 6b1bf663f6c..00000000000 --- a/libacc/tests/accarm +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python -# -# Run a test on the ARM version of acc. - -import unittest -import subprocess -import os -import sys - -def compile(args): - proc = subprocess.Popen(["acc"] + args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - result = proc.communicate() - return result - -def runCmd(args): - proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - result = proc.communicate() - return result[0].strip() - -def uname(): - return runCmd(["uname"]) - -def unameM(): - return runCmd(["uname", "-m"]) - -def which(item): - return runCmd(["which", item]) - -def adb(args): - return runCmd(["adb"] + args) - -def setupArm(file): - print "Setting up arm" - adb(["remount"]) - adb(["shell", "rm", "/system/bin/acc"]) - adb(["shell", "mkdir", "/system/bin/accdata"]) - adb(["shell", "mkdir", "/system/bin/accdata/data"]) - - remoteFileName = os.path.join("/system/bin/accdata", file) - adb(["push", file, remoteFileName]) - - # Copy over compiler - adb(["sync"]) - return remoteFileName - -def compileArm(args): - remoteArgs = [] - fileName = "" - for arg in sys.argv[1:]: - if arg.startswith('-'): - remoteArgs.append(arg) - else: - fileName = arg - - remoteFileName = setupArm(fileName) - remoteArgs.append(remoteFileName) - remoteCmdLine = ["adb", "shell", "/system/bin/acc"] + remoteArgs - proc = subprocess.Popen(remoteCmdLine, stdout=subprocess.PIPE) - result = proc.communicate() - return result[0].replace("\r","") - - -def main(): - print compileArm(sys.argv[1:]) - -if __name__ == '__main__': - main() - - diff --git a/libacc/tests/armreg.h b/libacc/tests/armreg.h deleted file mode 100644 index fde81ba8976..00000000000 --- a/libacc/tests/armreg.h +++ /dev/null @@ -1,300 +0,0 @@ -/* $NetBSD: armreg.h,v 1.28 2003/10/31 16:30:15 scw Exp $ */ - -/*- - * Copyright (c) 1998, 2001 Ben Harris - * Copyright (c) 1994-1996 Mark Brinicombe. - * Copyright (c) 1994 Brini. - * All rights reserved. - * - * This code is derived from software written for Brini by Mark Brinicombe - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Brini. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/armreg.h,v 1.3 2005/11/21 19:06:25 cognet Exp $ - */ - -#ifndef MACHINE_ARMREG_H -#define MACHINE_ARMREG_H -#define INSN_SIZE 4 -#define INSN_COND_MASK 0xf0000000 /* Condition mask */ -#define PSR_MODE 0x0000001f /* mode mask */ -#define PSR_USR26_MODE 0x00000000 -#define PSR_FIQ26_MODE 0x00000001 -#define PSR_IRQ26_MODE 0x00000002 -#define PSR_SVC26_MODE 0x00000003 -#define PSR_USR32_MODE 0x00000010 -#define PSR_FIQ32_MODE 0x00000011 -#define PSR_IRQ32_MODE 0x00000012 -#define PSR_SVC32_MODE 0x00000013 -#define PSR_ABT32_MODE 0x00000017 -#define PSR_UND32_MODE 0x0000001b -#define PSR_SYS32_MODE 0x0000001f -#define PSR_32_MODE 0x00000010 -#define PSR_FLAGS 0xf0000000 /* flags */ - -#define PSR_C_bit (1 << 29) /* carry */ - -/* The high-order byte is always the implementor */ -#define CPU_ID_IMPLEMENTOR_MASK 0xff000000 -#define CPU_ID_ARM_LTD 0x41000000 /* 'A' */ -#define CPU_ID_DEC 0x44000000 /* 'D' */ -#define CPU_ID_INTEL 0x69000000 /* 'i' */ -#define CPU_ID_TI 0x54000000 /* 'T' */ - -/* How to decide what format the CPUID is in. */ -#define CPU_ID_ISOLD(x) (((x) & 0x0000f000) == 0x00000000) -#define CPU_ID_IS7(x) (((x) & 0x0000f000) == 0x00007000) -#define CPU_ID_ISNEW(x) (!CPU_ID_ISOLD(x) && !CPU_ID_IS7(x)) - -/* On ARM3 and ARM6, this byte holds the foundry ID. */ -#define CPU_ID_FOUNDRY_MASK 0x00ff0000 -#define CPU_ID_FOUNDRY_VLSI 0x00560000 - -/* On ARM7 it holds the architecture and variant (sub-model) */ -#define CPU_ID_7ARCH_MASK 0x00800000 -#define CPU_ID_7ARCH_V3 0x00000000 -#define CPU_ID_7ARCH_V4T 0x00800000 -#define CPU_ID_7VARIANT_MASK 0x007f0000 - -/* On more recent ARMs, it does the same, but in a different format */ -#define CPU_ID_ARCH_MASK 0x000f0000 -#define CPU_ID_ARCH_V3 0x00000000 -#define CPU_ID_ARCH_V4 0x00010000 -#define CPU_ID_ARCH_V4T 0x00020000 -#define CPU_ID_ARCH_V5 0x00030000 -#define CPU_ID_ARCH_V5T 0x00040000 -#define CPU_ID_ARCH_V5TE 0x00050000 -#define CPU_ID_VARIANT_MASK 0x00f00000 - -/* Next three nybbles are part number */ -#define CPU_ID_PARTNO_MASK 0x0000fff0 - -/* Intel XScale has sub fields in part number */ -#define CPU_ID_XSCALE_COREGEN_MASK 0x0000e000 /* core generation */ -#define CPU_ID_XSCALE_COREREV_MASK 0x00001c00 /* core revision */ -#define CPU_ID_XSCALE_PRODUCT_MASK 0x000003f0 /* product number */ - -/* And finally, the revision number. */ -#define CPU_ID_REVISION_MASK 0x0000000f - -/* Individual CPUs are probably best IDed by everything but the revision. */ -#define CPU_ID_CPU_MASK 0xfffffff0 - -/* Fake CPU IDs for ARMs without CP15 */ -#define CPU_ID_ARM2 0x41560200 -#define CPU_ID_ARM250 0x41560250 - -/* Pre-ARM7 CPUs -- [15:12] == 0 */ -#define CPU_ID_ARM3 0x41560300 -#define CPU_ID_ARM600 0x41560600 -#define CPU_ID_ARM610 0x41560610 -#define CPU_ID_ARM620 0x41560620 - -/* ARM7 CPUs -- [15:12] == 7 */ -#define CPU_ID_ARM700 0x41007000 /* XXX This is a guess. */ -#define CPU_ID_ARM710 0x41007100 -#define CPU_ID_ARM7500 0x41027100 /* XXX This is a guess. */ -#define CPU_ID_ARM710A 0x41047100 /* inc ARM7100 */ -#define CPU_ID_ARM7500FE 0x41077100 -#define CPU_ID_ARM710T 0x41807100 -#define CPU_ID_ARM720T 0x41807200 -#define CPU_ID_ARM740T8K 0x41807400 /* XXX no MMU, 8KB cache */ -#define CPU_ID_ARM740T4K 0x41817400 /* XXX no MMU, 4KB cache */ - -/* Post-ARM7 CPUs */ -#define CPU_ID_ARM810 0x41018100 -#define CPU_ID_ARM920T 0x41129200 -#define CPU_ID_ARM920T_ALT 0x41009200 -#define CPU_ID_ARM922T 0x41029220 -#define CPU_ID_ARM940T 0x41029400 /* XXX no MMU */ -#define CPU_ID_ARM946ES 0x41049460 /* XXX no MMU */ -#define CPU_ID_ARM966ES 0x41049660 /* XXX no MMU */ -#define CPU_ID_ARM966ESR1 0x41059660 /* XXX no MMU */ -#define CPU_ID_ARM1020E 0x4115a200 /* (AKA arm10 rev 1) */ -#define CPU_ID_ARM1022ES 0x4105a220 -#define CPU_ID_SA110 0x4401a100 -#define CPU_ID_SA1100 0x4401a110 -#define CPU_ID_TI925T 0x54029250 -#define CPU_ID_SA1110 0x6901b110 -#define CPU_ID_IXP1200 0x6901c120 -#define CPU_ID_80200 0x69052000 -#define CPU_ID_PXA250 0x69052100 /* sans core revision */ -#define CPU_ID_PXA210 0x69052120 -#define CPU_ID_PXA250A 0x69052100 /* 1st version Core */ -#define CPU_ID_PXA210A 0x69052120 /* 1st version Core */ -#define CPU_ID_PXA250B 0x69052900 /* 3rd version Core */ -#define CPU_ID_PXA210B 0x69052920 /* 3rd version Core */ -#define CPU_ID_PXA250C 0x69052d00 /* 4th version Core */ -#define CPU_ID_PXA210C 0x69052d20 /* 4th version Core */ -#define CPU_ID_80321_400 0x69052420 -#define CPU_ID_80321_600 0x69052430 -#define CPU_ID_80321_400_B0 0x69052c20 -#define CPU_ID_80321_600_B0 0x69052c30 -#define CPU_ID_IXP425_533 0x690541c0 -#define CPU_ID_IXP425_400 0x690541d0 -#define CPU_ID_IXP425_266 0x690541f0 - -/* ARM3-specific coprocessor 15 registers */ -#define ARM3_CP15_FLUSH 1 -#define ARM3_CP15_CONTROL 2 -#define ARM3_CP15_CACHEABLE 3 -#define ARM3_CP15_UPDATEABLE 4 -#define ARM3_CP15_DISRUPTIVE 5 - -/* ARM3 Control register bits */ -#define ARM3_CTL_CACHE_ON 0x00000001 -#define ARM3_CTL_SHARED 0x00000002 -#define ARM3_CTL_MONITOR 0x00000004 - -/* - * Post-ARM3 CP15 registers: - * - * 1 Control register - * - * 2 Translation Table Base - * - * 3 Domain Access Control - * - * 4 Reserved - * - * 5 Fault Status - * - * 6 Fault Address - * - * 7 Cache/write-buffer Control - * - * 8 TLB Control - * - * 9 Cache Lockdown - * - * 10 TLB Lockdown - * - * 11 Reserved - * - * 12 Reserved - * - * 13 Process ID (for FCSE) - * - * 14 Reserved - * - * 15 Implementation Dependent - */ - -/* Some of the definitions below need cleaning up for V3/V4 architectures */ - -/* CPU control register (CP15 register 1) */ -#define CPU_CONTROL_MMU_ENABLE 0x00000001 /* M: MMU/Protection unit enable */ -#define CPU_CONTROL_AFLT_ENABLE 0x00000002 /* A: Alignment fault enable */ -#define CPU_CONTROL_DC_ENABLE 0x00000004 /* C: IDC/DC enable */ -#define CPU_CONTROL_WBUF_ENABLE 0x00000008 /* W: Write buffer enable */ -#define CPU_CONTROL_32BP_ENABLE 0x00000010 /* P: 32-bit exception handlers */ -#define CPU_CONTROL_32BD_ENABLE 0x00000020 /* D: 32-bit addressing */ -#define CPU_CONTROL_LABT_ENABLE 0x00000040 /* L: Late abort enable */ -#define CPU_CONTROL_BEND_ENABLE 0x00000080 /* B: Big-endian mode */ -#define CPU_CONTROL_SYST_ENABLE 0x00000100 /* S: System protection bit */ -#define CPU_CONTROL_ROM_ENABLE 0x00000200 /* R: ROM protection bit */ -#define CPU_CONTROL_CPCLK 0x00000400 /* F: Implementation defined */ -#define CPU_CONTROL_BPRD_ENABLE 0x00000800 /* Z: Branch prediction enable */ -#define CPU_CONTROL_IC_ENABLE 0x00001000 /* I: IC enable */ -#define CPU_CONTROL_VECRELOC 0x00002000 /* V: Vector relocation */ -#define CPU_CONTROL_ROUNDROBIN 0x00004000 /* RR: Predictable replacement */ -#define CPU_CONTROL_V4COMPAT 0x00008000 /* L4: ARMv4 compat LDR R15 etc */ - -#define CPU_CONTROL_IDC_ENABLE CPU_CONTROL_DC_ENABLE - -/* XScale Auxillary Control Register (CP15 register 1, opcode2 1) */ -#define XSCALE_AUXCTL_K 0x00000001 /* dis. write buffer coalescing */ -#define XSCALE_AUXCTL_P 0x00000002 /* ECC protect page table access */ -#define XSCALE_AUXCTL_MD_WB_RA 0x00000000 /* mini-D$ wb, read-allocate */ -#define XSCALE_AUXCTL_MD_WB_RWA 0x00000010 /* mini-D$ wb, read/write-allocate */ -#define XSCALE_AUXCTL_MD_WT 0x00000020 /* mini-D$ wt, read-allocate */ -#define XSCALE_AUXCTL_MD_MASK 0x00000030 - -/* Cache type register definitions */ -#define CPU_CT_ISIZE(x) ((x) & 0xfff) /* I$ info */ -#define CPU_CT_DSIZE(x) (((x) >> 12) & 0xfff) /* D$ info */ -#define CPU_CT_S (1U << 24) /* split cache */ -#define CPU_CT_CTYPE(x) (((x) >> 25) & 0xf) /* cache type */ - -#define CPU_CT_CTYPE_WT 0 /* write-through */ -#define CPU_CT_CTYPE_WB1 1 /* write-back, clean w/ read */ -#define CPU_CT_CTYPE_WB2 2 /* w/b, clean w/ cp15,7 */ -#define CPU_CT_CTYPE_WB6 6 /* w/b, cp15,7, lockdown fmt A */ -#define CPU_CT_CTYPE_WB7 7 /* w/b, cp15,7, lockdown fmt B */ - -#define CPU_CT_xSIZE_LEN(x) ((x) & 0x3) /* line size */ -#define CPU_CT_xSIZE_M (1U << 2) /* multiplier */ -#define CPU_CT_xSIZE_ASSOC(x) (((x) >> 3) & 0x7) /* associativity */ -#define CPU_CT_xSIZE_SIZE(x) (((x) >> 6) & 0x7) /* size */ - -/* Fault status register definitions */ - -#define FAULT_TYPE_MASK 0x0f -#define FAULT_USER 0x10 - -#define FAULT_WRTBUF_0 0x00 /* Vector Exception */ -#define FAULT_WRTBUF_1 0x02 /* Terminal Exception */ -#define FAULT_BUSERR_0 0x04 /* External Abort on Linefetch -- Section */ -#define FAULT_BUSERR_1 0x06 /* External Abort on Linefetch -- Page */ -#define FAULT_BUSERR_2 0x08 /* External Abort on Non-linefetch -- Section */ -#define FAULT_BUSERR_3 0x0a /* External Abort on Non-linefetch -- Page */ -#define FAULT_BUSTRNL1 0x0c /* External abort on Translation -- Level 1 */ -#define FAULT_BUSTRNL2 0x0e /* External abort on Translation -- Level 2 */ -#define FAULT_ALIGN_0 0x01 /* Alignment */ -#define FAULT_ALIGN_1 0x03 /* Alignment */ -#define FAULT_TRANS_S 0x05 /* Translation -- Section */ -#define FAULT_TRANS_P 0x07 /* Translation -- Page */ -#define FAULT_DOMAIN_S 0x09 /* Domain -- Section */ -#define FAULT_DOMAIN_P 0x0b /* Domain -- Page */ -#define FAULT_PERM_S 0x0d /* Permission -- Section */ -#define FAULT_PERM_P 0x0f /* Permission -- Page */ - -#define FAULT_IMPRECISE 0x400 /* Imprecise exception (XSCALE) */ - -/* - * Address of the vector page, low and high versions. - */ -#define ARM_VECTORS_LOW 0x00000000U -#define ARM_VECTORS_HIGH 0xffff0000U - -/* - * ARM Instructions - * - * 3 3 2 2 2 - * 1 0 9 8 7 0 - * +-------+-------------------------------------------------------+ - * | cond | instruction dependant | - * |c c c c| | - * +-------+-------------------------------------------------------+ - */ - -#define INSN_SIZE 4 /* Always 4 bytes */ -#define INSN_COND_MASK 0xf0000000 /* Condition mask */ -#define INSN_COND_AL 0xe0000000 /* Always condition */ - -#endif /* !MACHINE_ARMREG_H */ diff --git a/libacc/tests/data/addressOf.c b/libacc/tests/data/addressOf.c deleted file mode 100644 index e7acde53df1..00000000000 --- a/libacc/tests/data/addressOf.c +++ /dev/null @@ -1,31 +0,0 @@ -void testStruct() { - struct str { - float x; - float y; - }; - - struct str base; - int index = 0; - - base.x = 10.0; - struct str *s = &base; - - float *v = &(*s).x; - float *v2 = &s[index].x; - printf("testStruct: %g %g %g\n",base.x, *v, *v2); -} - -void testArray() { - int a[2]; - a[0] = 1; - a[1] = 2; - int* p = &a[0]; - int* p2 = a; - printf("testArray: %d %d %d\n", a[0], *p, *p2); -} - -int main() { - testStruct(); - testArray(); - return 0; -} diff --git a/libacc/tests/data/array.c b/libacc/tests/data/array.c deleted file mode 100644 index ca4a7283c1d..00000000000 --- a/libacc/tests/data/array.c +++ /dev/null @@ -1,107 +0,0 @@ -// Array allocation tests - -void testLocalInt() -{ - int a[3]; - a[0] = 1; - a[1] = 2; - a[2] = a[0] + a[1]; - printf("localInt: %d\n", a[2]); -} - -char a[3]; -double d[3]; - -void testGlobalChar() -{ - a[0] = 1; - a[1] = 2; - a[2] = a[0] + a[1]; - printf("globalChar: %d\n", a[2]); -} - -void testGlobalDouble() -{ - d[0] = 1; - d[1] = 2; - d[2] = d[0] + d[1]; - printf("globalDouble: %g\n", d[2]); -} - -void testLocalDouble() -{ - double d[3]; - float m[12]; - m[0] = 1.0f; - m[1] = 2.0f; - d[0] = 1.0; - d[1] = 2.0; - d[2] = d[0] + d[1]; - m[2] = m[0] + m[1]; - printf("localDouble: %g %g\n", d[2], m[2]); -} - -void vectorAdd(int* a, int* b, float* c, int len) { - int i; - for(i = 0; i < len; i++) { - c[i] = a[i] + b[i]; - } -} - -void testArgs() { - int a[3], b[3]; - float c[3]; - int i; - int len = 3; - for(i = 0; i < len; i++) { - a[i] = i; - b[i] = i; - c[i] = 0; - } - vectorAdd(a,b,c, len); - printf("testArgs:"); - for(i = 0; i < len; i++) { - printf(" %g", c[i]); - } - printf("\n"); -} - -void testDecay() { - char c[4]; - c[0] = 'H'; - c[1] = 'i'; - c[2] = '!'; - c[3] = 0; - printf("testDecay: %s\n", c); -} - -void test2D() { - char c[10][20]; - int x; - int y; - printf("test2D:\n"); - for(y = 0; y < 10; y++) { - for(x = 0; x < 20; x++) { - c[y][x] = 'a' + (15 & (y * 19 + x)); - } - } - for(y = 0; y < 10; y++) { - for(x = 0; x < 20; x++) { - printf("%c", c[y][x]); - } - printf("\n"); - } - -} - -int main() -{ - testLocalInt(); - testLocalDouble(); - testGlobalChar(); - testGlobalDouble(); - testArgs(); - testDecay(); - test2D(); - return 0; -} diff --git a/libacc/tests/data/assignment.c b/libacc/tests/data/assignment.c deleted file mode 100644 index 4fc7801d0a6..00000000000 --- a/libacc/tests/data/assignment.c +++ /dev/null @@ -1,9 +0,0 @@ -int main() { - int a = 0; - int b = 1; - a = b = 2; // Test that "b = 2" generates an rvalue. - if (a = 7) { // Test that a = 7 generates an rvalue. - b = 3; - } - return a; -} diff --git a/libacc/tests/data/assignmentop.c b/libacc/tests/data/assignmentop.c deleted file mode 100644 index 649edf9b6f3..00000000000 --- a/libacc/tests/data/assignmentop.c +++ /dev/null @@ -1,62 +0,0 @@ -// Test assignment operations - -void testAssignment() { - int a = 2; - a *= 5; - printf("2 *= 5 %d\n", a); - a = 20; - a /= 5; - printf("20 /= 5 %d\n", a); - a = 17; - a %= 5; - printf("17 %%= 5 %d\n", a); - a = 17; - a += 5; - printf("17 += 5 %d\n", a); - a = 17; - a-=5; - printf("17 -= 5 %d\n", a); - a = 17; - a<<=1; - printf("17<<= 1 %d\n", a); - a = 17; - a>>=1; - printf("17>>= 1 %d\n", a); - a = 17; - a&=1; - printf("17&= 1 %d\n", a); - a = 17; - a^=1; - printf("17^= 1 %d\n", a); - a = 16; - a^=1; - printf("16|= 1 %d\n", a); -} - -int a; - -int* f() { - printf("f()\n"); - return &a; -} - -void testEval() { - a = 0; - printf("*f() = *f() + 10;\n"); - *f() = *f() + 10; - printf("a = %d\n", a); -} - -void testOpEval() { - a = 0; - printf("*f() += 10;\n"); - *f() += 10; - printf("a = %d\n", a); -} - -int main() { - testAssignment(); - testEval(); - testOpEval(); - return 0; -} diff --git a/libacc/tests/data/b2071670.c b/libacc/tests/data/b2071670.c deleted file mode 100644 index 311bc4f49c2..00000000000 --- a/libacc/tests/data/b2071670.c +++ /dev/null @@ -1,9 +0,0 @@ -// See http://b/2071670 - -int main() { - float f = 10.0f; - float* floatPointer = &f; - // The following line used to incorrectly error: "Incompatible pointer or array types" - int* buffer = (int*) floatPointer; - return *buffer; -} diff --git a/libacc/tests/data/bellard.otccex.c b/libacc/tests/data/bellard.otccex.c deleted file mode 100644 index e8f098914da..00000000000 --- a/libacc/tests/data/bellard.otccex.c +++ /dev/null @@ -1,126 +0,0 @@ -/* #!/usr/local/bin/otcc */ -/* - * Sample OTCC C example. You can uncomment the first line and install - * otcc in /usr/local/bin to make otcc scripts ! - */ - -/* Any preprocessor directive except #define are ignored. We put this - include so that a standard C compiler can compile this code too. */ -#include - -/* defines are handled, but macro arguments cannot be given. No - recursive defines are tolerated */ -#define DEFAULT_BASE 10 - -/* - * Only old style K&R prototypes are parsed. Only int arguments are - * allowed (implicit types). - * - * By benchmarking the execution time of this function (for example - * for fib(35)), you'll notice that OTCC is quite fast because it - * generates native i386 machine code. - */ -fib(n) -{ - if (n <= 2) - return 1; - else - return fib(n-1) + fib(n-2); -} - -/* Identifiers are parsed the same way as C: begins with letter or - '_', and then letters, '_' or digits */ -fact(n) -{ - /* local variables can be declared. Only 'int' type is supported */ - int i, r; - r = 1; - /* 'while' and 'for' loops are supported */ - for(i=2;i<=n;i++) - r = r * i; - return r; -} - -/* Well, we could use printf, but it would be too easy */ -print_num(n, b) -{ - int tab, p, c; - /* Numbers can be entered in decimal, hexadecimal ('0x' prefix) and - octal ('0' prefix) */ - /* more complex programs use malloc */ - tab = malloc(0x100); - p = tab; - while (1) { - c = n % b; - /* Character constants can be used */ - if (c >= 10) - c = c + 'a' - 10; - else - c = c + '0'; - *(char *)p = c; - p++; - n = n / b; - /* 'break' is supported */ - if (n == 0) - break; - } - while (p != tab) { - p--; - printf("%c", *(char *)p); - } - free(tab); -} - -/* 'main' takes standard 'argc' and 'argv' parameters */ -main(argc, argv) -{ - /* no local name space is supported, but local variables ARE - supported. As long as you do not use a globally defined - variable name as local variable (which is a bad habbit), you - won't have any problem */ - int s, n, f, base; - - /* && and || operator have the same semantics as C (left to right - evaluation and early exit) */ - if (argc != 2 && argc != 3) { - /* '*' operator is supported with explicit casting to 'int *', - 'char *' or 'int (*)()' (function pointer). Of course, 'int' - are supposed to be used as pointers too. */ - s = *(int *)argv; - help(s); - return 1; - } - /* Any libc function can be used because OTCC uses dynamic linking */ - n = atoi(*(int *)(argv + 4)); - base = DEFAULT_BASE; - if (argc >= 3) { - base = atoi(*(int *)(argv + 8)); - if (base < 2 || base > 36) { - /* external variables can be used too (here: 'stderr') */ - fprintf(stderr, "Invalid base\n"); - return 1; - } - } - printf("fib(%d) = ", n); - print_num(fib(n), base); - printf("\n"); - - printf("fact(%d) = ", n); - if (n > 12) { - printf("Overflow"); - } else { - /* why not using a function pointer ? */ - f = &fact; - print_num((*(int (*)())f)(n), base); - } - printf("\n"); - return 0; -} - -/* functions can be used before being defined */ -help(name) -{ - printf("usage: %s n [base]\n", name); - printf("Compute fib(n) and fact(n) and output the result in base 'base'\n"); -} - diff --git a/libacc/tests/data/brackets.c b/libacc/tests/data/brackets.c deleted file mode 100644 index bab88a2fdc6..00000000000 --- a/libacc/tests/data/brackets.c +++ /dev/null @@ -1,61 +0,0 @@ -void testBrackets(int* ar, int len) { - int i; - int errors = 0; - for (i = 0; i < len; i++) { - ar[i] = i; - } - for (i = 0; i < len; i++) { - if (ar[i] != i) { - printf("error: [%d] %d != %d\n", i, ar[i], i); - errors++; - } - } - printf("Errors: %d\n", errors); -} - -void testBrackets2D(int** ar2D, int lenX, int lenY) { - int x, y; - int errors = 0; - for (x = 0; x < lenX; x++) { - for (y = 0; y < lenY; y++) { - ar2D[x][y] = x * lenY + y; - } - } - for (x = 0; x < lenX; x++) { - for (y = 0; y < lenY; y++) { - int expected = x * lenY + y; - int val = ar2D[x][y]; - if (val != expected) { - printf("error: [%d][%d] %d != %d\n", x, y, val, expected); - errors++; - } - } - } - printf("2D Errors: %d\n", errors); -} - -void testHeap() { - int* ar = (int*) malloc(100); - testBrackets(ar, 25); - free(ar); -} - -void testHeap2D() { - int lenX = 10; - int lenY = 5; - int* ar = (int*) malloc(lenX * lenY * 4); - int** ar2D = (int**) malloc(lenX * 4); - int i; - for(i = 0; i < lenX; i++) { - ar2D[i] = ar + lenY * i; - } - testBrackets2D(ar2D, lenX, lenY); - free(ar); - free(ar2D); -} - -int main() { - testHeap(); - testHeap2D(); - return 0; -} diff --git a/libacc/tests/data/casts.c b/libacc/tests/data/casts.c deleted file mode 100644 index d3a49b4cf3f..00000000000 --- a/libacc/tests/data/casts.c +++ /dev/null @@ -1,85 +0,0 @@ -void test1() { - int a = 3; - int* pb = &a; - int c = *pb; - printf("Reading from a pointer: %d %d\n", a, c); - *pb = 4; - printf("Writing to a pointer: %d\n", a); - printf("Testing casts: %d %g %g %d\n", 3, (float) 3, 4.5, (int) 4.5); -} - -void test2() { - int x = 4; - int px = &x; - // int z = * px; // An error, expected a pointer type - int y = * (int*) px; - printf("Testing reading (int*): %d\n", y); -} - -void test3() { - int px = (int) malloc(120); - * (int*) px = 8; - * (int*) (px + 4) = 9; - printf("Testing writing (int*): %d %d\n", * (int*) px, * (int*) (px + 4)); - free((void*) px); -} - -void test4() { - int x = 0x12345678; - int px = &x; - int a = * (char*) px; - int b = * (char*) (px + 1); - int c = * (char*) (px + 2); - int d = * (char*) (px + 3); - printf("Testing reading (char*): 0x%02x 0x%02x 0x%02x 0x%02x\n", a, b, c, d); -} - -void test5() { - int x = 0xFFFFFFFF; - int px = &x; - * (char*) px = 0x21; - * (char*) (px + 1) = 0x43; - * (char*) (px + 2) = 0x65; - * (char*) (px + 3) = 0x87; - printf("Testing writing (char*): 0x%08x\n", x); -} - -int f(int b) { - printf("f(%d)\n", b); - return 7 * b; -} - -void test6() { - int fp = &f; - int x = (*(int(*)()) fp)(10); - printf("Function pointer result: %d\n", x); -} - -void test7() { - int px = (int) malloc(120); - * (float*) px = 8.8f; - * (float*) (px + 4) = 9.9f; - printf("Testing read/write (float*): %g %g\n", * (float*) px, * (float*) (px + 4)); - free((void*) px); -} - -void test8() { - int px = (int) malloc(120); - * (double*) px = 8.8; - * (double*) (px + 8) = 9.9; - printf("Testing read/write (double*): %g %g\n", * (double*) px, * (double*) (px + 8)); - free((void*) px); -} - - -int main() { - test1(); - test2(); - test3(); - test4(); - test5(); - test6(); - test7(); - test8(); - return 0; -} diff --git a/libacc/tests/data/char.c b/libacc/tests/data/char.c deleted file mode 100644 index 8c63ba28550..00000000000 --- a/libacc/tests/data/char.c +++ /dev/null @@ -1,13 +0,0 @@ -char ga; -char gb; - -int main() { - char a = 'c'; - char b = a * 3; - printf("a = %d, b = %d\n", a, b); - ga = 'd'; - gb = ga * 3; - printf("ga = %d, gb = %d\n", ga, gb); - return 0; -} - diff --git a/libacc/tests/data/comma.c b/libacc/tests/data/comma.c deleted file mode 100644 index 496944ca8ff..00000000000 --- a/libacc/tests/data/comma.c +++ /dev/null @@ -1,35 +0,0 @@ -int testReturn() { - return 10, 20, 30; -} - -int testArg(int a) { - return a; -} - -void testComma() { - int a; - 0, a = 10,20; - printf("statement: %d\n", a); - a = 1; - if (a = 0, 1) { - printf("if: a = %d\n", a); - } - int b = 0; - a = 10; - while(b++,a--) {} - printf("while: b = %d\n", b); - b = 0; - for(b++,a = 0;b++, a < 10; b++, a++) {} - printf("for: b = %d\n", b); - b = testReturn(); - printf("return: %d\n", b); - b = testArg((a,12)); - printf("arg: %d\n", b); -} - - - -int main() { - testComma(); - return 0; -} diff --git a/libacc/tests/data/constants.c b/libacc/tests/data/constants.c deleted file mode 100644 index 230109a4ebf..00000000000 --- a/libacc/tests/data/constants.c +++ /dev/null @@ -1,30 +0,0 @@ -#define FOO 0x10 - -int main() { - printf("0 = %d\n", 0); - printf("010 = %d\n", 010); - printf("0x10 = %d\n", FOO); - printf("'\\a' = %d\n", '\a'); - printf("'\\b' = %d\n", '\b'); - printf("'\\f' = %d\n", '\f'); - printf("'\\n' = %d\n", '\n'); - printf("'\\r' = %d\n", '\r'); - printf("'\\t' = %d\n", '\t'); - printf("'\\v' = %d\n", '\v'); - // Undefined - // printf("'\\z' = %d\n", '\z'); - printf("'\\\\' = %d\n", '\\'); - printf("'\\'' = %d\n", '\''); - printf("'\\\"' = %d\n", '\"'); - printf("'\\?' = %d\n", '\?'); - printf("'\\0' = %d\n", '\0'); - printf("'\\1' = %d\n", '\1'); - printf("'\\12' = %d\n", '\12'); - printf("'\\123' = %d\n", '\123'); - printf("'\\x0' = %d\n", '\x0'); - printf("'\\x1' = %d\n", '\x1'); - printf("'\\x12' = %d\n", '\x12'); - printf("'\\x123' = %d\n", '\x123'); - printf("'\\x1f' = %d\n", '\x1f'); - printf("'\\x1F' = %d\n", '\x1F'); -} diff --git a/libacc/tests/data/defines.c b/libacc/tests/data/defines.c deleted file mode 100644 index 6cb6f7ebf67..00000000000 --- a/libacc/tests/data/defines.c +++ /dev/null @@ -1,9 +0,0 @@ -// Simple tests of the C preprocessor - -#define A 1 -#define A (4 / 2) -#define B 1 // This is a comment. With a / in it. - -int main() { - return A + B; -} diff --git a/libacc/tests/data/double.c b/libacc/tests/data/double.c deleted file mode 100644 index 5bc20a399a4..00000000000 --- a/libacc/tests/data/double.c +++ /dev/null @@ -1,7 +0,0 @@ -double atof(char *nptr); - -int main() { - printf("Value = %g\n", atof("10.42")); - return 0; -} - diff --git a/libacc/tests/data/error.c b/libacc/tests/data/error.c deleted file mode 100644 index 2e08dcc2119..00000000000 --- a/libacc/tests/data/error.c +++ /dev/null @@ -1,2 +0,0 @@ -void foo; - diff --git a/libacc/tests/data/expr-ansi.c b/libacc/tests/data/expr-ansi.c deleted file mode 100644 index d463659bda6..00000000000 --- a/libacc/tests/data/expr-ansi.c +++ /dev/null @@ -1,60 +0,0 @@ -/* Test operators */ - -void testInc() { int a, b; a = 3; b = a++; printf("3++ = %d %d\n", b, a); } -void testDec() { int a, b; a = 3; b = a--; printf("3-- = %d %d\n", b, a); } -void testTimes(){ printf("%d * %d = %d\n", 10, 4, 10 * 4); } -void testDiv(){ printf("%d / %d = %d\n", 11, 4, 11 / 4); } -void testMod(){ printf("%d %% %d = %d\n", 11, 4, 11 % 4); } -void testPlus(){ printf("%d + %d = %d\n", 10, 4, 10 + 4); } -void testMinus(){ printf("%d - %d = %d\n", 10, 4, 10 - 4); } -void testShiftLeft(){ printf("%d << %d = %d\n", 10, 4, 10 << 4); } -void testShiftRight(){ printf("%d >> %d = %d\n", 100, 4, 100 >> 4); } -void testLess(){ printf("%d < %d = %d\n", 10, 4, 10 < 4); } -void testLesEqual(){ printf("%d <= %d = %d\n", 10, 4, 10 <= 4); } -void testGreater(){ printf("%d > %d = %d\n", 10, 4, 10 > 4); } -void testGreaterEqual(){ printf("%d >= %d = %d\n", 10, 4, 10 >= 4); } -void testEqualTo(){ printf("%d == %d = %d\n", 10, 4, 10 == 4); } -void testNotEqualTo(){ printf("%d != %d = %d\n", 10, 4, 10 != 4); } -void testBitAnd(){ printf("%d & %d = %d\n", 10, 7, 10 & 7); } -void testBitXor(){ printf("%d ^ %d = %d\n", 10, 7, 10 ^ 7); } -void testBitOr(){ printf("%d | %d = %d\n", 10, 4, 10 | 4); } -void testAssignment(){ int a, b; a = 3; b = a; printf("b == %d\n", b); } -void testLogicalAnd(){ printf("%d && %d = %d\n", 10, 4, 10 && 4); } -void testLogicalOr(){ printf("%d || %d = %d\n", 10, 4, 10 || 4); } -void testAddressOf(){ int a; printf("&a is %d\n", &a); } -void testPointerIndirection(){ int a, b; a = &b; b = 17; printf("*%d = %d =?= %d\n", a, * (int*) a, b); } -void testNegation(){ printf("-%d = %d\n", 10, -10); } -void testUnaryPlus(){ printf("+%d = %d\n", 10, +10); } -void testUnaryNot(){ printf("!%d = %d\n", 10, !10); } -void testBitNot(){ printf("~%d = %d\n", 10, ~10); } - -int main(int a, char** b) { - testInc(); - testDec(); - testTimes(); - testDiv(); - testMod(); - testPlus(); - testMinus(); - testShiftLeft(); - testShiftRight(); - testLess(); - testLesEqual(); - testGreater(); - testGreaterEqual(); - testEqualTo(); - testNotEqualTo(); - testBitAnd(); - testBinXor(); - testBitOr(); - testAssignment(); - testLogicalAnd(); - testLogicalOr(); - testAddressOf(); - testPointerIndirection(); - testNegation(); - testUnaryPlus(); - testUnaryNot(); - testBitNot(); - return 0; -} diff --git a/libacc/tests/data/expr.c b/libacc/tests/data/expr.c deleted file mode 100644 index 4f2d2e78f4b..00000000000 --- a/libacc/tests/data/expr.c +++ /dev/null @@ -1,60 +0,0 @@ -/* Test operators */ - -testInc() { int a, b; a = 3; b = a++; printf("3++ = %d %d\n", b, a); } -testDec() { int a, b; a = 3; b = a--; printf("3-- = %d %d\n", b, a); } -testTimes(){ printf("%d * %d = %d\n", 10, 4, 10 * 4); } -testDiv(){ printf("%d / %d = %d\n", 11, 4, 11 / 4); } -testMod(){ printf("%d %% %d = %d\n", 11, 4, 11 % 4); } -testPlus(){ printf("%d + %d = %d\n", 10, 4, 10 + 4); } -testMinus(){ printf("%d - %d = %d\n", 10, 4, 10 - 4); } -testShiftLeft(){ printf("%d << %d = %d\n", 10, 4, 10 << 4); } -testShiftRight(){ printf("%d >> %d = %d\n", 100, 4, 100 >> 4); } -testLess(){ printf("%d < %d = %d\n", 10, 4, 10 < 4); } -testLesEqual(){ printf("%d <= %d = %d\n", 10, 4, 10 <= 4); } -testGreater(){ printf("%d > %d = %d\n", 10, 4, 10 > 4); } -testGreaterEqual(){ printf("%d >= %d = %d\n", 10, 4, 10 >= 4); } -testEqualTo(){ printf("%d == %d = %d\n", 10, 4, 10 == 4); } -testNotEqualTo(){ printf("%d != %d = %d\n", 10, 4, 10 != 4); } -testBitAnd(){ printf("%d & %d = %d\n", 10, 7, 10 & 7); } -testBitXor(){ printf("%d ^ %d = %d\n", 10, 7, 10 ^ 7); } -testBitOr(){ printf("%d | %d = %d\n", 10, 4, 10 | 4); } -testAssignment(){ int a, b; a = 3; b = a; printf("b == %d\n", b); } -testLogicalAnd(){ printf("%d && %d = %d\n", 10, 4, 10 && 4); } -testLogicalOr(){ printf("%d || %d = %d\n", 10, 4, 10 || 4); } -testAddressOf(){ int a; printf("&a is %d\n", &a); } -testPointerIndirection(){ int a, b; a = &b; b = 17; printf("*%d = %d =?= %d\n", a, * (int*) a, b); } -testNegation(){ printf("-%d = %d\n", 10, -10); } -testUnaryPlus(){ printf("+%d = %d\n", 10, +10); } -testUnaryNot(){ printf("!%d = %d\n", 10, !10); } -testBitNot(){ printf("~%d = %d\n", 10, ~10); } - -main(a,b) { - testInc(); - testDec(); - testTimes(); - testDiv(); - testMod(); - testPlus(); - testMinus(); - testShiftLeft(); - testShiftRight(); - testLess(); - testLesEqual(); - testGreater(); - testGreaterEqual(); - testEqualTo(); - testNotEqualTo(); - testBitAnd(); - testBinXor(); - testBitOr(); - testAssignment(); - testLogicalAnd(); - testLogicalOr(); - testAddressOf(); - testPointerIndirection(); - testNegation(); - testUnaryPlus(); - testUnaryNot(); - testBitNot(); - return 0; -} \ No newline at end of file diff --git a/libacc/tests/data/expr2.c b/libacc/tests/data/expr2.c deleted file mode 100644 index 04b6a38d3d2..00000000000 --- a/libacc/tests/data/expr2.c +++ /dev/null @@ -1,6 +0,0 @@ -/* Test operators */ - -main() { - int a; - a = a++; -} diff --git a/libacc/tests/data/film.c b/libacc/tests/data/film.c deleted file mode 100644 index 00c2d3604a5..00000000000 --- a/libacc/tests/data/film.c +++ /dev/null @@ -1,53 +0,0 @@ -// Test logical and bitwise AND and OR - -int test(int x, int y) { - int v = x || y; - return v; -} - -int test2(int x, int y) { - if(x | y) { - return 1; - } else { - return 0; - } -} - -int test3(int x, int y) { - int v = x && y; - return v; -} - -int test4(int x, int y) { - if(x & y) { - return 1; - } else { - return 0; - } -} - -int main(int index) -{ - int x,y; - printf("testing...\n"); - int totalBad = 0; - for(y = 0; y < 2; y++) { - for(x = 0; x < 2; x++) { - int a = test(x,y); - int b = test2(x,y); - if (a != b) { - printf("Results differ: OR x=%d y=%d a=%d b=%d\n", x, y, a, b); - totalBad++; - } - a = test3(x,y); - b = test4(x,y); - if (a != b) { - printf("Results differ: AND x=%d y=%d a=%d b=%d\n", x, y, a, b); - totalBad++; - } - } - } - printf("Total bad: %d\n", totalBad); - return 0; -} - diff --git a/libacc/tests/data/float.c b/libacc/tests/data/float.c deleted file mode 100644 index f48b3d1ecb7..00000000000 --- a/libacc/tests/data/float.c +++ /dev/null @@ -1,57 +0,0 @@ -int ftoi(float f) { - return f; -} - -int dtoi(double d) { - return d; -} - -float itof(int i) { - return i; -} - -double itod(int i) { - return i; -} - -float f0, f1; -double d0, d1; - -void testParseConsts() { - printf("Constants: %g %g %g %g %g %g %g %g %g\n", 0e1, 0E1, 0.f, .01f, - .01e0f, 1.0e-1, 1.0e1, 1.0e+1, - .1f); -} -void testVars(float arg0, float arg1, double arg2, double arg3) { - float local0, local1; - double local2, local3; - f0 = arg0; - f1 = arg1; - d0 = arg2; - d1 = arg3; - local0 = arg0; - local1 = arg1; - local2 = arg2; - local3 = arg3; - printf("globals: %g %g %g %g\n", f0, f1, d0, d1); - printf("args: %g %g %g %g\n", arg0, arg1, arg2, arg3); - printf("locals: %g %g %g %g\n", local0, local1, local2, local3); - - - printf("cast rval: %g %g\n", * (float*) & f1, * (double*) & d1); - - * (float*) & f0 = 1.1f; - * (double*) & d0 = 3.3; - printf("cast lval: %g %g %g %g\n", f0, f1, d0, d1); -} - -int main() { - testParseConsts(); - printf("int: %d float: %g double: %g\n", 1, 2.2f, 3.3); - printf(" ftoi(1.4f)=%d\n", ftoi(1.4f)); - printf(" dtoi(2.4)=%d\n", dtoi(2.4)); - printf(" itof(3)=%g\n", itof(3)); - printf(" itod(4)=%g\n", itod(4)); - testVars(1.0f, 2.0f, 3.0, 4.0); - return 0; -} diff --git a/libacc/tests/data/floatdouble.c b/libacc/tests/data/floatdouble.c deleted file mode 100644 index 264c641b081..00000000000 --- a/libacc/tests/data/floatdouble.c +++ /dev/null @@ -1,9 +0,0 @@ -int main() -{ - // Test coercing values when storing. - float a = 0.002; - double b = 0.1f; - int c = 10.002; - printf("%g %g %d\n", a, b, c); - return 0; -} diff --git a/libacc/tests/data/flops.c b/libacc/tests/data/flops.c deleted file mode 100644 index 40b1b283ef0..00000000000 --- a/libacc/tests/data/flops.c +++ /dev/null @@ -1,158 +0,0 @@ -// Test floating point operations. - -void unaryOps() { - // Unary ops - printf("-%g = %g\n", 1.1, -1.1); - printf("!%g = %d\n", 1.2, !1.2); - printf("!%g = %d\n", 0.0, !0.0); -} - -void binaryOps() { - printf("double op double:\n"); - printf("%g + %g = %g\n", 1.0, 2.0, 1.0 + 2.0); - printf("%g - %g = %g\n", 1.0, 2.0, 1.0 - 2.0); - printf("%g * %g = %g\n", 1.0, 2.0, 1.0 * 2.0); - printf("%g / %g = %g\n", 1.0, 2.0, 1.0 / 2.0); - - printf("float op float:\n"); - printf("%g + %g = %g\n", 1.0f, 2.0f, 1.0f + 2.0f); - printf("%g - %g = %g\n", 1.0f, 2.0f, 1.0f - 2.0f); - printf("%g * %g = %g\n", 1.0f, 2.0f, 1.0f * 2.0f); - printf("%g / %g = %g\n", 1.0f, 2.0f, 1.0f / 2.0f); - - printf("double op float:\n"); - printf("%g + %g = %g\n", 1.0, 2.0f, 1.0 + 2.0f); - printf("%g - %g = %g\n", 1.0, 2.0f, 1.0 - 2.0f); - printf("%g * %g = %g\n", 1.0, 2.0f, 1.0 * 2.0f); - printf("%g / %g = %g\n", 1.0, 2.0f, 1.0 / 2.0f); - - printf("double op int:\n"); - printf("%g + %d = %g\n", 1.0, 2, 1.0 + 2); - printf("%g - %d = %g\n", 1.0, 2, 1.0 - 2); - printf("%g * %d = %g\n", 1.0, 2, 1.0 * 2); - printf("%g / %d = %g\n", 1.0, 2, 1.0 / 2); - - printf("int op double:\n"); - printf("%d + %g = %g\n", 1, 2.0, 1 + 2.0); - printf("%d - %g = %g\n", 1, 2.0, 1 - 2.0); - printf("%d * %g = %g\n", 1, 2.0, 1 * 2.0); - printf("%d / %g = %g\n", 1, 2.0, 1 / 2.0); -} - -void comparisonTestdd(double a, double b) { - printf("%g op %g: < %d <= %d == %d >= %d > %d != %d\n", - a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); -} - -void comparisonOpsdd() { - printf("double op double:\n"); - comparisonTestdd(1.0, 2.0); - comparisonTestdd(1.0, 1.0); - comparisonTestdd(2.0, 1.0); -} - - -void comparisonTestdf(double a, float b) { - printf("%g op %g: < %d <= %d == %d >= %d > %d != %d\n", - a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); -} - -void comparisonOpsdf() { - printf("double op float:\n"); - comparisonTestdf(1.0, 2.0f); - comparisonTestdf(1.0, 1.0f); - comparisonTestdf(2.0, 1.0f); -} - -void comparisonTestff(float a, float b) { - printf("%g op %g: < %d <= %d == %d >= %d > %d != %d\n", - a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); -} - -void comparisonOpsff() { - printf("float op float:\n"); - comparisonTestff(1.0f, 2.0f); - comparisonTestff(1.0f, 1.0f); - comparisonTestff(2.0f, 1.0f); -} - -void comparisonTestid(int a, double b) { - printf("%d op %g: < %d <= %d == %d >= %d > %d != %d\n", - a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); -} - -void comparisonOpsid() { - printf("int op double:\n"); - comparisonTestid(1, 2.0); - comparisonTestid(1, 1.0); - comparisonTestid(2, 1.0); -} -void comparisonTestdi(double a, int b) { - printf("%g op %d: < %d <= %d == %d >= %d > %d != %d\n", - a, b, a < b, a <= b, a == b, a >= b, a > b, a != b); -} - -void comparisonOpsdi() { - printf("double op int:\n"); - comparisonTestdi(1.0f, 2); - comparisonTestdi(1.0f, 1); - comparisonTestdi(2.0f, 1); -} - -void comparisonOps() { - comparisonOpsdd(); - comparisonOpsdf(); - comparisonOpsff(); - comparisonOpsid(); - comparisonOpsdi(); -} - -int branch(double d) { - if (d) { - return 1; - } - return 0; -} - -void testBranching() { - printf("branching: %d %d %d\n", branch(-1.0), branch(0.0), branch(1.0)); -} - -void testpassi(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) { - printf("testpassi: %d %d %d %d %d %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g, h, i, j, k, l); -} - -void testpassf(float a, float b, float c, float d, float e, float f, float g, float h, float i, float j, float k, float l) { - printf("testpassf: %g %g %g %g %g %g %g %g %g %g %g %g\n", a, b, c, d, e, f, g, h, i, j, k, l); -} - -void testpassd(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k, double l) { - printf("testpassd: %g %g %g %g %g %g %g %g %g %g %g %g\n", a, b, c, d, e, f, g, h, i, j, k, l); -} - -void testpassidf(int i, double d, float f) { - printf("testpassidf: %d %g %g\n", i, d, f); -} - -void testParameterPassing() { - float x; - testpassi(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); - testpassf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); - testpassd(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); - testpassi(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f); - testpassf(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f); - testpassd(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f); - testpassi(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); - testpassf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); - testpassd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); - testpassidf(1, 2.0, 3.0f); -} - -int main() { - unaryOps(); - binaryOps(); - comparisonOps(); - testBranching(); - testParameterPassing(); - return 0; -} diff --git a/libacc/tests/data/funcargs.c b/libacc/tests/data/funcargs.c deleted file mode 100644 index 1dce2263c5b..00000000000 --- a/libacc/tests/data/funcargs.c +++ /dev/null @@ -1,8 +0,0 @@ -int f(int a,int, int c) { - return a + c; -} - -int main() { - return f(1,2,3); -} - diff --git a/libacc/tests/data/hello.c b/libacc/tests/data/hello.c deleted file mode 100644 index 585ce6cadbd..00000000000 --- a/libacc/tests/data/hello.c +++ /dev/null @@ -1,3 +0,0 @@ -main(a,b) { - printf("Hello, world\n"); -} diff --git a/libacc/tests/data/inc.c b/libacc/tests/data/inc.c deleted file mode 100644 index 14c09d18d54..00000000000 --- a/libacc/tests/data/inc.c +++ /dev/null @@ -1,14 +0,0 @@ -// Check integer operations - -int main() { - int a = 0; - printf("%d\n", a++); - printf("%d\n", a++); - printf("%d\n", a--); - printf("%d\n", a--); - printf("%d\n", ++a); - printf("%d\n", ++a); - printf("%d\n", --a); - printf("%d\n", --a); - return a; -} diff --git a/libacc/tests/data/iops.c b/libacc/tests/data/iops.c deleted file mode 100644 index 780e95d69e2..00000000000 --- a/libacc/tests/data/iops.c +++ /dev/null @@ -1,23 +0,0 @@ -// Check integer operations - -void loops() { - int y; - printf("++\n"); - for(y = 0; y < 10; y++) { - printf("%d\n", y); - } - printf("--\n"); - for(y = 10; y >= 0; y--) { - printf("%d\n", y); - } -} - -void checkLiterals() { - printf("Literals: %d %d\n", 1, -1); -} - -int main() { - checkLiterals(); - loops(); - return 0; -} diff --git a/libacc/tests/data/locals.c b/libacc/tests/data/locals.c deleted file mode 100644 index f1ef363c487..00000000000 --- a/libacc/tests/data/locals.c +++ /dev/null @@ -1,71 +0,0 @@ -int a; - -int f() { - int a; - // Undefined variable b - // printf("f 0: a = %d b = %d\n", a, b); - printf("f 0: a = %d\n", a); - a = 2; - printf("f 1: a = %d\n", a); -} - -int g(int a) { - printf("g 0: a = %d\n", a); - a = 3; - printf("g 1: a = %d\n", a); -} - -int h(int a) { - // int a; // gcc 4.3 says error: 'a' redeclared as different kind of symbol - - printf("h 0: a = %d\n", a); - a = 4; - printf("h 1: a = %d\n", a); -} - -// Already defined global -// int h() {} -int globCheck() { - fprintf(stdout, "globCheck()\n"); -} - -int fwdCheck() { - b(); - // Undefined forward reference - // c(); -} - -int b() { - printf("b()\n"); -} - -int nested() { - int a; - printf("nested 0: a = %d\n", a); - a = 50; - printf("nested 1: a = %d\n", a); - { - int a; - printf("nested 2: a = %d\n", a); - a = 51; - printf("nested 3: a = %d\n", a); - } - printf("nested 4: a = %d\n", a); -} - -int main() { - globCheck(); - fwdCheck(); - printf("main 0: a = %d\n", a); - a = 5; - printf("main 1: a = %d\n", a); - f(); - printf("main 2: a = %d\n", a); - g(77); - printf("main 3: a = %d\n", a); - h(30); - printf("main 4: a = %d\n", a); - nested(); - printf("main 5: a = %d\n", a); - return 0; -} diff --git a/libacc/tests/data/missing-main.c b/libacc/tests/data/missing-main.c deleted file mode 100644 index e73eec46157..00000000000 --- a/libacc/tests/data/missing-main.c +++ /dev/null @@ -1,4 +0,0 @@ -/* No main. */ - -a() { -} \ No newline at end of file diff --git a/libacc/tests/data/otcc-ansi.c b/libacc/tests/data/otcc-ansi.c deleted file mode 100644 index 72580e95f43..00000000000 --- a/libacc/tests/data/otcc-ansi.c +++ /dev/null @@ -1,466 +0,0 @@ -// #include -int d, z, C, h, P, K, ac, q, G, v, Q, R, D, L, W, M; - -void E(int e) { - *(char*) D++ = e; -} - -void o() { - if (L) { - h = *(char*) L++; - if (h == 2) { - L = 0; - h = W; - } - } else - h = fgetc(Q); -} - -int X() { - return isalnum(h) | h == 95; -} - -void Y() { - if (h == 92) { - o(); - if (h == 110) - h = 10; - } -} - -void ad() { - int e, j, m; - while (isspace(h) | h == 35) { - if (h == 35) { - o(); - ad(); - if (d == 536) { - ad(); - E(32); - *(int*) d = 1; - *(int*) (d + 4) = D; - } - while (h != 10) { - E(h); - o(); - } - E(h); - E(2); - } - o(); - } - C = 0; - d = h; - if (X()) { - E(32); - M = D; - while (X()) { - E(h); - o(); - } - if (isdigit(d)) { - z = strtol(M, 0, 0); - d = 2; - } else { - *(char*) D = 32; - d = strstr(R, M - 1) - R; - *(char*) D = 0; - d = d * 8 + 256; - if (d > 536) { - d = P + d; - if (*(int*) d == 1) { - L = *(int*) (d + 4); - W = h; - o(); - ad(); - } - } - } - } else { - o(); - if (d == 39) { - d = 2; - Y(); - z = h; - o(); - o(); - } else if (d == 47 & h == 42) { - o(); - while (h) { - while (h != 42) - o(); - o(); - if (h == 47) - h = 0; - } - o(); - ad(); - } else { - e - = "++#m--%am*@R<^1c/@%[_[H3c%@%[_[H3c+@.B#d-@%:_^BKd<>`/03e<=0f>=/f<@.f>@1f==&g!='g&&k||#l&@.BCh^@.BSi|@.B+j~@/%Yd!@&d*@b"; - while (j = *(char*) e++) { - m = *(char*) e++; - z = 0; - while ((C = *(char*) e++ - 98) < 0) - z = z * 64 + C + 64; - if (j == d & (m == h | m == 64)) { - if (m == h) { - o(); - d = 1; - } - break; - } - } - } - } -} - -void ae(int g) { - while( g&&g!=-1) { - *(char*) q++=g; - g=g>>8; - } -} - -void A(int e) { - int g; - while( e) { - g=*(int*) e; - *(int*) e=q-e-4; - e=g; - } -} - -int s(int g, int e) { - ae(g); - *(int*) q = e; - e = q; - q = q + 4; - return e; -} - -void H(int e) { - s(184,e); -} - -int B(int e) { - return s(233,e); -} - -int S(int j, int e) { - ae(1032325); - return s(132 + j, e); -} - -void Z(int e) { - ae( 49465); - H(0); - ae( 15); - ae( e+144); - ae( 192); -} - -void N(int j, int e) { - ae(j + 131); - s((e > -512 && e < 512) << 7 | 5, e); -} - -void T (int j) { - int g,e,m,aa; - g=1; - if( d == 34) { - H(v); - while( h!=34) { - Y (); - *(char*) v++=h; - o (); - } - *(char*) v=0; - v=v +4&-4; - o (); - ad(); - } - else { - aa=C; - m= z; - e=d; - ad(); - if( e == 2) { - H(m); - } - else if( aa == 2) { - T(0); - s(185,0); - if( e == 33)Z(m); - else ae( m); - } - else if( e == 40) { - w (); - ad(); - } - else if( e == 42) { - ad(); - e=d; - ad(); - ad(); - if( d == 42) { - ad(); - ad(); - ad(); - ad(); - e=0; - } - ad(); - T(0); - if( d == 61) { - ad(); - ae( 80); - w (); - ae( 89); - ae( 392+(e == 256)); - } - else if( e) { - if( e == 256)ae( 139); - else ae( 48655); - q++; - } - } - else if( e == 38) { - N(10,*(int*) d); - ad(); - } - else { - g=*(int*) e; - if(!g)g=dlsym(0,M); - if( d == 61&j) { - ad(); - w (); - N(6,g); - } - else if( d!= 40) { - N(8,g); - if( C == 11) { - N(0,g); - ae( z); - ad(); - } - } - } - } - if( d == 40) { - if( g == 1)ae( 80); - m= s(60545,0); - ad(); - j=0; - while( d!= 41) { - w (); - s(2393225,j); - if( d == 44)ad(); - j=j +4; - } - *(int*) m= j; - ad(); - if(!g) { - e=e +4; - *(int*) e=s(232,*(int*) e); - } - else if( g == 1) { - s(2397439,j); - j=j +4; - } - else { - s(232,g-q-5); - } - if( j)s(50305,j); - } -} - -void O (int j) { - int e,g,m; - if( j--== 1)T(1); - else { - O (j); - m= 0; - while( j == C) { - g=d; - e=z; - ad(); - if( j>8) { - m= S(e,m); - O (j); - } - else { - ae( 80); - O (j); - ae( 89); - if( j == 4|j == 5) { - Z(e); - } - else { - ae( e); - if( g == 37)ae( 146); - } - } - } - if( m&&j>8) { - m= S(e,m); - H(e^1); - B(5); - A(m); - H(e); - } - } -} - -void w() { - O(11); -} - -int U() { - w(); - return S(0, 0); -} - -void I (int j) { - int m,g,e; - if( d == 288) { - ad(); - ad(); - m= U (); - ad(); - I (j); - if( d == 312) { - ad(); - g=B(0); - A(m); - I (j); - A(g); - } - else { - A(m); - } - } - else if( d == 352|d == 504) { - e=d; - ad(); - ad(); - if( e == 352) { - g=q; - m= U (); - } - else { - if( d!= 59)w (); - ad(); - g=q; - m= 0; - if( d!= 59)m= U (); - ad(); - if( d!= 41) { - e=B(0); - w (); - B(g-q-5); - A(e); - g=e +4; - } - } - ad(); - I(&m); - B(g-q-5); - A(m); - } - else if( d == 123) { - ad(); - ab(1); - while( d!= 125)I (j); - ad(); - } - else { - if( d == 448) { - ad(); - if( d!= 59)w (); - K=B(K); - } - else if( d == 400) { - ad(); - *(int*) j=B(*(int*) j); - } - else if( d!= 59)w (); - ad(); - } -} - -void ab (int j) { - int m; - while( d == 256|d!=-1&!j) { - if( d == 256) { - ad(); - while( d!= 59) { - if( j) { - G=G +4; - *(int*) d=-G; - } - else { - *(int*) d=v; - v=v +4; - } - ad(); - if( d == 44)ad() ; - } - ad(); - } - else { - A(*(int*)(d +4)); - *(int*) d=q; - ad(); - ad(); - m= 8; - while( d!= 41) { - *(int*) d=m; - m= m +4; - ad(); - if( d == 44)ad(); - } - ad(); - K=G=0; - ae( 15042901); - m= s(60545,0); - I(0); - A(K); - ae( 50121); - *(int*) m= G; - } - } -} - -int run(int g, int e) { - return (*(int(*)()) *(int*) (P + 592))(g, e); -} - -int main(int g, int e) { - int result; - Q = stdin; - if (g-- > 1) { - e = e + 4; - Q = fopen(*(int*) e, "r"); - if (!Q) { - fprintf(stderr, "otcc-ansi.c: could not open file %s\n", *(int*) e); - return -2; - } - } - D = strcpy(R = calloc(1, 99999), " int if else while break return for define main ") + 48; - v = calloc(1, 99999); - q = ac = calloc(1, 99999); - P = calloc(1, 99999); - o(); - ad(); - ab(0); - if (mprotect(ac & (~ 4095), (99999 + 4095) & (~ 4095), 7)) { - printf("Mprotect failed. %d\n", errno); - return -1; - } - fprintf(stderr, "otcc-ansi.c: About to execute compiled code:\n"); - result = run(g, e); - fprintf(stderr, "atcc-ansi.c: result: %d\n", result); - return result; -} - diff --git a/libacc/tests/data/otcc-noinclude.c b/libacc/tests/data/otcc-noinclude.c deleted file mode 100644 index 530f9e2e8f8..00000000000 --- a/libacc/tests/data/otcc-noinclude.c +++ /dev/null @@ -1,446 +0,0 @@ -// #include -#define k *(int*) -#define a if( -#define c ad() -#define i else -#define p while( -#define x *(char*) -#define b == -#define V =calloc(1,99999) -#define f () -#define J return -#define l ae( -#define n e) -#define u d!= -#define F int -#define y (j) -#define r m= -#define t +4 -F d,z,C,h,P,K,ac,q,G,v,Q,R,D,L,W,M; -E(n{ -x D++=e; -} -o f{ -a L){ -h=x L++; -a h b 2){ -L=0; -h=W; -} -} -i h=fgetc(Q); -} -X f{ -J isalnum(h)|h b 95; -} -Y f{ -a h b 92){ -o f; -a h b 110)h=10; -} -} -c{ -F e,j,m; -p isspace(h)|h b 35){ -a h b 35){ -o f; -c; -a d b 536){ -c; -E(32); -k d=1; -k(d t)=D; -} -p h!=10){ -E(h); -o f; -} -E(h); -E(2); -} -o f; -} -C=0; -d=h; -a X f){ -E(32); -M=D; -p X f){ -E(h); -o f; -} -a isdigit(d)){ -z=strtol(M,0,0); -d=2; -} -i{ -x D=32; -d=strstr(R,M-1)-R; -x D=0; -d=d*8+256; -a d>536){ -d=P+d; -a k d b 1){ -L=k(d t); -W=h; -o f; -c; -} -} -} -} -i{ -o f; -a d b 39){ -d=2; -Y f; -z=h; -o f; -o f; -} -i a d b 47&h b 42){ -o f; -p h){ -p h!=42)o f; -o f; -a h b 47)h=0; -} -o f; -c; -} -i{ -e="++#m--%am*@R<^1c/@%[_[H3c%@%[_[H3c+@.B#d-@%:_^BKd<>`/03e<=0f>=/f<@.f>@1f==&g!='g&&k||#l&@.BCh^@.BSi|@.B+j~@/%Yd!@&d*@b"; -p j=x e++){ -r x e++; -z=0; -p(C=x e++-98)<0)z=z*64+C+64; -a j b d&(m b h|m b 64)){ -a m b h){ -o f; -d=1; -} -break; -} -} -} -} -} -l g){ -p g&&g!=-1){ -x q++=g; -g=g>>8; -} -} -A(n{ -F g; -p n{ -g=k e; -k e=q-e-4; -e=g; -} -} -s(g,n{ -l g); -k q=e; -e=q; -q=q t; -J e; -} -H(n{ -s(184,n; -} -B(n{ -J s(233,n; -} -S(j,n{ -l 1032325); -J s(132+j,n; -} -Z(n{ -l 49465); -H(0); -l 15); -l e+144); -l 192); -} -N(j,n{ -l j+131); -s((e<512)<<7|5,n; -} -T y{ -F g,e,m,aa; -g=1; -a d b 34){ -H(v); -p h!=34){ -Y f; -x v++=h; -o f; -} -x v=0; -v=v t&-4; -o f; -c; -} -i{ -aa=C; -r z; -e=d; -c; -a e b 2){ -H(m); -} -i a aa b 2){ -T(0); -s(185,0); -a e b 33)Z(m); -i l m); -} -i a e b 40){ -w f; -c; -} -i a e b 42){ -c; -e=d; -c; -c; -a d b 42){ -c; -c; -c; -c; -e=0; -} -c; -T(0); -a d b 61){ -c; -l 80); -w f; -l 89); -l 392+(e b 256)); -} -i a n{ -a e b 256)l 139); -i l 48655); -q++; -} -} -i a e b 38){ -N(10,k d); -c; -} -i{ -g=k e; -a!g)g=dlsym(0,M); -a d b 61&j){ -c; -w f; -N(6,g); -} -i a u 40){ -N(8,g); -a C b 11){ -N(0,g); -l z); -c; -} -} -} -} -a d b 40){ -a g b 1)l 80); -r s(60545,0); -c; -j=0; -p u 41){ -w f; -s(2393225,j); -a d b 44)c; -j=j t; -} -k r j; -c; -a!g){ -e=e t; -k e=s(232,k n; -} -i a g b 1){ -s(2397439,j); -j=j t; -} -i{ -s(232,g-q-5); -} -a j)s(50305,j); -} -} -O y{ -F e,g,m; -a j--b 1)T(1); -i{ -O y; -r 0; -p j b C){ -g=d; -e=z; -c; -a j>8){ -r S(e,m); -O y; -} -i{ -l 80); -O y; -l 89); -a j b 4|j b 5){ -Z(n; -} -i{ -l n; -a g b 37)l 146); -} -} -} -a m&&j>8){ -r S(e,m); -H(e^1); -B(5); -A(m); -H(n; -} -} -} -w f{ -O(11); -} -U f{ -w f; -J S(0,0); -} -I y{ -F m,g,e; -a d b 288){ -c; -c; -r U f; -c; -I y; -a d b 312){ -c; -g=B(0); -A(m); -I y; -A(g); -} -i{ -A(m); -} -} -i a d b 352|d b 504){ -e=d; -c; -c; -a e b 352){ -g=q; -r U f; -} -i{ -a u 59)w f; -c; -g=q; -r 0; -a u 59)r U f; -c; -a u 41){ -e=B(0); -w f; -B(g-q-5); -A(n; -g=e t; -} -} -c; -I(&m); -B(g-q-5); -A(m); -} -i a d b 123){ -c; -ab(1); -p u 125)I y; -c; -} -i{ -a d b 448){ -c; -a u 59)w f; -K=B(K); -} -i a d b 400){ -c; -k j=B(k j); -} -i a u 59)w f; -c; -} -} -ab y{ -F m; -p d b 256|u-1&!j){ -a d b 256){ -c; -p u 59){ -a j){ -G=G t; -k d=-G; -} -i{ -k d=v; -v=v t; -} -c; -a d b 44)c; -} -c; -} -i{ -A(k(d t)); -k d=q; -c; -c; -r 8; -p u 41){ -k d=m; -r m t; -c; -a d b 44)c; -} -c; -K=G=0; -l 15042901); -r s(60545,0); -I(0); -A(K); -l 50121); -k r G; -} -} -} -main(g,n{ -Q=stdin; -a g-->1){ -e=e t; -Q=fopen(k e,"r"); -} -D=strcpy(R V," int if else while break return for define main ")+48; -v V; -q=ac V; -P V; -o f; -c; -ab(0); -J(*(int(*)f)k(P+592))(g,n; -} - diff --git a/libacc/tests/data/otcc.c b/libacc/tests/data/otcc.c deleted file mode 100644 index 433ae2ea65f..00000000000 --- a/libacc/tests/data/otcc.c +++ /dev/null @@ -1,448 +0,0 @@ -#include -#define k *(int*) -#define a if( -#define c ad() -#define i else -#define p while( -#define x *(char*) -#define b == -#define V =calloc(1,99999) -#define f () -#define J return -#define l ae( -#define n e) -#define u d!= -#define F int -#define y (j) -#define r m= -#define t +4 -F d,z,C,h,P,K,ac,q,G,v,Q,R,D,L,W,M; -E(n{ -x D++=e; -} -o f{ -a L){ -h=x L++; -a h b 2){ -L=0; -h=W; -} -} -i h=fgetc(Q); -} -X f{ -J isalnum(h)|h b 95; -} -Y f{ -a h b 92){ -o f; -a h b 110)h=10; -} -} -c{ -F e,j,m; -p isspace(h)|h b 35){ -a h b 35){ -o f; -c; -a d b 536){ -c; -E(32); -k d=1; -k(d t)=D; -} -p h!=10){ -E(h); -o f; -} -E(h); -E(2); -} -o f; -} -C=0; -d=h; -a X f){ -E(32); -M=D; -p X f){ -E(h); -o f; -} -a isdigit(d)){ -z=strtol(M,0,0); -d=2; -} -i{ -x D=32; -d=strstr(R,M-1)-R; -x D=0; -d=d*8+256; -a d>536){ -d=P+d; -a k d b 1){ -L=k(d t); -W=h; -o f; -c; -} -} -} -} -i{ -o f; -a d b 39){ -d=2; -Y f; -z=h; -o f; -o f; -} -i a d b 47&h b 42){ -o f; -p h){ -p h!=42)o f; -o f; -a h b 47)h=0; -} -o f; -c; -} -i{ -e="++#m--%am*@R<^1c/@%[_[H3c%@%[_[H3c+@.B#d-@%:_^BKd<>`/03e<=0f>=/f<@.f>@1f==&g!='g&&k||#l&@.BCh^@.BSi|@.B+j~@/%Yd!@&d*@b"; -p j=x e++){ -r x e++; -z=0; -p(C=x e++-98)<0)z=z*64+C+64; -a j b d&(m b h|m b 64)){ -a m b h){ -o f; -d=1; -} -break; -} -} -} -} -} -l g){ -p g&&g!=-1){ -x q++=g; -g=g>>8; -} -} -A(n{ -F g; -p n{ -g=k e; -k e=q-e-4; -e=g; -} -} -s(g,n{ -l g); -k q=e; -e=q; -q=q t; -J e; -} -H(n{ -s(184,n; -} -B(n{ -J s(233,n; -} -S(j,n{ -l 1032325); -J s(132+j,n; -} -Z(n{ -l 49465); -H(0); -l 15); -l e+144); -l 192); -} -N(j,n{ -l j+131); -s((e<512)<<7|5,n; -} -T y{ -F g,e,m,aa; -g=1; -a d b 34){ -H(v); -p h!=34){ -Y f; -x v++=h; -o f; -} -x v=0; -v=v t&-4; -o f; -c; -} -i{ -aa=C; -r z; -e=d; -c; -a e b 2){ -H(m); -} -i a aa b 2){ -T(0); -s(185,0); -a e b 33)Z(m); -i l m); -} -i a e b 40){ -w f; -c; -} -i a e b 42){ -c; -e=d; -c; -c; -a d b 42){ -c; -c; -c; -c; -e=0; -} -c; -T(0); -a d b 61){ -c; -l 80); -w f; -l 89); -l 392+(e b 256)); -} -i a n{ -a e b 256)l 139); -i l 48655); -q++; -} -} -i a e b 38){ -N(10,k d); -c; -} -i{ -g=k e; -a!g)g=dlsym(0,M); -a d b 61&j){ -c; -w f; -N(6,g); -} -i a u 40){ -N(8,g); -a C b 11){ -N(0,g); -l z); -c; -} -} -} -} -a d b 40){ -a g b 1)l 80); -r s(60545,0); -c; -j=0; -p u 41){ -w f; -s(2393225,j); -a d b 44)c; -j=j t; -} -k r j; -c; -a!g){ -e=e t; -k e=s(232,k n; -} -i a g b 1){ -s(2397439,j); -j=j t; -} -i{ -s(232,g-q-5); -} -a j)s(50305,j); -} -} -O y{ -F e,g,m; -a j--b 1)T(1); -i{ -O y; -r 0; -p j b C){ -g=d; -e=z; -c; -a j>8){ -r S(e,m); -O y; -} -i{ -l 80); -O y; -l 89); -a j b 4|j b 5){ -Z(n; -} -i{ -l n; -a g b 37)l 146); -} -} -} -a m&&j>8){ -r S(e,m); -H(e^1); -B(5); -A(m); -H(n; -} -} -} -w f{ -O(11); -} -U f{ -w f; -J S(0,0); -} -I y{ -F m,g,e; -a d b 288){ -c; -c; -r U f; -c; -I y; -a d b 312){ -c; -g=B(0); -A(m); -I y; -A(g); -} -i{ -A(m); -} -} -i a d b 352|d b 504){ -e=d; -c; -c; -a e b 352){ -g=q; -r U f; -} -i{ -a u 59)w f; -c; -g=q; -r 0; -a u 59)r U f; -c; -a u 41){ -e=B(0); -w f; -B(g-q-5); -A(n; -g=e t; -} -} -c; -I(&m); -B(g-q-5); -A(m); -} -i a d b 123){ -c; -ab(1); -p u 125)I y; -c; -} -i{ -a d b 448){ -c; -a u 59)w f; -K=B(K); -} -i a d b 400){ -c; -k j=B(k j); -} -i a u 59)w f; -c; -} -} -ab y{ -F m; -p d b 256|u-1&!j){ -a d b 256){ -c; -p u 59){ -a j){ -G=G t; -k d=-G; -} -i{ -k d=v; -v=v t; -} -c; -a d b 44)c; -} -c; -} -i{ -A(k(d t)); -k d=q; -c; -c; -r 8; -p u 41){ -k d=m; -r m t; -c; -a d b 44)c; -} -c; -K=G=0; -l 15042901); -r s(60545,0); -I(0); -A(K); -l 50121); -k r G; -} -} -} -main(g,n{ -Q=stdin; -a g-->1){ -e=e t; -Q=fopen(k e,"r"); -} -D=strcpy(R V," int if else while break return for define main ")+48; -v V; -q=ac V; -P V; -o f; -c; -ab(0); -mprotect(ac & (~ 4095), (99999 + 4095) & (~ 4095), 7); -fprintf(stderr, "otcc.c: about to execute compiled code.\n"); -J(*(int(*)f)k(P+592))(g,n; -} - diff --git a/libacc/tests/data/pointers.c b/libacc/tests/data/pointers.c deleted file mode 100644 index 461ebeb4684..00000000000 --- a/libacc/tests/data/pointers.c +++ /dev/null @@ -1,15 +0,0 @@ -int main() { - int* pa = (int*) malloc(100); - int* pb = pa + 1; - int* pc = (int*) 0; - *pa = 1; - *pb = 2; - printf("Pointer difference: %d %d\n", pb - pa, ((int) pb) - ((int) pa)); - int c = * (pa + 1); - printf("Pointer addition: %d\n", c); - printf("Pointer comparison to zero: %d %d %d\n", pa == 0, pb == 0, pc == 0); - printf("Pointer comparison: %d %d %d %d %d\n", pa < pb, pa == pb, pa > pb, ! pb, ! pc); - free(pa); - return 0; -} - diff --git a/libacc/tests/data/pointers2.c b/libacc/tests/data/pointers2.c deleted file mode 100644 index 69e402f6990..00000000000 --- a/libacc/tests/data/pointers2.c +++ /dev/null @@ -1,35 +0,0 @@ -// Test multiple levels of indirection - -void testsingle() { - int a = 0; - int* pa = &a; - printf("a = %d, *pa = %d\n", a, *pa); - *pa = 2; - printf("a = %d, *pa = %d\n", a, *pa); -} - -void testdouble() { - int a = 0; - int* pa = &a; - int** ppa = &pa; - printf("a = %d, *pa = %d **ppa = %d\n", a, *pa, **ppa); - **ppa = 2; - printf("a = %d, *pa = %d **ppa = %d\n", a, *pa, **ppa); -} - -void testtripple() { - int a = 0; - int* pa = &a; - int** ppa = &pa; - int*** pppa = &ppa; - printf("a = %d, *pa = %d **ppa = %d\n ***pppa = %d", a, *pa, **ppa, ***pppa); - ***pppa = 2; - printf("a = %d, *pa = %d **ppa = %d\n ***pppa = %d", a, *pa, **ppa, ***pppa); -} - -int main() { - testsingle(); - testdouble(); - testdouble(); - return 0; -} diff --git a/libacc/tests/data/returnval-ansi.c b/libacc/tests/data/returnval-ansi.c deleted file mode 100644 index 6b53fd54bdf..00000000000 --- a/libacc/tests/data/returnval-ansi.c +++ /dev/null @@ -1,8 +0,0 @@ - -int main(int argc, char** argv) { - return f(); -} - -int f() { - return 42; -} diff --git a/libacc/tests/data/returnval.c b/libacc/tests/data/returnval.c deleted file mode 100644 index 1cf5bae9318..00000000000 --- a/libacc/tests/data/returnval.c +++ /dev/null @@ -1,4 +0,0 @@ -main() { - return 42; -} - diff --git a/libacc/tests/data/rollo3.c b/libacc/tests/data/rollo3.c deleted file mode 100644 index b21c12fc302..00000000000 --- a/libacc/tests/data/rollo3.c +++ /dev/null @@ -1,9 +0,0 @@ - -float fabsf(float); - -int main(void* con, int ft, int launchID) -{ - float f = fabsf(-10.0f); - return f; -} - diff --git a/libacc/tests/data/short.c b/libacc/tests/data/short.c deleted file mode 100644 index 5e222f33aa8..00000000000 --- a/libacc/tests/data/short.c +++ /dev/null @@ -1,6 +0,0 @@ -short a = 3; -int main() { - short* b = &a; - *b = *b - 5; - return a; -} diff --git a/libacc/tests/data/simplest.c b/libacc/tests/data/simplest.c deleted file mode 100644 index bae895adcb6..00000000000 --- a/libacc/tests/data/simplest.c +++ /dev/null @@ -1 +0,0 @@ -main() {} diff --git a/libacc/tests/data/structs.c b/libacc/tests/data/structs.c deleted file mode 100644 index dd81af31edf..00000000000 --- a/libacc/tests/data/structs.c +++ /dev/null @@ -1,95 +0,0 @@ -// struct definition and declaration -struct a { - int a; - int b; -} c; - -// Useless, but legal struct declaration -struct { - int x; -}; - -// Useful anonymous struct declaration -struct { - int y; -} anon1, anon2; - -// forward declarations -struct a; -struct b; -struct c; - -struct b {int a; int b; }; - -// struct c {b g; }; // syntax error. - -// struct s {float c,a,b,c;} s; // duplicate struct member - -struct c {struct b g; }; - -// struct a { int w; }; // error - -void testCopying() { - struct a {int a[10]; char c;} a, b; - a.c = 37; - b.c = 38; - b = a; - printf("testCopying: %d == %d\n", a.c, b.c); -} - -void testUnion() { - union u; - union u {float f;int i;} u; - u.f = 1.0f; - printf("testUnion: %g == 0x%08x\n", u.f, u.i); -} - -struct v {float x, y, z, w; }; - -void add(struct v* result, struct v* a, struct v* b) { - result->x = a->x + b->x; - result->y = a->y + b->y; - result->z = a->z + b->z; - result->w = a->w + b->w; -} - -void set(struct v* v, float x, float y, float z, float w) { - v->x = x; - v->y = y; - v->z = z; - v->w = w; -} - -void print(struct v* v) { - printf("(%g, %g, %g, %g)\n", v->x, v->y, v->z, v->w); -} - -void testArgs() { - struct v a, b, c; - set(&a, 1.0f, 2.0f, 3.0f, 4.0f); - set(&b, 5.0f, 6.0f, 7.0f, 8.0f); - add(&c, &a, &b); - printf("testArgs: "); - print(&c); -} - -int main() { - anon1.y = 3; - anon2.y = anon1.y; - - testCopying(); - testUnion(); - testArgs(); - - struct c cc; - cc.g.a = 3; - c.a = 1; - c.b = 3; - struct a {int x, y; } z; - // struct a {int x, y; } z2; - z.x = c.a; - struct a *pA; - pA = &z; - pA->x += 5; - return pA->x; -} diff --git a/libacc/tests/data/testStringConcat.c b/libacc/tests/data/testStringConcat.c deleted file mode 100644 index bf06ae18ef2..00000000000 --- a/libacc/tests/data/testStringConcat.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - return printf("Hello" "," " world\n"); -} - diff --git a/libacc/tests/disassem.cpp b/libacc/tests/disassem.cpp deleted file mode 100644 index ac35342fde2..00000000000 --- a/libacc/tests/disassem.cpp +++ /dev/null @@ -1,711 +0,0 @@ -/* $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $ */ - -/*- - * Copyright (c) 1996 Mark Brinicombe. - * Copyright (c) 1996 Brini. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Brini. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * RiscBSD kernel project - * - * db_disasm.c - * - * Kernel disassembler - * - * Created : 10/02/96 - * - * Structured after the sparc/sparc/db_disasm.c by David S. Miller & - * Paul Kranenburg - * - * This code is not complete. Not all instructions are disassembled. - */ - -#include -//__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/arm/arm/disassem.c,v 1.2 2005/01/05 21:58:47 imp Exp $"); -#include -#include -#include - -#include "disassem.h" -#include "armreg.h" -//#include - -/* - * General instruction format - * - * insn[cc][mod] [operands] - * - * Those fields with an uppercase format code indicate that the field - * follows directly after the instruction before the separator i.e. - * they modify the instruction rather than just being an operand to - * the instruction. The only exception is the writeback flag which - * follows a operand. - * - * - * 2 - print Operand 2 of a data processing instruction - * d - destination register (bits 12-15) - * n - n register (bits 16-19) - * s - s register (bits 8-11) - * o - indirect register rn (bits 16-19) (used by swap) - * m - m register (bits 0-3) - * a - address operand of ldr/str instruction - * e - address operand of ldrh/strh instruction - * l - register list for ldm/stm instruction - * f - 1st fp operand (register) (bits 12-14) - * g - 2nd fp operand (register) (bits 16-18) - * h - 3rd fp operand (register/immediate) (bits 0-4) - * b - branch address - * t - thumb branch address (bits 24, 0-23) - * k - breakpoint comment (bits 0-3, 8-19) - * X - block transfer type - * Y - block transfer type (r13 base) - * c - comment field bits(0-23) - * p - saved or current status register - * F - PSR transfer fields - * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN - * L - co-processor transfer size - * S - set status flag - * P - fp precision - * Q - fp precision (for ldf/stf) - * R - fp rounding - * v - co-processor data transfer registers + addressing mode - * W - writeback flag - * x - instruction in hex - * # - co-processor number - * y - co-processor data processing registers - * z - co-processor register transfer registers - */ - -struct arm32_insn { - u_int mask; - u_int pattern; - const char* name; - const char* format; -}; - -static const struct arm32_insn arm32_i[] = { - { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */ - { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */ - { 0x0f000000, 0x0f000000, "swi", "c" }, - { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */ - { 0x0f000000, 0x0a000000, "b", "b" }, - { 0x0f000000, 0x0b000000, "bl", "b" }, - { 0x0fe000f0, 0x00000090, "mul", "Snms" }, - { 0x0fe000f0, 0x00200090, "mla", "Snmsd" }, - { 0x0fe000f0, 0x00800090, "umull", "Sdnms" }, - { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" }, - { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" }, - { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" }, - { 0x0d700000, 0x04200000, "strt", "daW" }, - { 0x0d700000, 0x04300000, "ldrt", "daW" }, - { 0x0d700000, 0x04600000, "strbt", "daW" }, - { 0x0d700000, 0x04700000, "ldrbt", "daW" }, - { 0x0c500000, 0x04000000, "str", "daW" }, - { 0x0c500000, 0x04100000, "ldr", "daW" }, - { 0x0c500000, 0x04400000, "strb", "daW" }, - { 0x0c500000, 0x04500000, "ldrb", "daW" }, - { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */ - { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */ - { 0x0e100000, 0x08000000, "stm", "XnWl" }, - { 0x0e100000, 0x08100000, "ldm", "XnWl" }, - { 0x0e1000f0, 0x00100090, "ldrb", "deW" }, - { 0x0e1000f0, 0x00000090, "strb", "deW" }, - { 0x0e1000f0, 0x001000d0, "ldrsb", "deW" }, - { 0x0e1000f0, 0x001000b0, "ldrh", "deW" }, - { 0x0e1000f0, 0x000000b0, "strh", "deW" }, - { 0x0e1000f0, 0x001000f0, "ldrsh", "deW" }, - { 0x0f200090, 0x00200090, "und", "x" }, /* Before data processing */ - { 0x0e1000d0, 0x000000d0, "und", "x" }, /* Before data processing */ - { 0x0ff00ff0, 0x01000090, "swp", "dmo" }, - { 0x0ff00ff0, 0x01400090, "swpb", "dmo" }, - { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* Before data processing */ - { 0x0fb0fff0, 0x0120f000, "msr", "pFm" },/* Before data processing */ - { 0x0fb0f000, 0x0320f000, "msr", "pF2" },/* Before data processing */ - { 0x0ffffff0, 0x012fff10, "bx", "m" }, - { 0x0fff0ff0, 0x016f0f10, "clz", "dm" }, - { 0x0ffffff0, 0x012fff30, "blx", "m" }, - { 0xfff000f0, 0xe1200070, "bkpt", "k" }, - { 0x0de00000, 0x00000000, "and", "Sdn2" }, - { 0x0de00000, 0x00200000, "eor", "Sdn2" }, - { 0x0de00000, 0x00400000, "sub", "Sdn2" }, - { 0x0de00000, 0x00600000, "rsb", "Sdn2" }, - { 0x0de00000, 0x00800000, "add", "Sdn2" }, - { 0x0de00000, 0x00a00000, "adc", "Sdn2" }, - { 0x0de00000, 0x00c00000, "sbc", "Sdn2" }, - { 0x0de00000, 0x00e00000, "rsc", "Sdn2" }, - { 0x0df00000, 0x01100000, "tst", "Dn2" }, - { 0x0df00000, 0x01300000, "teq", "Dn2" }, - { 0x0df00000, 0x01500000, "cmp", "Dn2" }, - { 0x0df00000, 0x01700000, "cmn", "Dn2" }, - { 0x0de00000, 0x01800000, "orr", "Sdn2" }, - { 0x0de00000, 0x01a00000, "mov", "Sd2" }, - { 0x0de00000, 0x01c00000, "bic", "Sdn2" }, - { 0x0de00000, 0x01e00000, "mvn", "Sd2" }, - { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" }, - { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" }, - { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" }, - { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" }, - { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" }, - { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" }, - { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" }, - { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" }, - { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" }, - { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" }, - { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" }, - { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" }, - { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" }, - { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" }, - { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" }, - { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" }, - { 0x0ff08f10, 0x0e208100, "abs", "PRfh" }, - { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" }, - { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" }, - { 0x0ff08f10, 0x0e508100, "log", "PRfh" }, - { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" }, - { 0x0ff08f10, 0x0e708100, "exp", "PRfh" }, - { 0x0ff08f10, 0x0e808100, "sin", "PRfh" }, - { 0x0ff08f10, 0x0e908100, "cos", "PRfh" }, - { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" }, - { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" }, - { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" }, - { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" }, - { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" }, - { 0x0e100f00, 0x0c000100, "stf", "QLv" }, - { 0x0e100f00, 0x0c100100, "ldf", "QLv" }, - { 0x0ff00f10, 0x0e000110, "flt", "PRgd" }, - { 0x0ff00f10, 0x0e100110, "fix", "PRdh" }, - { 0x0ff00f10, 0x0e200110, "wfs", "d" }, - { 0x0ff00f10, 0x0e300110, "rfs", "d" }, - { 0x0ff00f10, 0x0e400110, "wfc", "d" }, - { 0x0ff00f10, 0x0e500110, "rfc", "d" }, - { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" }, - { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" }, - { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" }, - { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" }, - { 0xff100010, 0xfe000010, "mcr2", "#z" }, - { 0x0f100010, 0x0e000010, "mcr", "#z" }, - { 0xff100010, 0xfe100010, "mrc2", "#z" }, - { 0x0f100010, 0x0e100010, "mrc", "#z" }, - { 0xff000010, 0xfe000000, "cdp2", "#y" }, - { 0x0f000010, 0x0e000000, "cdp", "#y" }, - { 0xfe100090, 0xfc100000, "ldc2", "L#v" }, - { 0x0e100090, 0x0c100000, "ldc", "L#v" }, - { 0xfe100090, 0xfc000000, "stc2", "L#v" }, - { 0x0e100090, 0x0c000000, "stc", "L#v" }, - { 0xf550f000, 0xf550f000, "pld", "ne" }, - { 0x0ff00ff0, 0x01000050, "qaad", "dmn" }, - { 0x0ff00ff0, 0x01400050, "qdaad", "dmn" }, - { 0x0ff00ff0, 0x01600050, "qdsub", "dmn" }, - { 0x0ff00ff0, 0x01200050, "dsub", "dmn" }, - { 0x0ff000f0, 0x01000080, "smlabb", "nmsd" }, // d & n inverted!! - { 0x0ff000f0, 0x010000a0, "smlatb", "nmsd" }, // d & n inverted!! - { 0x0ff000f0, 0x010000c0, "smlabt", "nmsd" }, // d & n inverted!! - { 0x0ff000f0, 0x010000e0, "smlatt", "nmsd" }, // d & n inverted!! - { 0x0ff000f0, 0x01400080, "smlalbb","ndms" }, // d & n inverted!! - { 0x0ff000f0, 0x014000a0, "smlaltb","ndms" }, // d & n inverted!! - { 0x0ff000f0, 0x014000c0, "smlalbt","ndms" }, // d & n inverted!! - { 0x0ff000f0, 0x014000e0, "smlaltt","ndms" }, // d & n inverted!! - { 0x0ff000f0, 0x01200080, "smlawb", "nmsd" }, // d & n inverted!! - { 0x0ff0f0f0, 0x012000a0, "smulwb","nms" }, // d & n inverted!! - { 0x0ff000f0, 0x012000c0, "smlawt", "nmsd" }, // d & n inverted!! - { 0x0ff0f0f0, 0x012000e0, "smulwt","nms" }, // d & n inverted!! - { 0x0ff0f0f0, 0x01600080, "smulbb","nms" }, // d & n inverted!! - { 0x0ff0f0f0, 0x016000a0, "smultb","nms" }, // d & n inverted!! - { 0x0ff0f0f0, 0x016000c0, "smulbt","nms" }, // d & n inverted!! - { 0x0ff0f0f0, 0x016000e0, "smultt","nms" }, // d & n inverted!! - { 0x00000000, 0x00000000, NULL, NULL } -}; - -static char const arm32_insn_conditions[][4] = { - "eq", "ne", "cs", "cc", - "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", - "gt", "le", "", "nv" -}; - -static char const insn_block_transfers[][4] = { - "da", "ia", "db", "ib" -}; - -static char const insn_stack_block_transfers[][4] = { - "ed", "ea", "fd", "fa" -}; - -static char const op_shifts[][4] = { - "lsl", "lsr", "asr", "ror" -}; - -static char const insn_fpa_rounding[][2] = { - "", "p", "m", "z" -}; - -static char const insn_fpa_precision[][2] = { - "s", "d", "e", "p" -}; - -static char const insn_fpaconstants[][8] = { - "0.0", "1.0", "2.0", "3.0", - "4.0", "5.0", "0.5", "10.0" -}; - -#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f] -#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3] -#define insn_stkblktrans(x) insn_stack_block_transfers[(x >> 23) & 3] -#define op2_shift(x) op_shifts[(x >> 5) & 3] -#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03] -#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1] -#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1] -#define insn_fpaimm(x) insn_fpaconstants[x & 0x07] - -/* Local prototypes */ -static void disasm_register_shift(const disasm_interface_t *di, u_int insn); -static void disasm_print_reglist(const disasm_interface_t *di, u_int insn); -static void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, - u_int loc); -static void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, - u_int loc); -static void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, - u_int loc); -static u_int disassemble_readword(u_int address); -static void disassemble_printaddr(u_int address); - -u_int -disasm(const disasm_interface_t *di, u_int loc, int altfmt) -{ - const struct arm32_insn *i_ptr = &arm32_i[0]; - - u_int insn; - int matchp; - int branch; - const char* f_ptr; - int fmt; - - fmt = 0; - matchp = 0; - insn = di->di_readword(loc); - -/* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ - - while (i_ptr->name) { - if ((insn & i_ptr->mask) == i_ptr->pattern) { - matchp = 1; - break; - } - i_ptr++; - } - - if (!matchp) { - di->di_printf("und%s\t%08x\n", insn_condition(insn), insn); - return(loc + INSN_SIZE); - } - - /* If instruction forces condition code, don't print it. */ - if ((i_ptr->mask & 0xf0000000) == 0xf0000000) - di->di_printf("%s", i_ptr->name); - else - di->di_printf("%s%s", i_ptr->name, insn_condition(insn)); - - f_ptr = i_ptr->format; - - /* Insert tab if there are no instruction modifiers */ - - if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') { - ++fmt; - di->di_printf("\t"); - } - - while (*f_ptr) { - switch (*f_ptr) { - /* 2 - print Operand 2 of a data processing instruction */ - case '2': - if (insn & 0x02000000) { - int rotate= ((insn >> 7) & 0x1e); - - di->di_printf("#0x%08x", - (insn & 0xff) << (32 - rotate) | - (insn & 0xff) >> rotate); - } else { - disasm_register_shift(di, insn); - } - break; - /* d - destination register (bits 12-15) */ - case 'd': - di->di_printf("r%d", ((insn >> 12) & 0x0f)); - break; - /* D - insert 'p' if Rd is R15 */ - case 'D': - if (((insn >> 12) & 0x0f) == 15) - di->di_printf("p"); - break; - /* n - n register (bits 16-19) */ - case 'n': - di->di_printf("r%d", ((insn >> 16) & 0x0f)); - break; - /* s - s register (bits 8-11) */ - case 's': - di->di_printf("r%d", ((insn >> 8) & 0x0f)); - break; - /* o - indirect register rn (bits 16-19) (used by swap) */ - case 'o': - di->di_printf("[r%d]", ((insn >> 16) & 0x0f)); - break; - /* m - m register (bits 0-4) */ - case 'm': - di->di_printf("r%d", ((insn >> 0) & 0x0f)); - break; - /* a - address operand of ldr/str instruction */ - case 'a': - disasm_insn_ldrstr(di, insn, loc); - break; - /* e - address operand of ldrh/strh instruction */ - case 'e': - disasm_insn_ldrhstrh(di, insn, loc); - break; - /* l - register list for ldm/stm instruction */ - case 'l': - disasm_print_reglist(di, insn); - break; - /* f - 1st fp operand (register) (bits 12-14) */ - case 'f': - di->di_printf("f%d", (insn >> 12) & 7); - break; - /* g - 2nd fp operand (register) (bits 16-18) */ - case 'g': - di->di_printf("f%d", (insn >> 16) & 7); - break; - /* h - 3rd fp operand (register/immediate) (bits 0-4) */ - case 'h': - if (insn & (1 << 3)) - di->di_printf("#%s", insn_fpaimm(insn)); - else - di->di_printf("f%d", insn & 7); - break; - /* b - branch address */ - case 'b': - branch = ((insn << 2) & 0x03ffffff); - if (branch & 0x02000000) - branch |= 0xfc000000; - di->di_printaddr(loc + 8 + branch); - break; - /* t - blx address */ - case 't': - branch = ((insn << 2) & 0x03ffffff) | - (insn >> 23 & 0x00000002); - if (branch & 0x02000000) - branch |= 0xfc000000; - di->di_printaddr(loc + 8 + branch); - break; - /* X - block transfer type */ - case 'X': - di->di_printf("%s", insn_blktrans(insn)); - break; - /* Y - block transfer type (r13 base) */ - case 'Y': - di->di_printf("%s", insn_stkblktrans(insn)); - break; - /* c - comment field bits(0-23) */ - case 'c': - di->di_printf("0x%08x", (insn & 0x00ffffff)); - break; - /* k - breakpoint comment (bits 0-3, 8-19) */ - case 'k': - di->di_printf("0x%04x", - (insn & 0x000fff00) >> 4 | (insn & 0x0000000f)); - break; - /* p - saved or current status register */ - case 'p': - if (insn & 0x00400000) - di->di_printf("spsr"); - else - di->di_printf("cpsr"); - break; - /* F - PSR transfer fields */ - case 'F': - di->di_printf("_"); - if (insn & (1 << 16)) - di->di_printf("c"); - if (insn & (1 << 17)) - di->di_printf("x"); - if (insn & (1 << 18)) - di->di_printf("s"); - if (insn & (1 << 19)) - di->di_printf("f"); - break; - /* B - byte transfer flag */ - case 'B': - if (insn & 0x00400000) - di->di_printf("b"); - break; - /* L - co-processor transfer size */ - case 'L': - if (insn & (1 << 22)) - di->di_printf("l"); - break; - /* S - set status flag */ - case 'S': - if (insn & 0x00100000) - di->di_printf("s"); - break; - /* P - fp precision */ - case 'P': - di->di_printf("%s", insn_fpaprec(insn)); - break; - /* Q - fp precision (for ldf/stf) */ - case 'Q': - break; - /* R - fp rounding */ - case 'R': - di->di_printf("%s", insn_fparnd(insn)); - break; - /* W - writeback flag */ - case 'W': - if (insn & (1 << 21)) - di->di_printf("!"); - break; - /* # - co-processor number */ - case '#': - di->di_printf("p%d", (insn >> 8) & 0x0f); - break; - /* v - co-processor data transfer registers+addressing mode */ - case 'v': - disasm_insn_ldcstc(di, insn, loc); - break; - /* x - instruction in hex */ - case 'x': - di->di_printf("0x%08x", insn); - break; - /* y - co-processor data processing registers */ - case 'y': - di->di_printf("%d, ", (insn >> 20) & 0x0f); - - di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f, - (insn >> 16) & 0x0f, insn & 0x0f); - - di->di_printf(", %d", (insn >> 5) & 0x07); - break; - /* z - co-processor register transfer registers */ - case 'z': - di->di_printf("%d, ", (insn >> 21) & 0x07); - di->di_printf("r%d, c%d, c%d, %d", - (insn >> 12) & 0x0f, (insn >> 16) & 0x0f, - insn & 0x0f, (insn >> 5) & 0x07); - -/* if (((insn >> 5) & 0x07) != 0) - di->di_printf(", %d", (insn >> 5) & 0x07);*/ - break; - default: - di->di_printf("[%c - unknown]", *f_ptr); - break; - } - if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z') - ++f_ptr; - else if (*(++f_ptr)) { - ++fmt; - if (fmt == 1) - di->di_printf("\t"); - else - di->di_printf(", "); - } - }; - - di->di_printf("\n"); - - return(loc + INSN_SIZE); -} - - -static void -disasm_register_shift(const disasm_interface_t *di, u_int insn) -{ - di->di_printf("r%d", (insn & 0x0f)); - if ((insn & 0x00000ff0) == 0) - ; - else if ((insn & 0x00000ff0) == 0x00000060) - di->di_printf(", rrx"); - else { - if (insn & 0x10) - di->di_printf(", %s r%d", op2_shift(insn), - (insn >> 8) & 0x0f); - else - di->di_printf(", %s #%d", op2_shift(insn), - (insn >> 7) & 0x1f); - } -} - - -static void -disasm_print_reglist(const disasm_interface_t *di, u_int insn) -{ - int loop; - int start; - int comma; - - di->di_printf("{"); - start = -1; - comma = 0; - - for (loop = 0; loop < 17; ++loop) { - if (start != -1) { - if (loop == 16 || !(insn & (1 << loop))) { - if (comma) - di->di_printf(", "); - else - comma = 1; - if (start == loop - 1) - di->di_printf("r%d", start); - else - di->di_printf("r%d-r%d", start, loop - 1); - start = -1; - } - } else { - if (insn & (1 << loop)) - start = loop; - } - } - di->di_printf("}"); - - if (insn & (1 << 22)) - di->di_printf("^"); -} - -static void -disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc) -{ - int offset; - - offset = insn & 0xfff; - if ((insn & 0x032f0000) == 0x010f0000) { - /* rA = pc, immediate index */ - if (insn & 0x00800000) - loc += offset; - else - loc -= offset; - di->di_printaddr(loc + 8); - } else { - di->di_printf("[r%d", (insn >> 16) & 0x0f); - if ((insn & 0x03000fff) != 0x01000000) { - di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); - if (!(insn & 0x00800000)) - di->di_printf("-"); - if (insn & (1 << 25)) - disasm_register_shift(di, insn); - else - di->di_printf("#0x%03x", offset); - } - if (insn & (1 << 24)) - di->di_printf("]"); - } -} - -static void -disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc) -{ - int offset; - - offset = ((insn & 0xf00) >> 4) | (insn & 0xf); - if ((insn & 0x004f0000) == 0x004f0000) { - /* rA = pc, immediate index */ - if (insn & 0x00800000) - loc += offset; - else - loc -= offset; - di->di_printaddr(loc + 8); - } else { - di->di_printf("[r%d", (insn >> 16) & 0x0f); - if ((insn & 0x01400f0f) != 0x01400000) { - di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); - if (!(insn & 0x00800000)) - di->di_printf("-"); - if (insn & (1 << 22)) - di->di_printf("#0x%02x", offset); - else - di->di_printf("r%d", (insn & 0x0f)); - } - if (insn & (1 << 24)) - di->di_printf("]"); - } -} - -static void -disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) -{ - if (((insn >> 8) & 0xf) == 1) - di->di_printf("f%d, ", (insn >> 12) & 0x07); - else - di->di_printf("c%d, ", (insn >> 12) & 0x0f); - - di->di_printf("[r%d", (insn >> 16) & 0x0f); - - di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); - - if (!(insn & (1 << 23))) - di->di_printf("-"); - - di->di_printf("#0x%03x", (insn & 0xff) << 2); - - if (insn & (1 << 24)) - di->di_printf("]"); - - if (insn & (1 << 21)) - di->di_printf("!"); -} - -static u_int -disassemble_readword(u_int address) -{ - return(*((u_int *)address)); -} - -static void -disassemble_printaddr(u_int address) -{ - printf("0x%08x", address); -} - -static void -disassemble_printf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); -} - -static const disasm_interface_t disassemble_di = { - disassemble_readword, disassemble_printaddr, disassemble_printf -}; - -void -disassemble(u_int address) -{ - - (void)disasm(&disassemble_di, address, 0); -} - -/* End of disassem.c */ diff --git a/libacc/tests/disassem.h b/libacc/tests/disassem.h deleted file mode 100644 index 02747cd068f..00000000000 --- a/libacc/tests/disassem.h +++ /dev/null @@ -1,65 +0,0 @@ -/* $NetBSD: disassem.h,v 1.4 2001/03/04 04:15:58 matt Exp $ */ - -/*- - * Copyright (c) 1997 Mark Brinicombe. - * Copyright (c) 1997 Causality Limited. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Mark Brinicombe. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Define the interface structure required by the disassembler. - * - * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/disassem.h,v 1.2 2005/01/05 21:58:48 imp Exp $ - */ - -#ifndef ANDROID_MACHINE_DISASSEM_H -#define ANDROID_MACHINE_DISASSEM_H - -#include - -#if __cplusplus -extern "C" { -#endif - -typedef struct { - u_int (*di_readword)(u_int); - void (*di_printaddr)(u_int); - void (*di_printf)(const char *, ...); -} disasm_interface_t; - -/* Prototypes for callable functions */ - -u_int disasm(const disasm_interface_t *, u_int, int); -void disassemble(u_int); - -#if __cplusplus -} -#endif - -#endif /* !ANDROID_MACHINE_DISASSEM_H */ diff --git a/libacc/tests/main.cpp b/libacc/tests/main.cpp deleted file mode 100644 index e4e386fc2b9..00000000000 --- a/libacc/tests/main.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Android "Almost" C Compiler. - * This is a compiler for a small subset of the C language, intended for use - * in scripting environments where speed and memory footprint are important. - * - * This code is based upon the "unobfuscated" version of the - * Obfuscated Tiny C compiler, see the file LICENSE for details. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#if defined(__arm__) -#include -#endif - -#if defined(__arm__) -#define PROVIDE_ARM_DISASSEMBLY -#endif - -#ifdef PROVIDE_ARM_DISASSEMBLY -#include "disassem.h" -#endif - -#include - - -typedef int (*MainPtr)(int, char**); -// This is a separate function so it can easily be set by breakpoint in gdb. -int run(MainPtr mainFunc, int argc, char** argv) { - return mainFunc(argc, argv); -} - -ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) { - return (ACCvoid*) dlsym(RTLD_DEFAULT, name); -} - -#ifdef PROVIDE_ARM_DISASSEMBLY - -static FILE* disasmOut; - -static u_int -disassemble_readword(u_int address) -{ - return(*((u_int *)address)); -} - -static void -disassemble_printaddr(u_int address) -{ - fprintf(disasmOut, "0x%08x", address); -} - -static void -disassemble_printf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(disasmOut, fmt, ap); - va_end(ap); -} - -static int disassemble(ACCscript* script, FILE* out) { - disasmOut = out; - disasm_interface_t di; - di.di_readword = disassemble_readword; - di.di_printaddr = disassemble_printaddr; - di.di_printf = disassemble_printf; - - ACCvoid* base; - ACCsizei length; - - accGetProgramBinary(script, &base, &length); - unsigned long* pBase = (unsigned long*) base; - unsigned long* pEnd = (unsigned long*) (((unsigned char*) base) + length); - - for(unsigned long* pInstruction = pBase; pInstruction < pEnd; pInstruction++) { - fprintf(out, "%08x: %08x ", (int) pInstruction, (int) *pInstruction); - ::disasm(&di, (uint) pInstruction, 0); - } - return 0; -} - -#endif // PROVIDE_ARM_DISASSEMBLY - -int main(int argc, char** argv) { - const char* inFile = NULL; - bool printListing; - bool runResults = false; - FILE* in = stdin; - int i; - for (i = 1; i < argc; i++) { - char* arg = argv[i]; - if (arg[0] == '-') { - switch (arg[1]) { - case 'S': - printListing = true; - break; - case 'R': - runResults = true; - break; - default: - fprintf(stderr, "Unrecognized flag %s\n", arg); - return 3; - } - } else if (inFile == NULL) { - inFile = arg; - } else { - break; - } - } - - if (! inFile) { - fprintf(stderr, "input file required\n"); - return 2; - } - - if (inFile) { - in = fopen(inFile, "r"); - if (!in) { - fprintf(stderr, "Could not open input file %s\n", inFile); - return 1; - } - } - - fseek(in, 0, SEEK_END); - size_t fileSize = (size_t) ftell(in); - rewind(in); - ACCchar* text = new ACCchar[fileSize + 1]; - size_t bytesRead = fread(text, 1, fileSize, in); - if (bytesRead != fileSize) { - fprintf(stderr, "Could not read all of file %s\n", inFile); - } - - text[fileSize] = '\0'; - - ACCscript* script = accCreateScript(); - - const ACCchar* scriptSource[] = {text}; - accScriptSource(script, 1, scriptSource, NULL); - delete[] text; - - accRegisterSymbolCallback(script, symbolLookup, NULL); - - accCompileScript(script); - int result = accGetError(script); - MainPtr mainPointer = 0; - if (result != 0) { - ACCsizei bufferLength; - accGetScriptInfoLog(script, 0, &bufferLength, NULL); - char* buf = (char*) malloc(bufferLength + 1); - if (buf != NULL) { - accGetScriptInfoLog(script, bufferLength + 1, NULL, buf); - fprintf(stderr, "%s", buf); - free(buf); - } else { - fprintf(stderr, "Out of memory.\n"); - } - goto exit; - } - - { - ACCsizei numPragmaStrings; - accGetPragmas(script, &numPragmaStrings, 0, NULL); - if (numPragmaStrings) { - char** strings = new char*[numPragmaStrings]; - accGetPragmas(script, NULL, numPragmaStrings, strings); - for(ACCsizei i = 0; i < numPragmaStrings; i += 2) { - fprintf(stderr, "#pragma %s(%s)\n", strings[i], strings[i+1]); - } - delete[] strings; - } - } - - if (printListing) { -#ifdef PROVIDE_ARM_DISASSEMBLY - disassemble(script, stderr); -#endif - } - - if (runResults) { - accGetScriptLabel(script, "main", (ACCvoid**) & mainPointer); - - result = accGetError(script); - if (result != ACC_NO_ERROR) { - fprintf(stderr, "Could not find main: %d\n", result); - } else { - fprintf(stderr, "Executing compiled code:\n"); - int codeArgc = argc - i + 1; - char** codeArgv = argv + i - 1; - codeArgv[0] = (char*) (inFile ? inFile : "stdin"); - result = run(mainPointer, codeArgc, codeArgv); - fprintf(stderr, "result: %d\n", result); - } - } - -exit: - - accDeleteScript(script); - - return result; -} diff --git a/libacc/tests/runtimeTest.cpp b/libacc/tests/runtimeTest.cpp deleted file mode 100644 index 55bf877ed38..00000000000 --- a/libacc/tests/runtimeTest.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * RuntimeTest for ACC compiler. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#if defined(__arm__) -#include -#endif - -#include - - -typedef void (*ScriptPtr)(); - -// This is a separate function so it can easily be set by breakpoint in gdb. -void run(ScriptPtr scriptFn) { - scriptFn(); -} - -// Private API for development: - -extern "C" -void accDisassemble(ACCscript* script); - -int globalVar; - -void op_int(int a) { - printf("op_int(%d)\n", a); -} - -void op_float12(float a, float b, float c, float d, - float e, float f, float g, float h, - float i, float j, float k, float l) { - printf("op_float12(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g)\n", - a, b, c, d, e, f, g, h, i, j, k, l); -} - -const char* text = "void op_int(int a);\n" - "void op_float12(float a, float b, float c, float d,\n" - " float e, float f, float g, float h,\n" - " float i, float j, float k, float l);\n" - "void script() {\n" - " globalVar += 3;\n" - " op_int(123);\n" - " op_float12(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0);\n" - "}\n"; - -ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) { - if (strcmp("op_int", name) == 0) { - return (ACCvoid*) op_int; - } - if (strcmp("op_float12", name) == 0) { - return (ACCvoid*) op_float12; - } - if (strcmp("globalVar", name) == 0) { - return (ACCvoid*) &globalVar; - } - return (ACCvoid*) dlsym(RTLD_DEFAULT, name); -} - -int main(int argc, char** argv) { - ACCscript* script = accCreateScript(); - - accRegisterSymbolCallback(script, symbolLookup, NULL); - - const ACCchar* scriptSource[] = {text}; - accScriptSource(script, 1, scriptSource, NULL); - - accCompileScript(script); - int result = accGetError(script); - ScriptPtr scriptPointer = 0; - if (result != 0) { - char buf[1024]; - accGetScriptInfoLog(script, sizeof(buf), NULL, buf); - fprintf(stderr, "%s", buf); - goto exit; - } - - { - ACCsizei numPragmaStrings; - accGetPragmas(script, &numPragmaStrings, 0, NULL); - if (numPragmaStrings) { - char** strings = new char*[numPragmaStrings]; - accGetPragmas(script, NULL, numPragmaStrings, strings); - for(ACCsizei i = 0; i < numPragmaStrings; i += 2) { - fprintf(stderr, "#pragma %s(%s)\n", strings[i], strings[i+1]); - } - delete[] strings; - } - } - - accGetScriptLabel(script, "script", (ACCvoid**) & scriptPointer); - - result = accGetError(script); - if (result != ACC_NO_ERROR) { - fprintf(stderr, "Could not find script: %d\n", result); - } else { - fprintf(stderr, "Executing script:\n"); - globalVar = 17; - run(scriptPointer); - fprintf(stderr, "After script globalVar = %d\n", globalVar); - } - - -exit: - - accDeleteScript(script); - - return result; -} diff --git a/libacc/tests/test b/libacc/tests/test deleted file mode 100755 index 8fd6916076c..00000000000 --- a/libacc/tests/test +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -SCRIPT_DIR=`dirname $BASH_SOURCE` -cd $SCRIPT_DIR -python test.py "$@" - diff --git a/libacc/tests/test.py b/libacc/tests/test.py deleted file mode 100644 index d9843011168..00000000000 --- a/libacc/tests/test.py +++ /dev/null @@ -1,493 +0,0 @@ -# -# Test the acc compiler - -import unittest -import subprocess -import os -import sys - -gArmInitialized = False -gUseArm = True -gUseX86 = True -gRunOTCCOutput = True - - -def parseArgv(): - global gUseArm - global gUseX86 - global gRunOTCCOutput - for arg in sys.argv[1:]: - if arg == "--noarm": - print "--noarm: not testing ARM" - gUseArm = False - elif arg == "--nox86": - print "--nox86: not testing x86" - gUseX86 = False - elif arg == "--norunotcc": - print "--norunotcc detected, not running OTCC output" - gRunOTCCOutput = False - else: - print "Unknown parameter: ", arg - raise "Unknown parameter" - sys.argv = sys.argv[0:1] - -def compile(args): - proc = subprocess.Popen(["acc"] + args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - result = proc.communicate() - return result - -def runCmd(args): - proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - result = proc.communicate() - return result[0].strip() - -def uname(): - return runCmd(["uname"]) - -def unameM(): - return runCmd(["uname", "-m"]) - -def which(item): - return runCmd(["which", item]) - -def fileType(item): - return runCmd(["file", item]) - -def outputCanRun(): - ft = fileType(which("acc")) - return ft.find("ELF 32-bit LSB executable, Intel 80386") >= 0 - -def checkEnvironment(): - global gRunOTCCOutput - gRunOTCCOutput = uname() == "Linux" and unameM() != "x86_64" and outputCanRun() - -def adb(args): - return runCmd(["adb"] + args) - -def setupArm(): - global gArmInitialized - if gArmInitialized: - return - print "Setting up arm" - adb(["remount"]) - adb(["shell", "rm", "/system/bin/acc"]) - adb(["shell", "mkdir", "/system/bin/accdata"]) - adb(["shell", "mkdir", "/system/bin/accdata/data"]) - # Clear out old data TODO: handle recursion - adb(["shell", "rm", "/system/bin/accdata/data/*"]) - # Copy over data - for root, dirs, files in os.walk("data"): - for d in dirs: - adb(["shell", "mkdir", os.path.join(root, d)]) - for f in files: - adb(["push", os.path.join(root, f), os.path.join("/system/bin/accdata", root, f)]) - # Copy over compiler - adb(["sync"]) - gArmInitialized = True - -def compileArm(args): - setupArm() - proc = subprocess.Popen(["adb", "shell", "/system/bin/acc"] + args, stdout=subprocess.PIPE) - result = proc.communicate() - return result[0].replace("\r","") - -def compare(a, b): - if a != b: - firstDiff = firstDifference(a, b) - print "Strings differ at character %d. Common: %s. Difference '%s' != '%s'" % ( - firstDiff, a[0:firstDiff], safeAccess(a, firstDiff), safeAccess(b, firstDiff)) - -def safeAccess(s, i): - if 0 <= i < len(s): - return s[i] - else: - return '?' - -def firstDifference(a, b): - commonLen = min(len(a), len(b)) - for i in xrange(0, commonLen): - if a[i] != b[i]: - return i - return commonLen - -# a1 and a2 are the expected stdout and stderr. -# b1 and b2 are the actual stdout and stderr. -# Compare the two, sets. Allow any individual line -# to appear in either stdout or stderr. This is because -# the way we obtain output on the ARM combines both -# streams into one sequence. - -def compareOuput(a1,a2,b1,b2): - while True: - totalLen = len(a1) + len(a2) + len(b1) + len(b2) - a1, b1 = matchCommon(a1, b1) - a1, b2 = matchCommon(a1, b2) - a2, b1 = matchCommon(a2, b1) - a2, b2 = matchCommon(a2, b2) - newTotalLen = len(a1) + len(a2) + len(b1) + len(b2) - if newTotalLen == 0: - return True - if newTotalLen == totalLen: - print "Failed at %d %d %d %d" % (len(a1), len(a2), len(b1), len(b2)) - print "a1", a1 - print "a2", a2 - print "b1", b1 - print "b2", b2 - return False - -def matchCommon(a, b): - """Remove common items from the beginning of a and b, - return just the tails that are different.""" - while len(a) > 0 and len(b) > 0 and a[0] == b[0]: - a = a[1:] - b = b[1:] - return a, b - -def rewritePaths(args): - return [rewritePath(x) for x in args] - -def rewritePath(p): - """Take a path that's correct on the x86 and convert to a path - that's correct on ARM.""" - if p.startswith("data/"): - p = "/system/bin/accdata/" + p - return p - -class TestACC(unittest.TestCase): - - def checkResult(self, out, err, stdErrResult, stdOutResult=""): - a1 = out.splitlines() - a2 = err.splitlines() - b2 = stdErrResult.splitlines() - b1 = stdOutResult.splitlines() - self.assertEqual(True, compareOuput(a1,a2,b1,b2)) - - def compileCheck(self, args, stdErrResult, stdOutResult="", - targets=['arm', 'x86']): - global gUseArm - global gUseX86 - targetSet = frozenset(targets) - if gUseX86 and 'x86' in targetSet: - out, err = compile(args) - self.checkResult(out, err, stdErrResult, stdOutResult) - if gUseArm and 'arm' in targetSet: - out = compileArm(rewritePaths(args)) - self.checkResult(out, "", stdErrResult, stdOutResult) - - def compileCheckArm(self, args, result): - self.assertEqual(compileArm(args), result) - - def testCompileReturnVal(self): - self.compileCheck(["data/returnval-ansi.c"], "") - - def testCompileOTCCANSII(self): - self.compileCheck(["data/otcc-ansi.c"], "", "", ['x86']) - - def testRunReturnVal(self): - self.compileCheck(["-R", "data/returnval-ansi.c"], - "Executing compiled code:\nresult: 42\n") - - def testStringLiteralConcatenation(self): - self.compileCheck(["-R", "data/testStringConcat.c"], - "Executing compiled code:\nresult: 13\n", "Hello, world\n") - - def testRunOTCCANSI(self): - global gRunOTCCOutput - if gRunOTCCOutput: - self.compileCheck(["-R", "data/otcc-ansi.c", "data/returnval.c"], - "Executing compiled code:\notcc-ansi.c: About to execute compiled code:\natcc-ansi.c: result: 42\nresult: 42\n", "", - ['x86']) - - def testRunOTCCANSI2(self): - global gRunOTCCOutput - if gRunOTCCOutput: - self.compileCheck(["-R", "data/otcc-ansi.c", "data/otcc.c", "data/returnval.c"], - "Executing compiled code:\notcc-ansi.c: About to execute compiled code:\notcc.c: about to execute compiled code.\natcc-ansi.c: result: 42\nresult: 42\n", "",['x86']) - - def testRunConstants(self): - self.compileCheck(["-R", "data/constants.c"], - "Executing compiled code:\nresult: 12\n", - "0 = 0\n010 = 8\n0x10 = 16\n'\\a' = 7\n'\\b' = 8\n'\\f' = 12\n'\\n' = 10\n'\\r' = 13\n'\\t' = 9\n'\\v' = 11\n'\\\\' = 92\n'\\'' = 39\n" + - "'\\\"' = 34\n'\\?' = 63\n'\\0' = 0\n'\\1' = 1\n'\\12' = 10\n'\\123' = 83\n'\\x0' = 0\n'\\x1' = 1\n'\\x12' = 18\n'\\x123' = 291\n'\\x1f' = 31\n'\\x1F' = 31\n") - - def testRunFloat(self): - self.compileCheck(["-R", "data/float.c"], - "Executing compiled code:\nresult: 0\n", - """Constants: 0 0 0 0.01 0.01 0.1 10 10 0.1 -int: 1 float: 2.2 double: 3.3 - ftoi(1.4f)=1 - dtoi(2.4)=2 - itof(3)=3 - itod(4)=4 -globals: 1 2 3 4 -args: 1 2 3 4 -locals: 1 2 3 4 -cast rval: 2 4 -cast lval: 1.1 2 3.3 4 -""") - - def testRunFlops(self): - self.compileCheck(["-R", "data/flops.c"], - """Executing compiled code: -result: 0""", -"""-1.1 = -1.1 -!1.2 = 0 -!0 = 1 -double op double: -1 + 2 = 3 -1 - 2 = -1 -1 * 2 = 2 -1 / 2 = 0.5 -float op float: -1 + 2 = 3 -1 - 2 = -1 -1 * 2 = 2 -1 / 2 = 0.5 -double op float: -1 + 2 = 3 -1 - 2 = -1 -1 * 2 = 2 -1 / 2 = 0.5 -double op int: -1 + 2 = 3 -1 - 2 = -1 -1 * 2 = 2 -1 / 2 = 0.5 -int op double: -1 + 2 = 3 -1 - 2 = -1 -1 * 2 = 2 -1 / 2 = 0.5 -double op double: -1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 -1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 -2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 -double op float: -1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 -1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 -2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 -float op float: -1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 -1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 -2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 -int op double: -1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 -1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 -2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 -double op int: -1 op 2: < 1 <= 1 == 0 >= 0 > 0 != 1 -1 op 1: < 0 <= 1 == 1 >= 1 > 0 != 0 -2 op 1: < 0 <= 0 == 0 >= 1 > 1 != 1 -branching: 1 0 1 -testpassi: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassf: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassd: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassi: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassf: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassd: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassi: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassf: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassd: 1 2 3 4 5 6 7 8 9 10 11 12 -testpassidf: 1 2 3 -""") - def testCasts(self): - self.compileCheck(["-R", "data/casts.c"], - """Executing compiled code: -result: 0""", """Reading from a pointer: 3 3 -Writing to a pointer: 4 -Testing casts: 3 3 4.5 4 -Testing reading (int*): 4 -Testing writing (int*): 8 9 -Testing reading (char*): 0x78 0x56 0x34 0x12 -Testing writing (char*): 0x87654321 -f(10) -Function pointer result: 70 -Testing read/write (float*): 8.8 9.9 -Testing read/write (double*): 8.8 9.9 -""") - - def testChar(self): - self.compileCheck(["-R", "data/char.c"], """Executing compiled code: -result: 0""", """a = 99, b = 41 -ga = 100, gb = 44""") - - def testPointerArithmetic(self): - self.compileCheck(["-R", "data/pointers.c"], """Executing compiled code: -result: 0""", """Pointer difference: 1 4 -Pointer addition: 2 -Pointer comparison to zero: 0 0 1 -Pointer comparison: 1 0 0 0 1 -""") - def testRollo3(self): - self.compileCheck(["-R", "data/rollo3.c"], """Executing compiled code: -result: 10""", """""") - - def testFloatDouble(self): - self.compileCheck(["-R", "data/floatdouble.c"], """Executing compiled code: -result: 0""", """0.002 0.1 10""") - - def testIncDec(self): - self.compileCheck(["-R", "data/inc.c"], """Executing compiled code: -0 -1 -2 -1 -1 -2 -1 -0 -result: 0 -""","""""") - - def testIops(self): - self.compileCheck(["-R", "data/iops.c"], """Executing compiled code: -result: 0""", """Literals: 1 -1 -++ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 --- -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 -0 -""") - - def testFilm(self): - self.compileCheck(["-R", "data/film.c"], """Executing compiled code: -result: 0""", """testing... -Total bad: 0 -""") - - def testpointers2(self): - self.compileCheck(["-R", "data/pointers2.c"], """Executing compiled code: -result: 0""", """a = 0, *pa = 0 -a = 2, *pa = 2 -a = 0, *pa = 0 **ppa = 0 -a = 2, *pa = 2 **ppa = 2 -a = 0, *pa = 0 **ppa = 0 -a = 2, *pa = 2 **ppa = 2 -""") - - def testassignmentop(self): - self.compileCheck(["-R", "data/assignmentop.c"], """Executing compiled code: -result: 0""", """2 *= 5 10 -20 /= 5 4 -17 %= 5 2 -17 += 5 22 -17 -= 5 12 -17<<= 1 34 -17>>= 1 8 -17&= 1 1 -17^= 1 16 -16|= 1 17 -*f() = *f() + 10; -f() -f() -a = 10 -*f() += 10; -f() -a = 10 -""") - - def testcomma(self): - self.compileCheck(["-R", "data/comma.c"], """Executing compiled code: -result: 0""", """statement: 10 -if: a = 0 -while: b = 11 -for: b = 22 -return: 30 -arg: 12 -""") - - def testBrackets(self): - self.compileCheck(["-R", "data/brackets.c"], """Executing compiled code: -Errors: 0 -2D Errors: 0 -result: 0 -""","""""") - - def testShort(self): - self.compileCheck(["-R", "data/short.c"], """Executing compiled code: -result: -2 -""","""""") - - def testAssignment(self): - self.compileCheck(["-R", "data/assignment.c"], """Executing compiled code: -result: 7 -""","""""") - - def testArray(self): - self.compileCheck(["-R", "data/array.c"], """Executing compiled code: -localInt: 3 -localDouble: 3 3 -globalChar: 3 -globalDouble: 3 -testArgs: 0 2 4 -testDecay: Hi! -test2D: -abcdefghijdefghijklm -defghijklmghijklmnop -ghijklmnopjklmnopabc -jklmnopabcmnopabcdef -mnopabcdefpabcdefghi -pabcdefghicdefghijkl -cdefghijklfghijklmno -fghijklmnoijklmnopab -ijklmnopablmnopabcde -lmnopabcdefghijklmno -result: 0 -""","""""") - - def testDefines(self): - self.compileCheck(["-R", "data/defines.c"], """Executing compiled code: -result: 3 -""","""""") - - def testFuncArgs(self): - self.compileCheck(["-R", "data/funcargs.c"], """Executing compiled code: -result: 4 -""","""""") - - def testB2071670(self): - self.compileCheck(["-R", "data/b2071670.c"], """Executing compiled code: -result: 1092616192 -""","""""") - - def testStructs(self): - self.compileCheck(["-R", "data/structs.c"], """Executing compiled code: -testCopying: 37 == 37 -testUnion: 1 == 0x3f800000 -testArgs: (6, 8, 10, 12) -result: 6 -""","""""") - - def testAddressOf(self): - self.compileCheck(["-R", "data/addressOf.c"], """Executing compiled code: -testStruct: 10 10 10 -testArray: 1 1 1 -result: 0 -""","""""") - -def main(): - checkEnvironment() - parseArgv() - unittest.main() - -if __name__ == '__main__': - main() - diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 1d1e57603de..e7ed6ccd81c 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -16,10 +16,17 @@ LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) +ifeq ($(TARGET_CPU_SMP),true) + targetSmpFlag := -DANDROID_SMP=1 +else + targetSmpFlag := -DANDROID_SMP=0 +endif +hostSmpFlag := -DANDROID_SMP=0 + commonSources := \ array.c \ hashmap.c \ - atomic.c \ + atomic.c.arm \ native_handle.c \ buffer.c \ socket_inaddr_any_server.c \ @@ -28,16 +35,21 @@ commonSources := \ socket_loopback_client.c \ socket_loopback_server.c \ socket_network_client.c \ + sockets.c \ config_utils.c \ cpu_info.c \ load_file.c \ + list.c \ + open_memstream.c \ strdup16to8.c \ strdup8to16.c \ record_stream.c \ process_name.c \ properties.c \ threads.c \ - sched_policy.c + sched_policy.c \ + iosched_policy.c \ + str_parms.c commonHostSources := \ ashmem-host.c @@ -65,7 +77,6 @@ else mspace.c \ selector.c \ tztime.c \ - adb_networking.c \ zygote.c commonHostSources += \ @@ -76,45 +87,58 @@ endif # Static library for host # ======================================================== LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) +LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS += $(hostSmpFlag) include $(BUILD_HOST_STATIC_LIBRARY) -ifeq ($(TARGET_SIMULATOR),true) - -# Shared library for simulator -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c -LOCAL_LDLIBS := -lpthread -LOCAL_SHARED_LIBRARIES := liblog -include $(BUILD_SHARED_LIBRARY) - -else #!sim - # Shared and static library for target # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c +LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c android_reboot.c partition_utils.c uevent.c qtaguid.c klog.c ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += memset32.S atomic-android-arm.S +LOCAL_SRC_FILES += arch-arm/memset32.S else # !arm +ifeq ($(TARGET_ARCH),sh) +LOCAL_SRC_FILES += memory.c atomic-android-sh.c +else # !sh +ifeq ($(TARGET_ARCH_VARIANT),x86-atom) +LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c +else # !x86-atom LOCAL_SRC_FILES += memory.c +endif # !x86-atom +endif # !sh endif # !arm +ifneq ($(TARGET_RECOVERY_PRE_COMMAND),) + LOCAL_CFLAGS += -DRECOVERY_PRE_COMMAND='$(TARGET_RECOVERY_PRE_COMMAND)' +endif + +ifeq ($(TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON),true) + LOCAL_CFLAGS += -DRECOVERY_PRE_COMMAND_CLEAR_REASON +endif + LOCAL_C_INCLUDES := $(KERNEL_HEADERS) LOCAL_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libcutils LOCAL_WHOLE_STATIC_LIBRARIES := libcutils LOCAL_SHARED_LIBRARIES := liblog +LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_SHARED_LIBRARY) -endif #!sim +include $(CLEAR_VARS) +LOCAL_MODULE := tst_str_parms +LOCAL_CFLAGS += -DTEST_STR_PARMS +LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) diff --git a/libcutils/adb_networking.c b/libcutils/adb_networking.c deleted file mode 100644 index d819d44c8de..00000000000 --- a/libcutils/adb_networking.c +++ /dev/null @@ -1,172 +0,0 @@ -/* libs/utils/adb_networking.c -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define ADB_PORT 5037 - -#define _GNU_SOURCE /* for asprintf */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define ADB_RESPONSE_SIZE 4 - -/** - * Unfortunately, java.net.Socket wants to create it's filedescriptor early - * So, this function takes an fd that must be an unconnected - * PF_LOCAL SOCK_STREAM - */ -int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address) -{ - struct sockaddr_in local_addr; - socklen_t alen; - char *cmd; - char buf[ADB_RESPONSE_SIZE + 1]; - ssize_t count_read; - int ret; - int err; - /* for impl of inet_ntoa below*/ - union { - uint8_t b[4]; - uint32_t l; - } a; - - /* First, connect to adb */ - - memset(&local_addr, 0, sizeof(local_addr)); - local_addr.sin_family = AF_INET; - local_addr.sin_port = htons(ADB_PORT); - local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - do { - err = connect(fd, (struct sockaddr *) &local_addr, sizeof(local_addr)); - } while (err < 0 && errno == EINTR); - - if (err < 0) { - return -1; - } - - a.l = p_address->sin_addr.s_addr; - - // compose the command - asprintf(&cmd, "tcp:%u:%u.%u.%u.%u", - (unsigned int)ntohs(p_address->sin_port), - a.b[0],a.b[1],a.b[2],a.b[3]); - - // buf is now the ascii hex length of cmd - snprintf(buf, sizeof(buf), "%04X", strlen(cmd)); - - // write the 4-byte length - do { - err = write(fd, buf, 4); - } while (err < 0 && errno == EINTR); - - // write the command - do { - err = write(fd, cmd, strlen(cmd)); - } while (err < 0 && errno == EINTR); - - // read the result - do { - count_read = read(fd, buf, sizeof(buf) - 1); - } while (count_read < 0 && errno != EINTR); - - if (count_read == ADB_RESPONSE_SIZE - && 0 == strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) { - ret = 0; - } else { - /* what errno here? s_addr), sizeof(p_out_addr->s_addr)); - } while (count_read < 0 && errno != EINTR); - - if (count_read != 4) { - goto error; - } - - free(cmd); - close(fd); - return 0; -error: - free(cmd); - close(fd); - memset(p_out_addr, 0, sizeof(struct in_addr)); - return -1; -} - diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c new file mode 100644 index 00000000000..ab29d513ff0 --- /dev/null +++ b/libcutils/android_reboot.c @@ -0,0 +1,155 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Check to see if /proc/mounts contains any writeable filesystems + * backed by a block device. + * Return true if none found, else return false. + */ +static int remount_ro_done(void) +{ + FILE *f; + char mount_dev[256]; + char mount_dir[256]; + char mount_type[256]; + char mount_opts[256]; + int mount_freq; + int mount_passno; + int match; + int found_rw_fs = 0; + + f = fopen("/proc/mounts", "r"); + if (! f) { + /* If we can't read /proc/mounts, just give up */ + return 1; + } + + do { + match = fscanf(f, "%255s %255s %255s %255s %d %d\n", + mount_dev, mount_dir, mount_type, + mount_opts, &mount_freq, &mount_passno); + mount_dev[255] = 0; + mount_dir[255] = 0; + mount_type[255] = 0; + mount_opts[255] = 0; + if ((match == 6) && !strncmp(mount_dev, "/dev/block", 10) && strstr(mount_opts, "rw")) { + found_rw_fs = 1; + break; + } + } while (match != EOF); + + fclose(f); + + return !found_rw_fs; +} + +/* Remounting filesystems read-only is difficult when there are files + * opened for writing or pending deletes on the filesystem. There is + * no way to force the remount with the mount(2) syscall. The magic sysrq + * 'u' command does an emergency remount read-only on all writable filesystems + * that have a block device (i.e. not tmpfs filesystems) by calling + * emergency_remount(), which knows how to force the remount to read-only. + * Unfortunately, that is asynchronous, and just schedules the work and + * returns. The best way to determine if it is done is to read /proc/mounts + * repeatedly until there are no more writable filesystems mounted on + * block devices. + */ +static void remount_ro(void) +{ + int fd, cnt = 0; + + /* Trigger the remount of the filesystems as read-only, + * which also marks them clean. + */ + fd = open("/proc/sysrq-trigger", O_WRONLY); + if (fd < 0) { + return; + } + write(fd, "u", 1); + close(fd); + + + /* Now poll /proc/mounts till it's done */ + while (!remount_ro_done() && (cnt < 50)) { + usleep(100000); + cnt++; + } + + return; +} + + +int android_reboot(int cmd, int flags, char *arg) +{ + int ret = 0; + int reason = -1; + +#ifdef RECOVERY_PRE_COMMAND + if (cmd == (int) ANDROID_RB_RESTART2) { + if (arg && strlen(arg) > 0) { + char cmd[PATH_MAX]; + sprintf(cmd, RECOVERY_PRE_COMMAND " %s", arg); + system(cmd); + } + } +#endif + + if (!(flags & ANDROID_RB_FLAG_NO_SYNC)) + sync(); + + if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO)) + remount_ro(); + + switch (cmd) { + case ANDROID_RB_RESTART: + reason = RB_AUTOBOOT; + break; + + case ANDROID_RB_POWEROFF: + ret = reboot(RB_POWER_OFF); + return ret; + + case ANDROID_RB_RESTART2: + // REBOOT_MAGIC + break; + + default: + return -1; + } + +#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON + reason = RB_AUTOBOOT; +#endif + + if (reason != -1) + ret = reboot(reason); + else + ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, arg); + + return ret; +} + diff --git a/libcutils/memset32.S b/libcutils/arch-arm/memset32.S similarity index 100% rename from libcutils/memset32.S rename to libcutils/arch-arm/memset32.S diff --git a/vold/volmgr_ext3.h b/libcutils/arch-x86/android_memset16.S similarity index 66% rename from vold/volmgr_ext3.h rename to libcutils/arch-x86/android_memset16.S index bfe882a338e..b1f09cba30b 100644 --- a/vold/volmgr_ext3.h +++ b/libcutils/arch-x86/android_memset16.S @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* + * Contributed by: Intel Corporation + */ + +#if defined(USE_SSE2) + +# include "cache_wrapper.S" +# undef __i686 +# define USE_AS_ANDROID +# define sse2_memset16_atom android_memset16 +# include "sse2-memset16-atom.S" -#ifndef _VOLMGR_EXT3_H -#define _VOLMGR_EXT3_H +#else -#include "volmgr.h" -#include "blkdev.h" +# include "memset16.S" -int ext_identify(blkdev_t *blkdev); -int ext_check(blkdev_t *blkdev); -int ext_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode); #endif diff --git a/vold/volmgr_vfat.h b/libcutils/arch-x86/android_memset32.S similarity index 66% rename from vold/volmgr_vfat.h rename to libcutils/arch-x86/android_memset32.S index d9cf04d9b9b..1fb2ffefe82 100644 --- a/vold/volmgr_vfat.h +++ b/libcutils/arch-x86/android_memset32.S @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* + * Contributed by: Intel Corporation + */ -#ifndef _VOLMGR_VFAT_H -#define _VOLMGR_VFAT_H +#if defined(USE_SSE2) -#include "volmgr.h" -#include "blkdev.h" +# include "cache_wrapper.S" +# undef __i686 +# define USE_AS_ANDROID +# define sse2_memset32_atom android_memset32 +# include "sse2-memset32-atom.S" +#else +# include "memset32.S" -int vfat_identify(blkdev_t *blkdev); -int vfat_check(blkdev_t *blkdev); -int vfat_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode); #endif + diff --git a/libcutils/arch-x86/cache_wrapper.S b/libcutils/arch-x86/cache_wrapper.S new file mode 100644 index 00000000000..508fdd3e2a6 --- /dev/null +++ b/libcutils/arch-x86/cache_wrapper.S @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Contributed by: Intel Corporation + */ + +/* Values are optimized for Atom */ +#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */ +#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */ +#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2) +#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2) diff --git a/libcutils/arch-x86/sse2-memset16-atom.S b/libcutils/arch-x86/sse2-memset16-atom.S new file mode 100644 index 00000000000..cafec8287c5 --- /dev/null +++ b/libcutils/arch-x86/sse2-memset16-atom.S @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Contributed by: Intel Corporation + */ + +#ifndef L +# define L(label) .L##label +#endif + +#ifndef ALIGN +# define ALIGN(n) .p2align n +#endif + +#ifndef cfi_startproc +# define cfi_startproc .cfi_startproc +#endif + +#ifndef cfi_endproc +# define cfi_endproc .cfi_endproc +#endif + +#ifndef cfi_rel_offset +# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off +#endif + +#ifndef cfi_restore +# define cfi_restore(reg) .cfi_restore reg +#endif + +#ifndef cfi_adjust_cfa_offset +# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off +#endif + +#ifndef ENTRY +# define ENTRY(name) \ + .type name, @function; \ + .globl name; \ + .p2align 4; \ +name: \ + cfi_startproc +#endif + +#ifndef END +# define END(name) \ + cfi_endproc; \ + .size name, .-name +#endif + +#define CFI_PUSH(REG) \ + cfi_adjust_cfa_offset (4); \ + cfi_rel_offset (REG, 0) + +#define CFI_POP(REG) \ + cfi_adjust_cfa_offset (-4); \ + cfi_restore (REG) + +#define PUSH(REG) pushl REG; CFI_PUSH (REG) +#define POP(REG) popl REG; CFI_POP (REG) + +#ifdef USE_AS_BZERO16 +# define DEST PARMS +# define LEN DEST+4 +#else +# define DEST PARMS +# define CHR DEST+4 +# define LEN CHR+4 +#endif + +#if 1 +# define SETRTNVAL +#else +# define SETRTNVAL movl DEST(%esp), %eax +#endif + +#ifdef SHARED +# define ENTRANCE PUSH (%ebx); +# define RETURN_END POP (%ebx); ret +# define RETURN RETURN_END; CFI_PUSH (%ebx) +# define PARMS 8 /* Preserve EBX. */ +# define JMPTBL(I, B) I - B + +/* Load an entry in a jump table into EBX and branch to it. TABLE is a + jump table with relative offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + /* We first load PC into EBX. */ \ + call __i686.get_pc_thunk.bx; \ + /* Get the address of the jump table. */ \ + add $(TABLE - .), %ebx; \ + /* Get the entry and convert the relative offset to the \ + absolute address. */ \ + add (%ebx,%ecx,4), %ebx; \ + /* We loaded the jump table and adjuested EDX. Go. */ \ + jmp *%ebx + + .section .gnu.linkonce.t.__i686.get_pc_thunk.bx,"ax",@progbits + .globl __i686.get_pc_thunk.bx + .hidden __i686.get_pc_thunk.bx + ALIGN (4) + .type __i686.get_pc_thunk.bx,@function +__i686.get_pc_thunk.bx: + movl (%esp), %ebx + ret +#else +# define ENTRANCE +# define RETURN_END ret +# define RETURN RETURN_END +# define PARMS 4 +# define JMPTBL(I, B) I + +/* Branch to an entry in a jump table. TABLE is a jump table with + absolute offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + jmp *TABLE(,%ecx,4) +#endif + + .section .text.sse2,"ax",@progbits + ALIGN (4) +ENTRY (sse2_memset16_atom) + ENTRANCE + + movl LEN(%esp), %ecx +#ifdef USE_AS_ANDROID + shr $1, %ecx +#endif +#ifdef USE_AS_BZERO16 + xor %eax, %eax +#else + movzwl CHR(%esp), %eax + mov %eax, %edx + shl $16, %eax + or %edx, %eax +#endif + movl DEST(%esp), %edx + cmp $32, %ecx + jae L(32wordsormore) + +L(write_less32words): + lea (%edx, %ecx, 2), %edx + BRANCH_TO_JMPTBL_ENTRY (L(table_less32words)) + + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_less32words): + .int JMPTBL (L(write_0words), L(table_less32words)) + .int JMPTBL (L(write_1words), L(table_less32words)) + .int JMPTBL (L(write_2words), L(table_less32words)) + .int JMPTBL (L(write_3words), L(table_less32words)) + .int JMPTBL (L(write_4words), L(table_less32words)) + .int JMPTBL (L(write_5words), L(table_less32words)) + .int JMPTBL (L(write_6words), L(table_less32words)) + .int JMPTBL (L(write_7words), L(table_less32words)) + .int JMPTBL (L(write_8words), L(table_less32words)) + .int JMPTBL (L(write_9words), L(table_less32words)) + .int JMPTBL (L(write_10words), L(table_less32words)) + .int JMPTBL (L(write_11words), L(table_less32words)) + .int JMPTBL (L(write_12words), L(table_less32words)) + .int JMPTBL (L(write_13words), L(table_less32words)) + .int JMPTBL (L(write_14words), L(table_less32words)) + .int JMPTBL (L(write_15words), L(table_less32words)) + .int JMPTBL (L(write_16words), L(table_less32words)) + .int JMPTBL (L(write_17words), L(table_less32words)) + .int JMPTBL (L(write_18words), L(table_less32words)) + .int JMPTBL (L(write_19words), L(table_less32words)) + .int JMPTBL (L(write_20words), L(table_less32words)) + .int JMPTBL (L(write_21words), L(table_less32words)) + .int JMPTBL (L(write_22words), L(table_less32words)) + .int JMPTBL (L(write_23words), L(table_less32words)) + .int JMPTBL (L(write_24words), L(table_less32words)) + .int JMPTBL (L(write_25words), L(table_less32words)) + .int JMPTBL (L(write_26words), L(table_less32words)) + .int JMPTBL (L(write_27words), L(table_less32words)) + .int JMPTBL (L(write_28words), L(table_less32words)) + .int JMPTBL (L(write_29words), L(table_less32words)) + .int JMPTBL (L(write_30words), L(table_less32words)) + .int JMPTBL (L(write_31words), L(table_less32words)) + .popsection + + ALIGN (4) +L(write_28words): + movl %eax, -56(%edx) + movl %eax, -52(%edx) +L(write_24words): + movl %eax, -48(%edx) + movl %eax, -44(%edx) +L(write_20words): + movl %eax, -40(%edx) + movl %eax, -36(%edx) +L(write_16words): + movl %eax, -32(%edx) + movl %eax, -28(%edx) +L(write_12words): + movl %eax, -24(%edx) + movl %eax, -20(%edx) +L(write_8words): + movl %eax, -16(%edx) + movl %eax, -12(%edx) +L(write_4words): + movl %eax, -8(%edx) + movl %eax, -4(%edx) +L(write_0words): + SETRTNVAL + RETURN + + ALIGN (4) +L(write_29words): + movl %eax, -58(%edx) + movl %eax, -54(%edx) +L(write_25words): + movl %eax, -50(%edx) + movl %eax, -46(%edx) +L(write_21words): + movl %eax, -42(%edx) + movl %eax, -38(%edx) +L(write_17words): + movl %eax, -34(%edx) + movl %eax, -30(%edx) +L(write_13words): + movl %eax, -26(%edx) + movl %eax, -22(%edx) +L(write_9words): + movl %eax, -18(%edx) + movl %eax, -14(%edx) +L(write_5words): + movl %eax, -10(%edx) + movl %eax, -6(%edx) +L(write_1words): + mov %ax, -2(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(write_30words): + movl %eax, -60(%edx) + movl %eax, -56(%edx) +L(write_26words): + movl %eax, -52(%edx) + movl %eax, -48(%edx) +L(write_22words): + movl %eax, -44(%edx) + movl %eax, -40(%edx) +L(write_18words): + movl %eax, -36(%edx) + movl %eax, -32(%edx) +L(write_14words): + movl %eax, -28(%edx) + movl %eax, -24(%edx) +L(write_10words): + movl %eax, -20(%edx) + movl %eax, -16(%edx) +L(write_6words): + movl %eax, -12(%edx) + movl %eax, -8(%edx) +L(write_2words): + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(write_31words): + movl %eax, -62(%edx) + movl %eax, -58(%edx) +L(write_27words): + movl %eax, -54(%edx) + movl %eax, -50(%edx) +L(write_23words): + movl %eax, -46(%edx) + movl %eax, -42(%edx) +L(write_19words): + movl %eax, -38(%edx) + movl %eax, -34(%edx) +L(write_15words): + movl %eax, -30(%edx) + movl %eax, -26(%edx) +L(write_11words): + movl %eax, -22(%edx) + movl %eax, -18(%edx) +L(write_7words): + movl %eax, -14(%edx) + movl %eax, -10(%edx) +L(write_3words): + movl %eax, -6(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + ALIGN (4) + +L(32wordsormore): + shl $1, %ecx + test $0x01, %edx + jz L(aligned2bytes) + mov %eax, (%edx) + mov %eax, -4(%edx, %ecx) + sub $2, %ecx + add $1, %edx + rol $8, %eax +L(aligned2bytes): +#ifdef USE_AS_BZERO16 + pxor %xmm0, %xmm0 +#else + movd %eax, %xmm0 + pshufd $0, %xmm0, %xmm0 +#endif + testl $0xf, %edx + jz L(aligned_16) +/* ECX > 32 and EDX is not 16 byte aligned. */ +L(not_aligned_16): + movdqu %xmm0, (%edx) + movl %edx, %eax + and $-16, %edx + add $16, %edx + sub %edx, %eax + add %eax, %ecx + movd %xmm0, %eax + + ALIGN (4) +L(aligned_16): + cmp $128, %ecx + jae L(128bytesormore) + +L(aligned_16_less128bytes): + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytesormore): +#ifdef SHARED_CACHE_SIZE + PUSH (%ebx) + mov $SHARED_CACHE_SIZE, %ebx +#else +# ifdef SHARED + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx +# else + PUSH (%ebx) + mov __x86_shared_cache_size, %ebx +# endif +#endif + cmp %ebx, %ecx + jae L(128bytesormore_nt_start) + + +#ifdef DATA_CACHE_SIZE + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp $DATA_CACHE_SIZE, %ecx +#else +# ifdef SHARED +# define RESTORE_EBX_STATE + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx +# else + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp __x86_data_cache_size, %ecx +# endif +#endif + + jae L(128bytes_L2_normal) + subl $128, %ecx +L(128bytesormore_normal): + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jb L(128bytesless_normal) + + + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jae L(128bytesormore_normal) + +L(128bytesless_normal): + lea 128(%ecx), %ecx + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytes_L2_normal): + prefetcht0 0x380(%edx) + prefetcht0 0x3c0(%edx) + sub $128, %ecx + movdqa %xmm0, (%edx) + movaps %xmm0, 0x10(%edx) + movaps %xmm0, 0x20(%edx) + movaps %xmm0, 0x30(%edx) + movaps %xmm0, 0x40(%edx) + movaps %xmm0, 0x50(%edx) + movaps %xmm0, 0x60(%edx) + movaps %xmm0, 0x70(%edx) + add $128, %edx + cmp $128, %ecx + jae L(128bytes_L2_normal) + +L(128bytesless_L2_normal): + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + RESTORE_EBX_STATE +L(128bytesormore_nt_start): + sub %ebx, %ecx + mov %ebx, %eax + and $0x7f, %eax + add %eax, %ecx + movd %xmm0, %eax + ALIGN (4) +L(128bytesormore_shared_cache_loop): + prefetcht0 0x3c0(%edx) + prefetcht0 0x380(%edx) + sub $0x80, %ebx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ebx + jae L(128bytesormore_shared_cache_loop) + cmp $0x80, %ecx + jb L(shared_cache_loop_end) + ALIGN (4) +L(128bytesormore_nt): + sub $0x80, %ecx + movntdq %xmm0, (%edx) + movntdq %xmm0, 0x10(%edx) + movntdq %xmm0, 0x20(%edx) + movntdq %xmm0, 0x30(%edx) + movntdq %xmm0, 0x40(%edx) + movntdq %xmm0, 0x50(%edx) + movntdq %xmm0, 0x60(%edx) + movntdq %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ecx + jae L(128bytesormore_nt) + sfence +L(shared_cache_loop_end): +#if defined DATA_CACHE_SIZE || !defined SHARED + POP (%ebx) +#endif + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_16_128bytes): + .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_2bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_6bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_10bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_14bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_18bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_22bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_26bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_30bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_34bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_38bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_42bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_46bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_50bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_54bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_58bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_62bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_66bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_70bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_74bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_78bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_82bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_86bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_90bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_94bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_98bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_102bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_106bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_110bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_114bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_118bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_122bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_126bytes), L(table_16_128bytes)) + .popsection + + + ALIGN (4) +L(aligned_16_112bytes): + movdqa %xmm0, -112(%edx) +L(aligned_16_96bytes): + movdqa %xmm0, -96(%edx) +L(aligned_16_80bytes): + movdqa %xmm0, -80(%edx) +L(aligned_16_64bytes): + movdqa %xmm0, -64(%edx) +L(aligned_16_48bytes): + movdqa %xmm0, -48(%edx) +L(aligned_16_32bytes): + movdqa %xmm0, -32(%edx) +L(aligned_16_16bytes): + movdqa %xmm0, -16(%edx) +L(aligned_16_0bytes): + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_114bytes): + movdqa %xmm0, -114(%edx) +L(aligned_16_98bytes): + movdqa %xmm0, -98(%edx) +L(aligned_16_82bytes): + movdqa %xmm0, -82(%edx) +L(aligned_16_66bytes): + movdqa %xmm0, -66(%edx) +L(aligned_16_50bytes): + movdqa %xmm0, -50(%edx) +L(aligned_16_34bytes): + movdqa %xmm0, -34(%edx) +L(aligned_16_18bytes): + movdqa %xmm0, -18(%edx) +L(aligned_16_2bytes): + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_116bytes): + movdqa %xmm0, -116(%edx) +L(aligned_16_100bytes): + movdqa %xmm0, -100(%edx) +L(aligned_16_84bytes): + movdqa %xmm0, -84(%edx) +L(aligned_16_68bytes): + movdqa %xmm0, -68(%edx) +L(aligned_16_52bytes): + movdqa %xmm0, -52(%edx) +L(aligned_16_36bytes): + movdqa %xmm0, -36(%edx) +L(aligned_16_20bytes): + movdqa %xmm0, -20(%edx) +L(aligned_16_4bytes): + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_118bytes): + movdqa %xmm0, -118(%edx) +L(aligned_16_102bytes): + movdqa %xmm0, -102(%edx) +L(aligned_16_86bytes): + movdqa %xmm0, -86(%edx) +L(aligned_16_70bytes): + movdqa %xmm0, -70(%edx) +L(aligned_16_54bytes): + movdqa %xmm0, -54(%edx) +L(aligned_16_38bytes): + movdqa %xmm0, -38(%edx) +L(aligned_16_22bytes): + movdqa %xmm0, -22(%edx) +L(aligned_16_6bytes): + movl %eax, -6(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_120bytes): + movdqa %xmm0, -120(%edx) +L(aligned_16_104bytes): + movdqa %xmm0, -104(%edx) +L(aligned_16_88bytes): + movdqa %xmm0, -88(%edx) +L(aligned_16_72bytes): + movdqa %xmm0, -72(%edx) +L(aligned_16_56bytes): + movdqa %xmm0, -56(%edx) +L(aligned_16_40bytes): + movdqa %xmm0, -40(%edx) +L(aligned_16_24bytes): + movdqa %xmm0, -24(%edx) +L(aligned_16_8bytes): + movq %xmm0, -8(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_122bytes): + movdqa %xmm0, -122(%edx) +L(aligned_16_106bytes): + movdqa %xmm0, -106(%edx) +L(aligned_16_90bytes): + movdqa %xmm0, -90(%edx) +L(aligned_16_74bytes): + movdqa %xmm0, -74(%edx) +L(aligned_16_58bytes): + movdqa %xmm0, -58(%edx) +L(aligned_16_42bytes): + movdqa %xmm0, -42(%edx) +L(aligned_16_26bytes): + movdqa %xmm0, -26(%edx) +L(aligned_16_10bytes): + movq %xmm0, -10(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_124bytes): + movdqa %xmm0, -124(%edx) +L(aligned_16_108bytes): + movdqa %xmm0, -108(%edx) +L(aligned_16_92bytes): + movdqa %xmm0, -92(%edx) +L(aligned_16_76bytes): + movdqa %xmm0, -76(%edx) +L(aligned_16_60bytes): + movdqa %xmm0, -60(%edx) +L(aligned_16_44bytes): + movdqa %xmm0, -44(%edx) +L(aligned_16_28bytes): + movdqa %xmm0, -28(%edx) +L(aligned_16_12bytes): + movq %xmm0, -12(%edx) + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_126bytes): + movdqa %xmm0, -126(%edx) +L(aligned_16_110bytes): + movdqa %xmm0, -110(%edx) +L(aligned_16_94bytes): + movdqa %xmm0, -94(%edx) +L(aligned_16_78bytes): + movdqa %xmm0, -78(%edx) +L(aligned_16_62bytes): + movdqa %xmm0, -62(%edx) +L(aligned_16_46bytes): + movdqa %xmm0, -46(%edx) +L(aligned_16_30bytes): + movdqa %xmm0, -30(%edx) +L(aligned_16_14bytes): + movq %xmm0, -14(%edx) + movl %eax, -6(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + +END (sse2_memset16_atom) diff --git a/libcutils/arch-x86/sse2-memset32-atom.S b/libcutils/arch-x86/sse2-memset32-atom.S new file mode 100644 index 00000000000..4a524845008 --- /dev/null +++ b/libcutils/arch-x86/sse2-memset32-atom.S @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Contributed by: Intel Corporation + */ + +#ifndef L +# define L(label) .L##label +#endif + +#ifndef ALIGN +# define ALIGN(n) .p2align n +#endif + +#ifndef cfi_startproc +# define cfi_startproc .cfi_startproc +#endif + +#ifndef cfi_endproc +# define cfi_endproc .cfi_endproc +#endif + +#ifndef cfi_rel_offset +# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off +#endif + +#ifndef cfi_restore +# define cfi_restore(reg) .cfi_restore reg +#endif + +#ifndef cfi_adjust_cfa_offset +# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off +#endif + +#ifndef ENTRY +# define ENTRY(name) \ + .type name, @function; \ + .globl name; \ + .p2align 4; \ +name: \ + cfi_startproc +#endif + +#ifndef END +# define END(name) \ + cfi_endproc; \ + .size name, .-name +#endif + +#define CFI_PUSH(REG) \ + cfi_adjust_cfa_offset (4); \ + cfi_rel_offset (REG, 0) + +#define CFI_POP(REG) \ + cfi_adjust_cfa_offset (-4); \ + cfi_restore (REG) + +#define PUSH(REG) pushl REG; CFI_PUSH (REG) +#define POP(REG) popl REG; CFI_POP (REG) + +#ifdef USE_AS_BZERO32 +# define DEST PARMS +# define LEN DEST+4 +#else +# define DEST PARMS +# define DWDS DEST+4 +# define LEN DWDS+4 +#endif + +#ifdef USE_AS_WMEMSET32 +# define SETRTNVAL movl DEST(%esp), %eax +#else +# define SETRTNVAL +#endif + +#ifdef SHARED +# define ENTRANCE PUSH (%ebx); +# define RETURN_END POP (%ebx); ret +# define RETURN RETURN_END; CFI_PUSH (%ebx) +# define PARMS 8 /* Preserve EBX. */ +# define JMPTBL(I, B) I - B + +/* Load an entry in a jump table into EBX and branch to it. TABLE is a + jump table with relative offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + /* We first load PC into EBX. */ \ + call __i686.get_pc_thunk.bx; \ + /* Get the address of the jump table. */ \ + add $(TABLE - .), %ebx; \ + /* Get the entry and convert the relative offset to the \ + absolute address. */ \ + add (%ebx,%ecx,4), %ebx; \ + /* We loaded the jump table and adjuested EDX. Go. */ \ + jmp *%ebx + + .section .gnu.linkonce.t.__i686.get_pc_thunk.bx,"ax",@progbits + .globl __i686.get_pc_thunk.bx + .hidden __i686.get_pc_thunk.bx + ALIGN (4) + .type __i686.get_pc_thunk.bx,@function +__i686.get_pc_thunk.bx: + movl (%esp), %ebx + ret +#else +# define ENTRANCE +# define RETURN_END ret +# define RETURN RETURN_END +# define PARMS 4 +# define JMPTBL(I, B) I + +/* Branch to an entry in a jump table. TABLE is a jump table with + absolute offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + jmp *TABLE(,%ecx,4) +#endif + + .section .text.sse2,"ax",@progbits + ALIGN (4) +ENTRY (sse2_memset32_atom) + ENTRANCE + + movl LEN(%esp), %ecx +#ifdef USE_AS_ANDROID + shr $2, %ecx +#endif +#ifdef USE_AS_BZERO32 + xor %eax, %eax +#else + mov DWDS(%esp), %eax + mov %eax, %edx +#endif + movl DEST(%esp), %edx + cmp $16, %ecx + jae L(16dbwordsormore) + +L(write_less16dbwords): + lea (%edx, %ecx, 4), %edx + BRANCH_TO_JMPTBL_ENTRY (L(table_less16dbwords)) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_less16dbwords): + .int JMPTBL (L(write_0dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_1dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_2dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_3dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_4dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_5dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_6dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_7dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_8dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_9dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_10dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_11dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_12dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_13dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_14dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_15dbwords), L(table_less16dbwords)) + .popsection + + ALIGN (4) +L(write_15dbwords): + movl %eax, -60(%edx) +L(write_14dbwords): + movl %eax, -56(%edx) +L(write_13dbwords): + movl %eax, -52(%edx) +L(write_12dbwords): + movl %eax, -48(%edx) +L(write_11dbwords): + movl %eax, -44(%edx) +L(write_10dbwords): + movl %eax, -40(%edx) +L(write_9dbwords): + movl %eax, -36(%edx) +L(write_8dbwords): + movl %eax, -32(%edx) +L(write_7dbwords): + movl %eax, -28(%edx) +L(write_6dbwords): + movl %eax, -24(%edx) +L(write_5dbwords): + movl %eax, -20(%edx) +L(write_4dbwords): + movl %eax, -16(%edx) +L(write_3dbwords): + movl %eax, -12(%edx) +L(write_2dbwords): + movl %eax, -8(%edx) +L(write_1dbwords): + movl %eax, -4(%edx) +L(write_0dbwords): + SETRTNVAL + RETURN + + ALIGN (4) +L(16dbwordsormore): + test $3, %edx + jz L(aligned4bytes) + mov %eax, (%edx) + mov %eax, -4(%edx, %ecx, 4) + sub $1, %ecx + rol $24, %eax + add $1, %edx + test $3, %edx + jz L(aligned4bytes) + ror $8, %eax + add $1, %edx + test $3, %edx + jz L(aligned4bytes) + ror $8, %eax + add $1, %edx +L(aligned4bytes): + shl $2, %ecx + +#ifdef USE_AS_BZERO32 + pxor %xmm0, %xmm0 +#else + movd %eax, %xmm0 + pshufd $0, %xmm0, %xmm0 +#endif + testl $0xf, %edx + jz L(aligned_16) +/* ECX > 32 and EDX is not 16 byte aligned. */ +L(not_aligned_16): + movdqu %xmm0, (%edx) + movl %edx, %eax + and $-16, %edx + add $16, %edx + sub %edx, %eax + add %eax, %ecx + movd %xmm0, %eax + ALIGN (4) +L(aligned_16): + cmp $128, %ecx + jae L(128bytesormore) + +L(aligned_16_less128bytes): + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytesormore): +#ifdef SHARED_CACHE_SIZE + PUSH (%ebx) + mov $SHARED_CACHE_SIZE, %ebx +#else +# ifdef SHARED + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx +# else + PUSH (%ebx) + mov __x86_shared_cache_size, %ebx +# endif +#endif + cmp %ebx, %ecx + jae L(128bytesormore_nt_start) + +#ifdef DATA_CACHE_SIZE + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp $DATA_CACHE_SIZE, %ecx +#else +# ifdef SHARED +# define RESTORE_EBX_STATE + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx +# else + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp __x86_data_cache_size, %ecx +# endif +#endif + + jae L(128bytes_L2_normal) + subl $128, %ecx +L(128bytesormore_normal): + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jb L(128bytesless_normal) + + + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jae L(128bytesormore_normal) + +L(128bytesless_normal): + lea 128(%ecx), %ecx + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytes_L2_normal): + prefetcht0 0x380(%edx) + prefetcht0 0x3c0(%edx) + sub $128, %ecx + movdqa %xmm0, (%edx) + movaps %xmm0, 0x10(%edx) + movaps %xmm0, 0x20(%edx) + movaps %xmm0, 0x30(%edx) + movaps %xmm0, 0x40(%edx) + movaps %xmm0, 0x50(%edx) + movaps %xmm0, 0x60(%edx) + movaps %xmm0, 0x70(%edx) + add $128, %edx + cmp $128, %ecx + jae L(128bytes_L2_normal) + +L(128bytesless_L2_normal): + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + RESTORE_EBX_STATE +L(128bytesormore_nt_start): + sub %ebx, %ecx + mov %ebx, %eax + and $0x7f, %eax + add %eax, %ecx + movd %xmm0, %eax + ALIGN (4) +L(128bytesormore_shared_cache_loop): + prefetcht0 0x3c0(%edx) + prefetcht0 0x380(%edx) + sub $0x80, %ebx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ebx + jae L(128bytesormore_shared_cache_loop) + cmp $0x80, %ecx + jb L(shared_cache_loop_end) + + ALIGN (4) +L(128bytesormore_nt): + sub $0x80, %ecx + movntdq %xmm0, (%edx) + movntdq %xmm0, 0x10(%edx) + movntdq %xmm0, 0x20(%edx) + movntdq %xmm0, 0x30(%edx) + movntdq %xmm0, 0x40(%edx) + movntdq %xmm0, 0x50(%edx) + movntdq %xmm0, 0x60(%edx) + movntdq %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ecx + jae L(128bytesormore_nt) + sfence +L(shared_cache_loop_end): +#if defined DATA_CACHE_SIZE || !defined SHARED + POP (%ebx) +#endif + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_16_128bytes): + .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes)) + .popsection + + ALIGN (4) +L(aligned_16_112bytes): + movdqa %xmm0, -112(%edx) +L(aligned_16_96bytes): + movdqa %xmm0, -96(%edx) +L(aligned_16_80bytes): + movdqa %xmm0, -80(%edx) +L(aligned_16_64bytes): + movdqa %xmm0, -64(%edx) +L(aligned_16_48bytes): + movdqa %xmm0, -48(%edx) +L(aligned_16_32bytes): + movdqa %xmm0, -32(%edx) +L(aligned_16_16bytes): + movdqa %xmm0, -16(%edx) +L(aligned_16_0bytes): + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_116bytes): + movdqa %xmm0, -116(%edx) +L(aligned_16_100bytes): + movdqa %xmm0, -100(%edx) +L(aligned_16_84bytes): + movdqa %xmm0, -84(%edx) +L(aligned_16_68bytes): + movdqa %xmm0, -68(%edx) +L(aligned_16_52bytes): + movdqa %xmm0, -52(%edx) +L(aligned_16_36bytes): + movdqa %xmm0, -36(%edx) +L(aligned_16_20bytes): + movdqa %xmm0, -20(%edx) +L(aligned_16_4bytes): + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_120bytes): + movdqa %xmm0, -120(%edx) +L(aligned_16_104bytes): + movdqa %xmm0, -104(%edx) +L(aligned_16_88bytes): + movdqa %xmm0, -88(%edx) +L(aligned_16_72bytes): + movdqa %xmm0, -72(%edx) +L(aligned_16_56bytes): + movdqa %xmm0, -56(%edx) +L(aligned_16_40bytes): + movdqa %xmm0, -40(%edx) +L(aligned_16_24bytes): + movdqa %xmm0, -24(%edx) +L(aligned_16_8bytes): + movq %xmm0, -8(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_124bytes): + movdqa %xmm0, -124(%edx) +L(aligned_16_108bytes): + movdqa %xmm0, -108(%edx) +L(aligned_16_92bytes): + movdqa %xmm0, -92(%edx) +L(aligned_16_76bytes): + movdqa %xmm0, -76(%edx) +L(aligned_16_60bytes): + movdqa %xmm0, -60(%edx) +L(aligned_16_44bytes): + movdqa %xmm0, -44(%edx) +L(aligned_16_28bytes): + movdqa %xmm0, -28(%edx) +L(aligned_16_12bytes): + movq %xmm0, -12(%edx) + movl %eax, -4(%edx) + SETRTNVAL + RETURN + +END (sse2_memset32_atom) diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S deleted file mode 100644 index 7befd780268..00000000000 --- a/libcutils/atomic-android-arm.S +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -/* - * NOTE: these atomic operations are SMP safe on all architectures, - * except swap(), see below. - */ - - .text - .align - - .global android_atomic_write - - .global android_atomic_inc - .global android_atomic_dec - - .global android_atomic_add - .global android_atomic_and - .global android_atomic_or - - .global android_atomic_swap - - .global android_atomic_cmpxchg - -/* - * ---------------------------------------------------------------------------- - * int __kernel_cmpxchg(int oldval, int newval, int *ptr) - * clobbered: r3, ip, flags - * return 0 if a swap was made, non-zero otherwise. - */ - - .equ kernel_cmpxchg, 0xFFFF0FC0 - .equ kernel_atomic_base, 0xFFFF0FFF - -/* - * ---------------------------------------------------------------------------- - * android_atomic_write - * input: r0=value, r1=address - * output: void - */ - -android_atomic_write: - str r0, [r1] - bx lr; - -/* - * ---------------------------------------------------------------------------- - * android_atomic_inc - * input: r0 = address - * output: r0 = old value - */ - -android_atomic_inc: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r2, r0 -1: @ android_atomic_inc - ldr r0, [r2] - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - add r1, r0, #1 - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - add r1, r0, #1 - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcc 1b - sub r0, r1, #1 - ldmia sp!, {r4, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_dec - * input: r0=address - * output: r0 = old value - */ - -android_atomic_dec: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r2, r0 -1: @ android_atomic_dec - ldr r0, [r2] - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - sub r1, r0, #1 - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - sub r1, r0, #1 - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcc 1b - add r0, r1, #1 - ldmia sp!, {r4, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_add - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_add: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r2, r1 - mov r4, r0 -1: @ android_atomic_add - ldr r0, [r2] - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - add r1, r0, r4 - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - add r1, r0, r4 - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcc 1b - sub r0, r1, r4 - ldmia sp!, {r4, lr} - bx lr - .fnend - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_and - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_and: - .fnstart - .save {r4, r5, lr} - stmdb sp!, {r4, r5, lr} - mov r2, r1 /* r2 = address */ - mov r4, r0 /* r4 = the value */ -1: @ android_atomic_and - ldr r0, [r2] /* r0 = address[0] */ - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #8 - mov r5, r0 /* r5 = save address[0] */ - and r1, r0, r4 /* r1 = new value */ - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ -#else - mov r5, r0 /* r5 = save address[0] */ - and r1, r0, r4 /* r1 = new value */ - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ - mov lr, pc - bx r3 -#endif - bcc 1b - mov r0, r5 - ldmia sp!, {r4, r5, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_or - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_or: - .fnstart - .save {r4, r5, lr} - stmdb sp!, {r4, r5, lr} - mov r2, r1 /* r2 = address */ - mov r4, r0 /* r4 = the value */ -1: @ android_atomic_or - ldr r0, [r2] /* r0 = address[0] */ - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #8 - mov r5, r0 /* r5 = save address[0] */ - orr r1, r0, r4 /* r1 = new value */ - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ -#else - mov r5, r0 /* r5 = save address[0] */ - orr r1, r0, r4 /* r1 = new value */ - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ - mov lr, pc - bx r3 -#endif - bcc 1b - mov r0, r5 - ldmia sp!, {r4, r5, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_swap - * input: r0=value, r1=address - * output: r0 = old value - */ - -/* FIXME: this is not safe on SMP systems - * a general way to do it is to use kernel_cmpxchg */ - -android_atomic_swap: - swp r0, r0, [r1] - bx lr - -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg - * input: r0=oldvalue, r1=newvalue, r2=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ - -android_atomic_cmpxchg: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r4, r0 /* r4 = save oldvalue */ -1: @ android_atomic_cmpxchg - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - mov r0, r4 /* r0 = oldvalue */ - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - mov r0, r4 /* r0 = oldvalue */ - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcs 2f /* swap was made. we're good, return. */ - ldr r3, [r2] /* swap not made, see if it's because *ptr!=oldvalue */ - cmp r3, r4 - beq 1b -2: @ android_atomic_cmpxchg - ldmia sp!, {r4, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg_64 - * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ -/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */ diff --git a/libcutils/atomic-android-armv6.S b/libcutils/atomic-android-armv6.S deleted file mode 100644 index a71308966c9..00000000000 --- a/libcutils/atomic-android-armv6.S +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - .text - .align - - .global android_atomic_write - - .global android_atomic_inc - .global android_atomic_dec - - .global android_atomic_add - .global android_atomic_and - .global android_atomic_or - - .global android_atomic_swap - - .global android_atomic_cmpxchg - - - -/* FIXME: On SMP systems memory barriers may be needed */ -#warning "this file is not safe with SMP systems" - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_write - * input: r0=value, r1=address - * output: void - */ - -android_atomic_write: - str r0, [r1] - bx lr; - -/* - * ---------------------------------------------------------------------------- - * android_atomic_inc - * input: r0 = address - * output: r0 = old value - */ - -android_atomic_inc: - mov r12, r0 -1: ldrex r0, [r12] - add r2, r0, #1 - strex r1, r2, [r12] - cmp r1, #0 - bxeq lr - b 1b - -/* - * ---------------------------------------------------------------------------- - * android_atomic_dec - * input: r0=address - * output: r0 = old value - */ - -android_atomic_dec: - mov r12, r0 -1: ldrex r0, [r12] - sub r2, r0, #1 - strex r1, r2, [r12] - cmp r1, #0 - bxeq lr - b 1b - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_add - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_add: - mov r12, r0 -1: ldrex r0, [r1] - add r2, r0, r12 - strex r3, r2, [r1] - cmp r3, #0 - bxeq lr - b 1b - -/* - * ---------------------------------------------------------------------------- - * android_atomic_and - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_and: - mov r12, r0 -1: ldrex r0, [r1] - and r2, r0, r12 - strex r3, r2, [r1] - cmp r3, #0 - bxeq lr - b 1b - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_or - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_or: - mov r12, r0 -1: ldrex r0, [r1] - orr r2, r0, r12 - strex r3, r2, [r1] - cmp r3, #0 - bxeq lr - b 1b - -/* - * ---------------------------------------------------------------------------- - * android_atomic_swap - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_swap: - swp r0, r0, [r1] - bx lr - -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg - * input: r0=oldvalue, r1=newvalue, r2=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ - -android_atomic_cmpxchg: - mov r12, r1 - ldrex r3, [r2] - eors r0, r0, r3 - strexeq r0, r12, [r2] - bx lr - - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg_64 - * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ -/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */ diff --git a/libcutils/atomic-android-sh.c b/libcutils/atomic-android-sh.c new file mode 100644 index 00000000000..8bac68ad394 --- /dev/null +++ b/libcutils/atomic-android-sh.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#ifdef HAVE_WIN32_THREADS +#include +#else +#include +#endif + +/* + * Note : + * + * (1) SuperH does not have CMPXCHG. It has only TAS for atomic + * operations. It does not seem a good idea to implement CMPXCHG, + * with TAS. So, we choose to implemnt these operations with + * posix mutexes. Please be sure that this might cause performance + * problem for Android-SH. Using LL/SC instructions supported in SH-X3, + * best performnace would be realized. + * + * (2) Mutex initialization problem happens, which is commented for + * ARM implementation, in this file above. + * We follow the fact that the initializer for mutex is a simple zero + * value. + * + * (3) These operations are NOT safe for SMP, as there is no currently + * no definition for a memory barrier operation. + */ + +#include + +#define SWAP_LOCK_COUNT 32U +static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT]; + +#define SWAP_LOCK(addr) \ + &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT] + + +int32_t android_atomic_acquire_load(volatile const int32_t* addr) +{ + return *addr; +} + +int32_t android_atomic_release_load(volatile const int32_t* addr) +{ + return *addr; +} + +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, value, addr)); +} + +void android_atomic_release_store(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, value, addr)); +} + +int32_t android_atomic_inc(volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, oldValue+1, addr)); + return oldValue; +} + +int32_t android_atomic_dec(volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, oldValue-1, addr)); + return oldValue; +} + +int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, oldValue+value, addr)); + return oldValue; +} + +int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, oldValue&value, addr)); + return oldValue; +} + +int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, oldValue|value, addr)); + return oldValue; +} + +int android_atomic_acquire_cmpxchg(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { + return android_atomic_release_cmpxchg(oldValue, newValue, addr); +} + +int android_atomic_release_cmpxchg(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { + int result; + pthread_mutex_t* lock = SWAP_LOCK(addr); + + pthread_mutex_lock(lock); + + if (*addr == oldvalue) { + *addr = newvalue; + result = 0; + } else { + result = 1; + } + pthread_mutex_unlock(lock); + return result; +} + diff --git a/libcutils/atomic.c b/libcutils/atomic.c index 65d7af0af00..f6cd8b0e7d9 100644 --- a/libcutils/atomic.c +++ b/libcutils/atomic.c @@ -14,322 +14,6 @@ * limitations under the License. */ -#include -#ifdef HAVE_WIN32_THREADS -#include -#else -#include -#endif +#define inline -/*****************************************************************************/ -#if defined(HAVE_MACOSX_IPC) - -#include - -void android_atomic_write(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, value, (int32_t*)addr) == 0); -} - -int32_t android_atomic_inc(volatile int32_t* addr) { - return OSAtomicIncrement32Barrier((int32_t*)addr)-1; -} - -int32_t android_atomic_dec(volatile int32_t* addr) { - return OSAtomicDecrement32Barrier((int32_t*)addr)+1; -} - -int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { - return OSAtomicAdd32Barrier(value, (int32_t*)addr)-value; -} - -int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue&value, (int32_t*)addr) == 0); - return oldValue; -} - -int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue|value, (int32_t*)addr) == 0); - return oldValue; -} - -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); - return oldValue; -} - -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { - return OSAtomicCompareAndSwap32Barrier(oldvalue, newvalue, (int32_t*)addr) == 0; -} - -#if defined(__ppc__) \ - || defined(__PPC__) \ - || defined(__powerpc__) \ - || defined(__powerpc) \ - || defined(__POWERPC__) \ - || defined(_M_PPC) \ - || defined(__PPC) -#define NEED_QUASIATOMICS 1 -#else - -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { - return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue, - (int64_t*)addr) == 0; -} - -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t oldValue; - do { - oldValue = *addr; - } while (android_quasiatomic_cmpxchg_64(oldValue, value, addr)); - return oldValue; -} - -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - return OSAtomicAdd64Barrier(0, addr); -} - -#endif - - -/*****************************************************************************/ -#elif defined(__i386__) || defined(__x86_64__) - -void android_atomic_write(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); -} - -int32_t android_atomic_inc(volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr)); - return oldValue; -} - -int32_t android_atomic_dec(volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr)); - return oldValue; -} - -int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr)); - return oldValue; -} - -int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue&value, addr)); - return oldValue; -} - -int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue|value, addr)); - return oldValue; -} - -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); - return oldValue; -} - -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { - int xchg; - asm volatile - ( - " lock; cmpxchg %%ecx, (%%edx);" - " setne %%al;" - " andl $1, %%eax" - : "=a" (xchg) - : "a" (oldvalue), "c" (newvalue), "d" (addr) - ); - return xchg; -} - -#define NEED_QUASIATOMICS 1 - -/*****************************************************************************/ -#elif __arm__ -// Most of the implementation is in atomic-android-arm.s. - -// on the device, we implement the 64-bit atomic operations through -// mutex locking. normally, this is bad because we must initialize -// a pthread_mutex_t before being able to use it, and this means -// having to do an initialization check on each function call, and -// that's where really ugly things begin... -// -// BUT, as a special twist, we take advantage of the fact that in our -// pthread library, a mutex is simply a volatile word whose value is always -// initialized to 0. In other words, simply declaring a static mutex -// object initializes it ! -// -// another twist is that we use a small array of mutexes to dispatch -// the contention locks from different memory addresses -// - -#include - -#define SWAP_LOCK_COUNT 32U -static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT]; - -#define SWAP_LOCK(addr) \ - &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT] - - -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t oldValue; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - - oldValue = *addr; - *addr = value; - - pthread_mutex_unlock(lock); - return oldValue; -} - -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { - int result; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - pthread_mutex_unlock(lock); - return result; -} - -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - int64_t result; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - result = *addr; - pthread_mutex_unlock(lock); - return result; -} - -#else - -#error "Unsupported atomic operations for this platform" - -#endif - - - -#if NEED_QUASIATOMICS - -/* Note that a spinlock is *not* a good idea in general - * since they can introduce subtle issues. For example, - * a real-time thread trying to acquire a spinlock already - * acquired by another thread will never yeld, making the - * CPU loop endlessly! - * - * However, this code is only used on the Linux simulator - * so it's probably ok for us. - * - * The alternative is to use a pthread mutex, but - * these must be initialized before being used, and - * then you have the problem of lazily initializing - * a mutex without any other synchronization primitive. - */ - -/* global spinlock for all 64-bit quasiatomic operations */ -static int32_t quasiatomic_spinlock = 0; - -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { - int result; - - while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) { -#ifdef HAVE_WIN32_THREADS - Sleep(0); -#else - sched_yield(); -#endif - } - - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - - android_atomic_swap(0, &quasiatomic_spinlock); - - return result; -} - -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - int64_t result; - - while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) { -#ifdef HAVE_WIN32_THREADS - Sleep(0); -#else - sched_yield(); -#endif - } - - result = *addr; - android_atomic_swap(0, &quasiatomic_spinlock); - - return result; -} - -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t result; - - while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) { -#ifdef HAVE_WIN32_THREADS - Sleep(0); -#else - sched_yield(); -#endif - } - - result = *addr; - *addr = value; - android_atomic_swap(0, &quasiatomic_spinlock); - - return result; -} - -#endif +#include diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c index 75fa6c6d4df..fc5ca78761a 100644 --- a/libcutils/config_utils.c +++ b/libcutils/config_utils.c @@ -315,3 +315,15 @@ void config_load_file(cnode *root, const char *fn) data = load_file(fn, 0); config_load(root, data); } + +void config_free(cnode *root) +{ + cnode *cur = root->first_child; + + while (cur) { + cnode *prev = cur; + config_free(cur); + cur = cur->next; + free(prev); + } +} diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c index e29bc2461cc..65539ea3046 100644 --- a/libcutils/hashmap.c +++ b/libcutils/hashmap.c @@ -310,10 +310,11 @@ void hashmapForEach(Hashmap* map, for (i = 0; i < map->bucketCount; i++) { Entry* entry = map->buckets[i]; while (entry != NULL) { + Entry *next = entry->next; if (!callback(entry->key, entry->value, context)) { return; } - entry = entry->next; + entry = next; } } } diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c new file mode 100644 index 00000000000..f350f58b9f9 --- /dev/null +++ b/libcutils/iosched_policy.c @@ -0,0 +1,67 @@ + +/* libs/cutils/iosched_policy.c +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SCHED_H + +#include + +extern int ioprio_set(int which, int who, int ioprio); + +enum { + WHO_PROCESS = 1, + WHO_PGRP, + WHO_USER, +}; + +#define CLASS_SHIFT 13 +#define IOPRIO_NORM 4 + +int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) { +#ifdef HAVE_ANDROID_OS + if (ioprio_set(WHO_PROCESS, pid, ioprio | (clazz << CLASS_SHIFT))) { + return -1; + } +#endif + return 0; +} + +int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) { +#ifdef HAVE_ANDROID_OS + int rc; + + if ((rc = ioprio_get(WHO_PROCESS, pid)) < 0) { + return -1; + } + + *clazz = (rc >> CLASS_SHIFT); + *ioprio = (rc & 0xff); +#else + *clazz = IoSchedClass_NONE; + *ioprio = 0; +#endif + return 0; +} + +#endif /* HAVE_SCHED_H */ diff --git a/libcutils/klog.c b/libcutils/klog.c new file mode 100644 index 00000000000..b586a575bfb --- /dev/null +++ b/libcutils/klog.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int klog_fd = -1; +static int klog_level = KLOG_DEFAULT_LEVEL; + +void klog_set_level(int level) { + klog_level = level; +} + +void klog_init(void) +{ + static const char *name = "/dev/__kmsg__"; + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { + klog_fd = open(name, O_WRONLY); + fcntl(klog_fd, F_SETFD, FD_CLOEXEC); + unlink(name); + } +} + +#define LOG_BUF_MAX 512 + +void klog_write(int level, const char *fmt, ...) +{ + char buf[LOG_BUF_MAX]; + va_list ap; + + if (level > klog_level) return; + if (klog_fd < 0) return; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_MAX, fmt, ap); + buf[LOG_BUF_MAX - 1] = 0; + va_end(ap); + write(klog_fd, buf, strlen(buf)); +} diff --git a/libcutils/list.c b/libcutils/list.c new file mode 100644 index 00000000000..e13452d474b --- /dev/null +++ b/libcutils/list.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} diff --git a/libcutils/memory.c b/libcutils/memory.c index ef6c7e6636c..6486b450446 100644 --- a/libcutils/memory.c +++ b/libcutils/memory.c @@ -16,6 +16,7 @@ #include +#if !HAVE_MEMSET16 void android_memset16(uint16_t* dst, uint16_t value, size_t size) { size >>= 1; @@ -23,7 +24,9 @@ void android_memset16(uint16_t* dst, uint16_t value, size_t size) *dst++ = value; } } +#endif +#if !HAVE_MEMSET32 void android_memset32(uint32_t* dst, uint32_t value, size_t size) { size >>= 2; @@ -31,6 +34,7 @@ void android_memset32(uint32_t* dst, uint32_t value, size_t size) *dst++ = value; } } +#endif #if !HAVE_STRLCPY /* diff --git a/libcutils/mspace.c b/libcutils/mspace.c index 8fd5de77de8..6d3b35c8421 100644 --- a/libcutils/mspace.c +++ b/libcutils/mspace.c @@ -134,67 +134,47 @@ assert(nb >= 0); //xxx deal with the trim case return oldbrk; } -mspace create_contiguous_mspace_with_name(size_t starting_capacity, - size_t max_capacity, int locked, char const * name) { - int fd, ret; +mspace create_contiguous_mspace_with_base(size_t starting_capacity, + size_t max_capacity, int locked, void *base) { struct mspace_contig_state *cs; - char buf[ASHMEM_NAME_LEN] = "mspace"; - void *base; unsigned int pagesize; mstate m; - if (starting_capacity > max_capacity) - return (mspace)0; - init_mparams(); pagesize = PAGESIZE; - - /* Create the anonymous memory that will back the mspace. - * This reserves all of the virtual address space we could - * ever need. Physical pages will be mapped as the memory - * is touched. - * - * Align max_capacity to a whole page. - */ - max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize); - - if (name) - snprintf(buf, sizeof(buf), "mspace/%s", name); - fd = ashmem_create_region(buf, max_capacity); - if (fd < 0) - return (mspace)0; - - base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); - if (base == MAP_FAILED) - return (mspace)0; - - /* Make sure that base is at the beginning of a page. - */ + assert(starting_capacity <= max_capacity); assert(((uintptr_t)base & (pagesize-1)) == 0); + assert(((uintptr_t)max_capacity & (pagesize-1)) == 0); + starting_capacity = (size_t)ALIGN_UP(starting_capacity, pagesize); - /* Reserve some space for the information that our MORECORE needs. + /* Make the first page read/write. dlmalloc needs to use that page. */ - cs = base; + if (mprotect(base, starting_capacity, PROT_READ | PROT_WRITE) < 0) { + goto error; + } - /* Create the mspace, pointing to the memory we just reserved. + /* Create the mspace, pointing to the memory given. */ - m = create_mspace_with_base(base + sizeof(*cs), starting_capacity, locked); - if (m == (mspace)0) + m = create_mspace_with_base((char *)base + sizeof(*cs), starting_capacity, + locked); + if (m == (mspace)0) { goto error; - - /* Make sure that m is in the same page as cs. + } + /* Make sure that m is in the same page as base. */ assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)base); + /* Use some space for the information that our MORECORE needs. + */ + cs = (struct mspace_contig_state *)base; /* Find out exactly how much of the memory the mspace * is using. */ cs->brk = m->seg.base + m->seg.size; cs->top = (char *)base + max_capacity; + assert((char *)base <= cs->brk); assert(cs->brk <= cs->top); - /* Prevent access to the memory we haven't handed out yet. */ if (cs->brk != cs->top) { @@ -202,8 +182,10 @@ mspace create_contiguous_mspace_with_name(size_t starting_capacity, * for cs->brk not to be page-aligned at this point. */ char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize); - if (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0) + if ((mprotect(base, prot_brk - (char *)base, PROT_READ | PROT_WRITE) < 0) || + (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)) { goto error; + } } cs->m = m; @@ -212,10 +194,56 @@ mspace create_contiguous_mspace_with_name(size_t starting_capacity, return (mspace)m; error: - munmap(base, max_capacity); return (mspace)0; } + +mspace create_contiguous_mspace_with_name(size_t starting_capacity, + size_t max_capacity, int locked, char const *name) { + int fd, ret; + char buf[ASHMEM_NAME_LEN] = "mspace"; + void *base; + unsigned int pagesize; + mstate m; + + if (starting_capacity > max_capacity) + return (mspace)0; + + init_mparams(); + pagesize = PAGESIZE; + + /* Create the anonymous memory that will back the mspace. + * This reserves all of the virtual address space we could + * ever need. Physical pages will be mapped as the memory + * is touched. + * + * Align max_capacity to a whole page. + */ + max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize); + + if (name) + snprintf(buf, sizeof(buf), "mspace/%s", name); + fd = ashmem_create_region(buf, max_capacity); + if (fd < 0) + return (mspace)0; + + base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + close(fd); + if (base == MAP_FAILED) + return (mspace)0; + + /* Make sure that base is at the beginning of a page. + */ + assert(((uintptr_t)base & (pagesize-1)) == 0); + + m = create_contiguous_mspace_with_base(starting_capacity, max_capacity, + locked, base); + if (m == 0) { + munmap(base, max_capacity); + } + return m; +} + mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity, int locked) { return create_contiguous_mspace_with_name(starting_capacity, @@ -243,4 +271,16 @@ size_t destroy_contiguous_mspace(mspace msp) { } return 0; } + +void *contiguous_mspace_sbrk0(mspace msp) { + struct mspace_contig_state *cs; + mstate ms; + const unsigned int pagesize = PAGESIZE; + + ms = (mstate)msp; + cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1)); + assert(cs->magic == CONTIG_STATE_MAGIC); + assert(cs->m == ms); + return cs->brk; +} #endif diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c new file mode 100644 index 00000000000..5b4388af736 --- /dev/null +++ b/libcutils/open_memstream.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HAVE_OPEN_MEMSTREAM + +/* + * Implementation of the POSIX open_memstream() function, which Linux has + * but BSD lacks. + * + * Summary: + * - Works like a file-backed FILE* opened with fopen(name, "w"), but the + * backing is a chunk of memory rather than a file. + * - The buffer expands as you write more data. Seeking past the end + * of the file and then writing to it zero-fills the gap. + * - The values at "*bufp" and "*sizep" should be considered read-only, + * and are only valid immediately after an fflush() or fclose(). + * - A '\0' is maintained just past the end of the file. This is not included + * in "*sizep". (The behavior w.r.t. fseek() is not clearly defined. + * The spec says the null byte is written when a write() advances EOF, + * but it looks like glibc ensures the null byte is always found at EOF, + * even if you just seeked backwards. The example on the opengroup.org + * page suggests that this is the expected behavior. The null must be + * present after a no-op fflush(), which we can't see, so we have to save + * and restore it. Annoying, but allows file truncation.) + * - After fclose(), the caller must eventually free(*bufp). + * + * This is built out of funopen(), which BSD has but Linux lacks. There is + * no flush() operator, so we need to keep the user pointers up to date + * after each operation. + * + * I don't think Windows has any of the above, but we don't need to use + * them there, so we just supply a stub. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +# define DBUG(x) printf x +#else +# define DBUG(x) ((void)0) +#endif + +#ifdef HAVE_FUNOPEN + +/* + * Definition of a seekable, write-only memory stream. + */ +typedef struct { + char** bufp; /* pointer to buffer pointer */ + size_t* sizep; /* pointer to eof */ + + size_t allocSize; /* size of buffer */ + size_t eof; /* furthest point we've written to */ + size_t offset; /* current write offset */ + char saved; /* required by NUL handling */ +} MemStream; + +#define kInitialSize 1024 + +/* + * Ensure that we have enough storage to write "size" bytes at the + * current offset. We also have to take into account the extra '\0' + * that we maintain just past EOF. + * + * Returns 0 on success. + */ +static int ensureCapacity(MemStream* stream, int writeSize) +{ + DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize)); + + size_t neededSize = stream->offset + writeSize + 1; + if (neededSize <= stream->allocSize) + return 0; + + size_t newSize; + + if (stream->allocSize == 0) { + newSize = kInitialSize; + } else { + newSize = stream->allocSize; + newSize += newSize / 2; /* expand by 3/2 */ + } + + if (newSize < neededSize) + newSize = neededSize; + DBUG(("+++ realloc %p->%p to size=%d\n", + stream->bufp, *stream->bufp, newSize)); + char* newBuf = (char*) realloc(*stream->bufp, newSize); + if (newBuf == NULL) + return -1; + + *stream->bufp = newBuf; + stream->allocSize = newSize; + return 0; +} + +/* + * Write data to a memstream, expanding the buffer if necessary. + * + * If we previously seeked beyond EOF, zero-fill the gap. + * + * Returns the number of bytes written. + */ +static int write_memstream(void* cookie, const char* buf, int size) +{ + MemStream* stream = (MemStream*) cookie; + + if (ensureCapacity(stream, size) < 0) + return -1; + + /* seeked past EOF earlier? */ + if (stream->eof < stream->offset) { + DBUG(("+++ zero-fill gap from %d to %d\n", + stream->eof, stream->offset-1)); + memset(*stream->bufp + stream->eof, '\0', + stream->offset - stream->eof); + } + + /* copy data, advance write pointer */ + memcpy(*stream->bufp + stream->offset, buf, size); + stream->offset += size; + + if (stream->offset > stream->eof) { + /* EOF has advanced, update it and append null byte */ + DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset)); + assert(stream->offset < stream->allocSize); + stream->eof = stream->offset; + } else { + /* within previously-written area; save char we're about to stomp */ + DBUG(("+++ within written area, saving '%c' at %d\n", + *(*stream->bufp + stream->offset), stream->offset)); + stream->saved = *(*stream->bufp + stream->offset); + } + *(*stream->bufp + stream->offset) = '\0'; + *stream->sizep = stream->offset; + + return size; +} + +/* + * Seek within a memstream. + * + * Returns the new offset, or -1 on failure. + */ +static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence) +{ + MemStream* stream = (MemStream*) cookie; + off_t newPosn = (off_t) offset; + + if (whence == SEEK_CUR) { + newPosn += stream->offset; + } else if (whence == SEEK_END) { + newPosn += stream->eof; + } + + if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) { + /* bad offset - negative or huge */ + DBUG(("+++ bogus seek offset %ld\n", (long) newPosn)); + errno = EINVAL; + return (fpos_t) -1; + } + + if (stream->offset < stream->eof) { + /* + * We were pointing to an area we'd already written to, which means + * we stomped on a character and must now restore it. + */ + DBUG(("+++ restoring char '%c' at %d\n", + stream->saved, stream->offset)); + *(*stream->bufp + stream->offset) = stream->saved; + } + + stream->offset = (size_t) newPosn; + + if (stream->offset < stream->eof) { + /* + * We're seeked backward into the stream. Preserve the character + * at EOF and stomp it with a NUL. + */ + stream->saved = *(*stream->bufp + stream->offset); + *(*stream->bufp + stream->offset) = '\0'; + *stream->sizep = stream->offset; + } else { + /* + * We're positioned at, or possibly beyond, the EOF. We want to + * publish the current EOF, not the current position. + */ + *stream->sizep = stream->eof; + } + + return newPosn; +} + +/* + * Close the memstream. We free everything but the data buffer. + */ +static int close_memstream(void* cookie) +{ + free(cookie); + return 0; +} + +/* + * Prepare a memstream. + */ +FILE* open_memstream(char** bufp, size_t* sizep) +{ + FILE* fp; + MemStream* stream; + + if (bufp == NULL || sizep == NULL) { + errno = EINVAL; + return NULL; + } + + stream = (MemStream*) calloc(1, sizeof(MemStream)); + if (stream == NULL) + return NULL; + + fp = funopen(stream, + NULL, write_memstream, seek_memstream, close_memstream); + if (fp == NULL) { + free(stream); + return NULL; + } + + *sizep = 0; + *bufp = NULL; + stream->bufp = bufp; + stream->sizep = sizep; + + return fp; +} + +#else /*not HAVE_FUNOPEN*/ +FILE* open_memstream(char** bufp, size_t* sizep) +{ + abort(); +} +#endif /*HAVE_FUNOPEN*/ + + + +#if 0 +#define _GNU_SOURCE +#include +#include +#include + +/* + * Simple regression test. + * + * To test on desktop Linux with valgrind, it's possible to make a simple + * change to open_memstream() to use fopencookie instead: + * + * cookie_io_functions_t iofuncs = + * { NULL, write_memstream, seek_memstream, close_memstream }; + * fp = fopencookie(stream, "w", iofuncs); + * + * (Some tweaks to seek_memstream are also required, as that takes a + * pointer to an offset rather than an offset, and returns 0 or -1.) + */ +int testMemStream(void) +{ + FILE *stream; + char *buf; + size_t len; + off_t eob; + + printf("Test1\n"); + + /* std example */ + stream = open_memstream(&buf, &len); + fprintf(stream, "hello my world"); + fflush(stream); + printf("buf=%s, len=%zu\n", buf, len); + eob = ftello(stream); + fseeko(stream, 0, SEEK_SET); + fprintf(stream, "good-bye"); + fseeko(stream, eob, SEEK_SET); + fclose(stream); + printf("buf=%s, len=%zu\n", buf, len); + free(buf); + + printf("Test2\n"); + + /* std example without final seek-to-end */ + stream = open_memstream(&buf, &len); + fprintf(stream, "hello my world"); + fflush(stream); + printf("buf=%s, len=%zu\n", buf, len); + eob = ftello(stream); + fseeko(stream, 0, SEEK_SET); + fprintf(stream, "good-bye"); + //fseeko(stream, eob, SEEK_SET); + fclose(stream); + printf("buf=%s, len=%zu\n", buf, len); + free(buf); + + printf("Test3\n"); + + /* fancy example; should expand buffer with writes */ + static const int kCmpLen = 1024 + 128; + char* cmp = malloc(kCmpLen); + memset(cmp, 0, 1024); + memset(cmp+1024, 0xff, kCmpLen-1024); + sprintf(cmp, "This-is-a-tes1234"); + sprintf(cmp + 1022, "abcdef"); + + stream = open_memstream (&buf, &len); + setvbuf(stream, NULL, _IONBF, 0); /* note: crashes in glibc with this */ + fprintf(stream, "This-is-a-test"); + fseek(stream, -1, SEEK_CUR); /* broken in glibc; can use {13,SEEK_SET} */ + fprintf(stream, "1234"); + fseek(stream, 1022, SEEK_SET); + fputc('a', stream); + fputc('b', stream); + fputc('c', stream); + fputc('d', stream); + fputc('e', stream); + fputc('f', stream); + fflush(stream); + + if (memcmp(buf, cmp, len+1) != 0) { + printf("mismatch\n"); + } else { + printf("match\n"); + } + + printf("Test4\n"); + stream = open_memstream (&buf, &len); + fseek(stream, 5000, SEEK_SET); + fseek(stream, 4096, SEEK_SET); + fseek(stream, -1, SEEK_SET); /* should have no effect */ + fputc('x', stream); + if (ftell(stream) == 4097) + printf("good\n"); + else + printf("BAD: offset is %ld\n", ftell(stream)); + + printf("DONE\n"); + + return 0; +} + +/* expected output: +Test1 +buf=hello my world, len=14 +buf=good-bye world, len=14 +Test2 +buf=hello my world, len=14 +buf=good-bye, len=8 +Test3 +match +Test4 +good +DONE +*/ + +#endif + +#endif /*!HAVE_OPEN_MEMSTREAM*/ diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c new file mode 100644 index 00000000000..10539fa5930 --- /dev/null +++ b/libcutils/partition_utils.c @@ -0,0 +1,67 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include /* for BLKGETSIZE */ +#include + +static int only_one_char(char *buf, int len, char c) +{ + int i, ret; + + ret = 1; + for (i=0; i -static int send_prop_msg(prop_msg *msg) +int property_set(const char *key, const char *value) { - int s; - int r; - - s = socket_local_client(PROP_SERVICE_NAME, - ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM); - if(s < 0) return -1; - - while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) { - if((errno == EINTR) || (errno == EAGAIN)) continue; - break; - } - - if(r == sizeof(prop_msg)) { - r = 0; - } else { - r = -1; - } - - close(s); - return r; + return __system_property_set(key, value); } -int property_set(const char *key, const char *value) +int property_set_sync(const char *key, const char *value) { - prop_msg msg; - unsigned resp; - - if(key == 0) return -1; - if(value == 0) value = ""; - - if(strlen(key) >= PROP_NAME_MAX) return -1; - if(strlen(value) >= PROP_VALUE_MAX) return -1; - - msg.cmd = PROP_MSG_SETPROP; - strcpy((char*) msg.name, key); - strcpy((char*) msg.value, value); - - return send_prop_msg(&msg); + return __system_property_set(key, value); } int property_get(const char *key, char *value, const char *default_value) @@ -133,7 +100,10 @@ static int connectToServer(const char* fileName) int sock = -1; int cc; - struct sockaddr_un addr; + union { + struct sockaddr_un un; + struct sockaddr generic; + } addr; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { @@ -142,9 +112,9 @@ static int connectToServer(const char* fileName) } /* connect to socket; fails if file doesn't exist */ - strcpy(addr.sun_path, fileName); // max 108 bytes - addr.sun_family = AF_UNIX; - cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr)); + strcpy(addr.un.sun_path, fileName); // max 108 bytes + addr.un.sun_family = AF_UNIX; + cc = connect(sock, &addr.generic, SUN_LEN(&addr.un)); if (cc < 0) { // ENOENT means socket file doesn't exist // ECONNREFUSED means socket exists but nobody is listening diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c new file mode 100644 index 00000000000..994ad3283ba --- /dev/null +++ b/libcutils/qtaguid.c @@ -0,0 +1,179 @@ +/* libcutils/qtaguid.c +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// #define LOG_NDEBUG 0 + +#define LOG_TAG "qtaguid" + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl"; +static const int CTRL_MAX_INPUT_LEN = 128; +static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive"; +static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive"; + +/* + * One per proccess. + * Once the device is open, this process will have its socket tags tracked. + * And on exit or untimely death, all socket tags will be removed. + * A process can only open /dev/xt_qtaguid once. + * It should not close it unless it is really done with all the socket tags. + * Failure to open it will be visible when socket tagging will be attempted. + */ +static int resTrackFd = -1; +pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT; + +/* Only call once per process. */ +void qtaguid_resTrack(void) { + resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY)); + if (resTrackFd >=0) { + TEMP_FAILURE_RETRY(fcntl(resTrackFd, F_SETFD, FD_CLOEXEC)); + } +} + +/* + * Returns: + * 0 on success. + * -errno on failure. + */ +static int write_ctrl(const char *cmd) { + int fd, res, savedErrno; + + LOGV("write_ctrl(%s)", cmd); + + fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY)); + if (fd < 0) { + return -errno; + } + + res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd))); + if (res < 0) { + savedErrno = errno; + } else { + savedErrno = 0; + } + if (res < 0) { + LOGI("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno); + } + close(fd); + return -savedErrno; +} + +static int write_param(const char *param_path, const char *value) { + int param_fd; + int res; + + param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY)); + if (param_fd < 0) { + return -errno; + } + res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value))); + if (res < 0) { + return -errno; + } + close(param_fd); + return 0; +} + +int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + /* Doing java-land a favor, enforcing "long" */ + uint64_t kTag = ((uint64_t)tag << 32) & ~(1LLU<<63); + + + pthread_once(&resTrackInitDone, qtaguid_resTrack); + + snprintf(lineBuf, sizeof(lineBuf), "t %d %llu %d", sockfd, kTag, uid); + + LOGV("Tagging socket %d with tag %llx{%u,0} for uid %d", sockfd, kTag, tag, uid); + + res = write_ctrl(lineBuf); + if (res < 0) { + LOGI("Tagging socket %d with tag %llx(%d) for uid %d failed errno=%d", + sockfd, kTag, tag, uid, res); + } + + return res; +} + +int qtaguid_untagSocket(int sockfd) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + + LOGV("Untagging socket %d", sockfd); + + snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd); + res = write_ctrl(lineBuf); + if (res < 0) { + LOGI("Untagging socket %d failed errno=%d", sockfd, res); + } + + return res; +} + +int qtaguid_setCounterSet(int counterSetNum, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + + LOGV("Setting counters to set %d for uid %d", counterSetNum, uid); + + snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid); + res = write_ctrl(lineBuf); + return res; +} + +int qtaguid_deleteTagData(int tag, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int fd, cnt = 0, res = 0; + uint64_t kTag = (uint64_t)tag << 32; + + LOGV("Deleting tag data with tag %llx{%d,0} for uid %d", kTag, tag, uid); + + pthread_once(&resTrackInitDone, qtaguid_resTrack); + + snprintf(lineBuf, sizeof(lineBuf), "d %llu %d", kTag, uid); + res = write_ctrl(lineBuf); + if (res < 0) { + LOGI("Deleteing tag data with tag %llx/%d for uid %d failed with cnt=%d errno=%d", + kTag, tag, uid, cnt, errno); + } + + return res; +} + +int qtaguid_setPacifier(int on) { + int param_fd; + int res; + const char *value; + + value = on ? "Y" : "N"; + if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) { + return -errno; + } + if (write_param(TAG_PACIFIER_PARAM, value) < 0) { + return -errno; + } + return 0; +} diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index 8134aa13aff..f9c111ed69f 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -27,8 +27,10 @@ #include "cutils/log.h" #ifdef HAVE_SCHED_H +#ifdef HAVE_PTHREADS #include +#include #include @@ -42,56 +44,83 @@ #define POLICY_DEBUG 0 +static pthread_once_t the_once = PTHREAD_ONCE_INIT; + static int __sys_supports_schedgroups = -1; -static int add_tid_to_cgroup(int tid, const char *grp_name) +// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error. +static int normal_cgroup_fd = -1; +static int bg_cgroup_fd = -1; + +/* Add tid to the scheduling group defined by the policy */ +static int add_tid_to_cgroup(int tid, SchedPolicy policy) { int fd; - char path[255]; - char text[64]; - sprintf(path, "/dev/cpuctl/%s/tasks", grp_name); + if (policy == SP_BACKGROUND) { + fd = bg_cgroup_fd; + } else { + fd = normal_cgroup_fd; + } - if ((fd = open(path, O_WRONLY)) < 0) { - LOGE("add_tid_to_cgroup failed to open '%s' (%s)\n", path, - strerror(errno)); + if (fd < 0) { + SLOGE("add_tid_to_cgroup failed; background=%d\n", + policy == SP_BACKGROUND ? 1 : 0); return -1; } - sprintf(text, "%d", tid); - if (write(fd, text, strlen(text)) < 0) { - close(fd); - /* - * If the thread is in the process of exiting, - * don't flag an error - */ - if (errno == ESRCH) - return 0; - LOGW("add_tid_to_cgroup failed to write '%s' (%s)\n", path, - strerror(errno)); + // specialized itoa -- works for tid > 0 + char text[22]; + char *end = text + sizeof(text) - 1; + char *ptr = end; + *ptr = '\0'; + while (tid > 0) { + *--ptr = '0' + (tid % 10); + tid = tid / 10; + } + + if (write(fd, ptr, end - ptr) < 0) { + /* + * If the thread is in the process of exiting, + * don't flag an error + */ + if (errno == ESRCH) + return 0; + SLOGW("add_tid_to_cgroup failed to write '%s' (%s); background=%d\n", + ptr, strerror(errno), policy == SP_BACKGROUND ? 1 : 0); return -1; } - close(fd); return 0; } -static inline void initialize() -{ - if (__sys_supports_schedgroups < 0) { - if (!access("/dev/cpuctl/tasks", F_OK)) { - __sys_supports_schedgroups = 1; - } else { - __sys_supports_schedgroups = 0; +static void __initialize(void) { + char* filename; + if (!access("/dev/cpuctl/tasks", F_OK)) { + __sys_supports_schedgroups = 1; + + filename = "/dev/cpuctl/tasks"; + normal_cgroup_fd = open(filename, O_WRONLY); + if (normal_cgroup_fd < 0) { + SLOGE("open of %s failed: %s\n", filename, strerror(errno)); + } + + filename = "/dev/cpuctl/bg_non_interactive/tasks"; + bg_cgroup_fd = open(filename, O_WRONLY); + if (bg_cgroup_fd < 0) { + SLOGE("open of %s failed: %s\n", filename, strerror(errno)); } + } else { + __sys_supports_schedgroups = 0; } } /* * Try to get the scheduler group. * - * The data from /proc//cgroup looks like: + * The data from /proc//cgroup looks (something) like: * 2:cpu:/bg_non_interactive + * 1:cpuacct:/ * * We return the part after the "/", which will be an empty string for * the default cgroup. If the string is longer than "bufLen", the string @@ -101,34 +130,57 @@ static int getSchedulerGroup(int tid, char* buf, size_t bufLen) { #ifdef HAVE_ANDROID_OS char pathBuf[32]; - char readBuf[256]; - ssize_t count; - int fd; + char lineBuf[256]; + FILE *fp; snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid); - if ((fd = open(pathBuf, O_RDONLY)) < 0) { + if (!(fp = fopen(pathBuf, "r"))) { return -1; } - count = read(fd, readBuf, sizeof(readBuf)); - if (count <= 0) { - close(fd); - errno = ENODATA; - return -1; - } - close(fd); + while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) { + char *next = lineBuf; + char *subsys; + char *grp; + size_t len; - readBuf[--count] = '\0'; /* remove the '\n', now count==strlen */ + /* Junk the first field */ + if (!strsep(&next, ":")) { + goto out_bad_data; + } - char* cp = strchr(readBuf, '/'); - if (cp == NULL) { - readBuf[sizeof(readBuf)-1] = '\0'; - errno = ENODATA; - return -1; + if (!(subsys = strsep(&next, ":"))) { + goto out_bad_data; + } + + if (strcmp(subsys, "cpu")) { + /* Not the subsys we're looking for */ + continue; + } + + if (!(grp = strsep(&next, ":"))) { + goto out_bad_data; + } + grp++; /* Drop the leading '/' */ + len = strlen(grp); + grp[len-1] = '\0'; /* Drop the trailing '\n' */ + + if (bufLen <= len) { + len = bufLen - 1; + } + strncpy(buf, grp, len); + buf[len] = '\0'; + fclose(fp); + return 0; } - memcpy(buf, cp+1, count); /* count-1 for cp+1, count+1 for NUL */ - return 0; + SLOGE("Failed to find cpu subsys"); + fclose(fp); + return -1; + out_bad_data: + SLOGE("Bad cgroup data {%s}", lineBuf); + fclose(fp); + return -1; #else errno = ENOSYS; return -1; @@ -137,7 +189,7 @@ static int getSchedulerGroup(int tid, char* buf, size_t bufLen) int get_sched_policy(int tid, SchedPolicy *policy) { - initialize(); + pthread_once(&the_once, __initialize); if (__sys_supports_schedgroups) { char grpBuf[32]; @@ -169,7 +221,7 @@ int get_sched_policy(int tid, SchedPolicy *policy) int set_sched_policy(int tid, SchedPolicy policy) { - initialize(); + pthread_once(&the_once, __initialize); #if POLICY_DEBUG char statfile[64]; @@ -195,22 +247,16 @@ int set_sched_policy(int tid, SchedPolicy policy) strncpy(thread_name, p, (q-p)); } if (policy == SP_BACKGROUND) { - LOGD("vvv tid %d (%s)", tid, thread_name); + SLOGD("vvv tid %d (%s)", tid, thread_name); } else if (policy == SP_FOREGROUND) { - LOGD("^^^ tid %d (%s)", tid, thread_name); + SLOGD("^^^ tid %d (%s)", tid, thread_name); } else { - LOGD("??? tid %d (%s)", tid, thread_name); + SLOGD("??? tid %d (%s)", tid, thread_name); } #endif if (__sys_supports_schedgroups) { - const char *grp = ""; - - if (policy == SP_BACKGROUND) { - grp = "bg_non_interactive"; - } - - if (add_tid_to_cgroup(tid, grp)) { + if (add_tid_to_cgroup(tid, policy)) { if (errno != ESRCH && errno != ENOENT) return -errno; } @@ -227,4 +273,5 @@ int set_sched_policy(int tid, SchedPolicy policy) return 0; } +#endif /* HAVE_PTHREADS */ #endif /* HAVE_SCHED_H */ diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c index 7d5dab4c8a5..d57db11893b 100644 --- a/libcutils/socket_inaddr_any_server.c +++ b/libcutils/socket_inaddr_any_server.c @@ -35,14 +35,17 @@ /* open listen() port on any interface */ int socket_inaddr_any_server(int port, int type) { - struct sockaddr_in addr; + union { + struct sockaddr_in in; + struct sockaddr generic; + } addr; size_t alen; int s, n; memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.in.sin_family = AF_INET; + addr.in.sin_port = htons(port); + addr.in.sin_addr.s_addr = htonl(INADDR_ANY); s = socket(AF_INET, type, 0); if(s < 0) return -1; @@ -50,7 +53,7 @@ int socket_inaddr_any_server(int port, int type) n = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); - if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if(bind(s, &addr.generic, sizeof(addr.in)) < 0) { close(s); return -1; } diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c index 036ce2ef13b..45466a17490 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client.c @@ -124,18 +124,21 @@ int socket_make_sockaddr_un(const char *name, int namespaceId, int socket_local_client_connect(int fd, const char *name, int namespaceId, int type) { - struct sockaddr_un addr; + union { + struct sockaddr_un un; + struct sockaddr generic; + } addr; socklen_t alen; size_t namelen; int err; - err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); + err = socket_make_sockaddr_un(name, namespaceId, &addr.un, &alen); if (err < 0) { goto error; } - if(connect(fd, (struct sockaddr *) &addr, alen) < 0) { + if(connect(fd, &addr.generic, alen) < 0) { goto error; } diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c index 4971b1b1cc5..830fd0f0b07 100644 --- a/libcutils/socket_local_server.c +++ b/libcutils/socket_local_server.c @@ -52,12 +52,15 @@ int socket_local_server(const char *name, int namespaceId, int type) */ int socket_local_server_bind(int s, const char *name, int namespaceId) { - struct sockaddr_un addr; + union { + struct sockaddr_un un; + struct sockaddr generic; + } addr; socklen_t alen; int n; int err; - err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); + err = socket_make_sockaddr_un(name, namespaceId, &addr.un, &alen); if (err < 0) { return -1; @@ -71,13 +74,13 @@ int socket_local_server_bind(int s, const char *name, int namespaceId) || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) { #endif /*ignore ENOENT*/ - unlink(addr.sun_path); + unlink(addr.un.sun_path); } n = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); - if(bind(s, (struct sockaddr *) &addr, alen) < 0) { + if(bind(s, &addr.generic, alen) < 0) { return -1; } diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c index cb82c5ede3b..f7c749c5dc1 100644 --- a/libcutils/socket_loopback_client.c +++ b/libcutils/socket_loopback_client.c @@ -36,19 +36,22 @@ */ int socket_loopback_client(int port, int type) { - struct sockaddr_in addr; + union { + struct sockaddr_in in; + struct sockaddr generic; + } addr; socklen_t alen; int s; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + memset(&addr.in, 0, sizeof(addr.in)); + addr.in.sin_family = AF_INET; + addr.in.sin_port = htons(port); + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); s = socket(AF_INET, type, 0); if(s < 0) return -1; - if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if(connect(s, &addr.generic, sizeof(addr.in)) < 0) { close(s); return -1; } diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c index 3208488adde..bed6f94ba41 100644 --- a/libcutils/socket_loopback_server.c +++ b/libcutils/socket_loopback_server.c @@ -35,14 +35,17 @@ /* open listen() port on loopback interface */ int socket_loopback_server(int port, int type) { - struct sockaddr_in addr; + union { + struct sockaddr_in in; + struct sockaddr generic; + } addr; size_t alen; int s, n; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + memset(&addr.in, 0, sizeof(addr.in)); + addr.in.sin_family = AF_INET; + addr.in.sin_port = htons(port); + addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); s = socket(AF_INET, type, 0); if(s < 0) return -1; @@ -50,7 +53,7 @@ int socket_loopback_server(int port, int type) n = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); - if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if(bind(s, &addr.generic, sizeof(addr.in)) < 0) { close(s); return -1; } diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c index a64006cdf4d..37e1c6ccb01 100644 --- a/libcutils/socket_network_client.c +++ b/libcutils/socket_network_client.c @@ -39,22 +39,25 @@ int socket_network_client(const char *host, int port, int type) { struct hostent *hp; - struct sockaddr_in addr; + union { + struct sockaddr_in in; + struct sockaddr generic; + } addr; socklen_t alen; int s; hp = gethostbyname(host); if(hp == 0) return -1; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = hp->h_addrtype; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); + memset(&addr.in, 0, sizeof(addr.in)); + addr.in.sin_family = hp->h_addrtype; + addr.in.sin_port = htons(port); + memcpy(&addr.in.sin_addr, hp->h_addr, hp->h_length); s = socket(hp->h_addrtype, type, 0); if(s < 0) return -1; - if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if(connect(s, &addr.generic, sizeof(addr.in)) < 0) { close(s); return -1; } diff --git a/libcutils/sockets.c b/libcutils/sockets.c new file mode 100644 index 00000000000..101a382f2ad --- /dev/null +++ b/libcutils/sockets.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifdef HAVE_ANDROID_OS +/* For the socket trust (credentials) check */ +#include +#endif + +bool socket_peer_is_trusted(int fd) +{ +#ifdef HAVE_ANDROID_OS + struct ucred cr; + socklen_t len = sizeof(cr); + int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + + if (n != 0) { + LOGE("could not get socket credentials: %s\n", strerror(errno)); + return false; + } + + if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) { + LOGE("untrusted userid on other end of socket: userid %d\n", cr.uid); + return false; + } +#endif + + return true; +} diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c new file mode 100644 index 00000000000..dfa1f737c67 --- /dev/null +++ b/libcutils/str_parms.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "str_params" +//#define LOG_NDEBUG 0 + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct str_parms { + Hashmap *map; +}; + + +static bool str_eq(void *key_a, void *key_b) +{ + return !strcmp((const char *)key_a, (const char *)key_b); +} + +/* use djb hash unless we find it inadequate */ +static int str_hash_fn(void *str) +{ + uint32_t hash = 5381; + char *p; + + for (p = str; p && *p; p++) + hash = ((hash << 5) + hash) + *p; + return (int)hash; +} + +struct str_parms *str_parms_create(void) +{ + struct str_parms *str_parms; + + str_parms = calloc(1, sizeof(struct str_parms)); + if (!str_parms) + return NULL; + + str_parms->map = hashmapCreate(5, str_hash_fn, str_eq); + if (!str_parms->map) + goto err; + + return str_parms; + +err: + free(str_parms); + return NULL; +} + +static bool remove_pair(void *key, void *value, void *context) +{ + struct str_parms *str_parms = context; + + hashmapRemove(str_parms->map, key); + free(key); + free(value); + return true; +} + +void str_parms_destroy(struct str_parms *str_parms) +{ + hashmapForEach(str_parms->map, remove_pair, str_parms); + hashmapFree(str_parms->map); + free(str_parms); +} + +struct str_parms *str_parms_create_str(const char *_string) +{ + struct str_parms *str_parms; + char *str; + char *kvpair; + char *tmpstr; + int items = 0; + + str_parms = str_parms_create(); + if (!str_parms) + goto err_create_str_parms; + + str = strdup(_string); + if (!str) + goto err_strdup; + + LOGV("%s: source string == '%s'\n", __func__, _string); + + kvpair = strtok_r(str, ";", &tmpstr); + while (kvpair && *kvpair) { + char *eq = strchr(kvpair, '='); /* would love strchrnul */ + char *value; + char *key; + void *old_val; + + if (eq == kvpair) + goto next_pair; + + if (eq) { + key = strndup(kvpair, eq - kvpair); + if (*(++eq)) + value = strdup(eq); + else + value = strdup(""); + } else { + key = strdup(kvpair); + value = strdup(""); + } + + /* if we replaced a value, free it */ + old_val = hashmapPut(str_parms->map, key, value); + if (old_val) + free(old_val); + + items++; +next_pair: + kvpair = strtok_r(NULL, ";", &tmpstr); + } + + if (!items) + LOGV("%s: no items found in string\n", __func__); + + free(str); + + return str_parms; + +err_strdup: + str_parms_destroy(str_parms); +err_create_str_parms: + return NULL; +} + +void str_parms_del(struct str_parms *str_parms, const char *key) +{ + hashmapRemove(str_parms->map, (void *)key); +} + +int str_parms_add_str(struct str_parms *str_parms, const char *key, + const char *value) +{ + void *old_val; + char *tmp; + + tmp = strdup(value); + old_val = hashmapPut(str_parms->map, (void *)key, tmp); + + if (old_val) { + free(old_val); + } else if (errno == ENOMEM) { + free(tmp); + return -ENOMEM; + } + return 0; +} + +int str_parms_add_int(struct str_parms *str_parms, const char *key, int value) +{ + char val_str[12]; + int ret; + + ret = snprintf(val_str, sizeof(val_str), "%d", value); + if (ret < 0) + return -EINVAL; + + ret = str_parms_add_str(str_parms, key, val_str); + return ret; +} + +int str_parms_add_float(struct str_parms *str_parms, const char *key, + float value) +{ + char val_str[23]; + int ret; + + ret = snprintf(val_str, sizeof(val_str), "%.10f", value); + if (ret < 0) + return -EINVAL; + + ret = str_parms_add_str(str_parms, key, val_str); + return ret; +} + +int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val, + int len) +{ + char *value; + + value = hashmapGet(str_parms->map, (void *)key); + if (value) + return strlcpy(val, value, len); + + return -ENOENT; +} + +int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val) +{ + char *value; + char *end; + + value = hashmapGet(str_parms->map, (void *)key); + if (!value) + return -ENOENT; + + *val = (int)strtol(value, &end, 0); + if (*value != '\0' && *end == '\0') + return 0; + + return -EINVAL; +} + +int str_parms_get_float(struct str_parms *str_parms, const char *key, + float *val) +{ + float out; + char *value; + char *end; + + value = hashmapGet(str_parms->map, (void *)key); + if (!value) + return -ENOENT; + + out = strtof(value, &end); + if (*value != '\0' && *end == '\0') + return 0; + + return -EINVAL; +} + +static bool combine_strings(void *key, void *value, void *context) +{ + char **old_str = context; + char *new_str; + int ret; + + ret = asprintf(&new_str, "%s%s%s=%s", + *old_str ? *old_str : "", + *old_str ? ";" : "", + (char *)key, + (char *)value); + if (*old_str) + free(*old_str); + + if (ret >= 0) { + *old_str = new_str; + return true; + } + + *old_str = NULL; + return false; +} + +char *str_parms_to_str(struct str_parms *str_parms) +{ + char *str = NULL; + + if (hashmapSize(str_parms->map) > 0) + hashmapForEach(str_parms->map, combine_strings, &str); + else + str = strdup(""); + return str; +} + +static bool dump_entry(void *key, void *value, void *context) +{ + LOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value); + return true; +} + +void str_parms_dump(struct str_parms *str_parms) +{ + hashmapForEach(str_parms->map, dump_entry, str_parms); +} + +#ifdef TEST_STR_PARMS +static void test_str_parms_str(const char *str) +{ + struct str_parms *str_parms; + char *out_str; + int ret; + + str_parms = str_parms_create_str(str); + str_parms_dump(str_parms); + out_str = str_parms_to_str(str_parms); + str_parms_destroy(str_parms); + LOGI("%s: '%s' stringified is '%s'", __func__, str, out_str); + free(out_str); +} + +int main(void) +{ + struct str_parms *str_parms; + + test_str_parms_str(""); + test_str_parms_str(";"); + test_str_parms_str("="); + test_str_parms_str("=;"); + test_str_parms_str("=bar"); + test_str_parms_str("=bar;"); + test_str_parms_str("foo="); + test_str_parms_str("foo=;"); + test_str_parms_str("foo=bar"); + test_str_parms_str("foo=bar;"); + test_str_parms_str("foo=bar;baz"); + test_str_parms_str("foo=bar;baz="); + test_str_parms_str("foo=bar;baz=bat"); + test_str_parms_str("foo=bar;baz=bat;"); + + return 0; +} +#endif diff --git a/libcutils/uevent.c b/libcutils/uevent.c new file mode 100644 index 00000000000..c3d9626eb2f --- /dev/null +++ b/libcutils/uevent.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Like recv(), but checks that messages actually originate from the kernel. + */ +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) { + struct iovec iov = { buffer, length }; + struct sockaddr_nl addr; + char control[CMSG_SPACE(sizeof(struct ucred))]; + struct msghdr hdr = { + &addr, + sizeof(addr), + &iov, + 1, + control, + sizeof(control), + 0, + }; + + ssize_t n = recvmsg(socket, &hdr, 0); + if (n <= 0) { + return n; + } + + if (addr.nl_groups == 0 || addr.nl_pid != 0) { + /* ignoring non-kernel or unicast netlink message */ + goto out; + } + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + /* ignoring netlink message with no sender credentials */ + goto out; + } + + struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + /* ignoring netlink message from non-root user */ + goto out; + } + + return n; + +out: + /* clear residual potentially malicious data */ + bzero(buffer, length); + errno = EIO; + return -1; +} + +int uevent_open_socket(int buf_sz, bool passcred) +{ + union { + struct sockaddr_nl nl; + struct sockaddr generic; + } addr; + int on = passcred; + int s; + + memset(&addr.nl, 0, sizeof(addr.nl)); + addr.nl.nl_family = AF_NETLINK; + addr.nl.nl_pid = getpid(); + addr.nl.nl_groups = 0xffffffff; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(s < 0) + return -1; + + setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)); + setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + if(bind(s, &addr.generic, sizeof(addr.nl)) < 0) { + close(s); + return -1; + } + + return s; +} diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk new file mode 100644 index 00000000000..9decbb91aef --- /dev/null +++ b/libdiskconfig/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +commonSources := \ + diskconfig.c \ + diskutils.c \ + write_lst.c \ + config_mbr.c + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(commonSources) +LOCAL_MODULE := libdiskconfig +LOCAL_MODULE_TAGS := optional +LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc +include $(BUILD_SHARED_LIBRARY) + +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(commonSources) +LOCAL_MODULE := libdiskconfig_host +LOCAL_MODULE_TAGS := optional +LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils +LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE +include $(BUILD_HOST_STATIC_LIBRARY) +endif # HOST_OS == linux diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c new file mode 100644 index 00000000000..825ba60292a --- /dev/null +++ b/libdiskconfig/config_mbr.c @@ -0,0 +1,325 @@ +/* libs/diskconfig/diskconfig.c + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "config_mbr" +#include +#include +#include +#include + +#include + +#include + + +/* start and len are in LBA units */ +static void +cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type, + uint32_t start, uint32_t len) +{ + if (len > 0) { + /* seems that somes BIOSens can get wedged on boot while verifying + * the mbr if these are 0 */ + memset(&pentry->start, 0xff, sizeof(struct chs)); + memset(&pentry->end, 0xff, sizeof(struct chs)); + } else { + /* zero out the c/h/s entries.. they are not used */ + memset(&pentry->start, 0, sizeof(struct chs)); + memset(&pentry->end, 0, sizeof(struct chs)); + } + + pentry->status = status; + pentry->type = type; + pentry->start_lba = start; + pentry->len_lba = len; + + LOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u", + pentry->status, pentry->type, pentry->start_lba, pentry->len_lba); +} + + +static inline uint32_t +kb_to_lba(uint32_t len_kb, uint32_t sect_size) +{ + uint64_t lba; + + lba = (uint64_t)len_kb * 1024; + /* bump it up to the next LBA boundary just in case */ + lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1); + lba /= (uint64_t)sect_size; + if (lba >= 0xffffffffULL) + LOGE("Error converting kb -> lba. 32bit overflow, expect weirdness"); + return (uint32_t)(lba & 0xffffffffULL); +} + + +static struct write_list * +mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum, + uint32_t *lba) +{ + struct write_list *item; + struct pc_partition *pentry; + + if (pnum >= PC_NUM_BOOT_RECORD_PARTS) { + LOGE("Maximum number of primary partition exceeded."); + return NULL; + } + + if (!(item = alloc_wl(sizeof(struct pc_partition)))) { + LOGE("Unable to allocate memory for partition entry."); + return NULL; + } + + { + /* DO NOT DEREFERENCE */ + struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; + /* grab the offset in mbr where to write this partition entry. */ + item->offset = (loff_t)((uint32_t)((uint8_t *)(&mbr->ptable[pnum]))); + } + + pentry = (struct pc_partition *) &item->data; + + /* need a standard primary partition entry */ + if (pinfo) { + /* need this to be 64 bit in case len_kb is large */ + uint64_t len_lba; + + if (pinfo->len_kb != (uint32_t)-1) { + /* bump it up to the next LBA boundary just in case */ + len_lba = ((uint64_t)pinfo->len_kb * 1024); + len_lba += ((uint64_t)dinfo->sect_size - 1); + len_lba &= ~((uint64_t)dinfo->sect_size - 1); + len_lba /= (uint64_t)dinfo->sect_size; + } else { + /* make it fill the rest of disk */ + len_lba = dinfo->num_lba - *lba; + } + + cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ? + PC_PART_ACTIVE : PC_PART_NORMAL), + pinfo->type, *lba, (uint32_t)len_lba); + + pinfo->start_lba = *lba; + *lba += (uint32_t)len_lba; + } else { + /* this should be made an extended partition, and should take + * up the rest of the disk as a primary partition */ + cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED, + *lba, dinfo->num_lba - *lba); + + /* note that we do not update the *lba because we now have to + * create a chain of extended partition tables, and first one is at + * *lba */ + } + + return item; +} + + +/* This function configures an extended boot record at the beginning of an + * extended partition. This creates a logical partition and a pointer to + * the next EBR. + * + * ext_lba == The start of the toplevel extended partition (pointed to by the + * entry in the MBR). + */ +static struct write_list * +mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba, + uint32_t ext_lba, struct part_info *pnext) +{ + struct write_list *item; + struct pc_boot_record *ebr; + uint32_t len; /* in lba units */ + + if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) { + LOGE("Unable to allocate memory for EBR."); + return NULL; + } + + /* we are going to write the ebr at the current LBA, and then bump the + * lba counter since that is where the logical data partition will start */ + item->offset = (*lba) * dinfo->sect_size; + (*lba)++; + + ebr = (struct pc_boot_record *) &item->data; + memset(ebr, 0, sizeof(struct pc_boot_record)); + ebr->mbr_sig = PC_BIOS_BOOT_SIG; + + if (pinfo->len_kb != (uint32_t)-1) + len = kb_to_lba(pinfo->len_kb, dinfo->sect_size); + else { + if (pnext) { + LOGE("Only the last partition can be specified to fill the disk " + "(name = '%s')", pinfo->name); + goto fail; + } + len = dinfo->num_lba - *lba; + /* update the pinfo structure to reflect the new size, for + * bookkeeping */ + pinfo->len_kb = + (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) / + ((uint64_t)1024)); + } + + cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL, + pinfo->type, 1, len); + + pinfo->start_lba = *lba; + *lba += len; + + /* If this is not the last partition, we have to create a link to the + * next extended partition. + * + * Otherwise, there's nothing to do since the "pointer entry" is + * already zero-filled. + */ + if (pnext) { + /* The start lba for next partition is an offset from the beginning + * of the top-level extended partition */ + uint32_t next_start_lba = *lba - ext_lba; + uint32_t next_len_lba; + if (pnext->len_kb != (uint32_t)-1) + next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size); + else + next_len_lba = dinfo->num_lba - *lba; + cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL, + PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba); + } + + return item; + +fail: + free_wl(item); + return NULL; +} + + +struct write_list * +config_mbr(struct disk_info *dinfo) +{ + struct part_info *pinfo; + uint32_t cur_lba = dinfo->skip_lba; + uint32_t ext_lba = 0; + struct write_list *wr_list = NULL; + struct write_list *temp_wr = NULL; + int cnt = 0; + int extended = 0; + + if (!dinfo->part_lst) + return NULL; + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + pinfo = &dinfo->part_lst[cnt]; + + /* Should we create an extedned partition? */ + if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) { + if (cnt + 1 < dinfo->num_parts) { + extended = 1; + ext_lba = cur_lba; + if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba))) + wlist_add(&wr_list, temp_wr); + else { + LOGE("Cannot create primary extended partition."); + goto fail; + } + } + } + + /* if extended, need 1 lba for ebr */ + if ((cur_lba + extended) >= dinfo->num_lba) + goto nospace; + else if (pinfo->len_kb != (uint32_t)-1) { + uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024; + if ((cur_lba + sz_lba + extended) > dinfo->num_lba) + goto nospace; + } + + if (!extended) + temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba); + else { + struct part_info *pnext; + pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL; + temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext); + } + + if (temp_wr) + wlist_add(&wr_list, temp_wr); + else { + LOGE("Cannot create partition %d (%s).", cnt, pinfo->name); + goto fail; + } + } + + /* fill in the rest of the MBR with empty parts (if needed). */ + for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) { + struct part_info blank; + cur_lba = 0; + memset(&blank, 0, sizeof(struct part_info)); + if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) { + LOGE("Cannot create blank partition %d.", cnt); + goto fail; + } + wlist_add(&wr_list, temp_wr); + } + + return wr_list; + +nospace: + LOGE("Not enough space to add parttion '%s'.", pinfo->name); + +fail: + wlist_free(wr_list); + return NULL; +} + + +/* Returns the device path of the partition referred to by 'name' + * Must be freed by the caller. + */ +char * +find_mbr_part(struct disk_info *dinfo, const char *name) +{ + struct part_info *plist = dinfo->part_lst; + int num = 0; + char *dev_name = NULL; + int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS); + + for(num = 1; num <= dinfo->num_parts; ++num) { + if (!strcmp(plist[num-1].name, name)) + break; + } + + if (num > dinfo->num_parts) + return NULL; + + if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS)) + num++; + + if (!(dev_name = malloc(MAX_NAME_LEN))) { + LOGE("Cannot allocate memory."); + return NULL; + } + + num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num); + if (num >= MAX_NAME_LEN) { + LOGE("Device name is too long?!"); + free(dev_name); + return NULL; + } + + return dev_name; +} diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c new file mode 100644 index 00000000000..4dd8c526518 --- /dev/null +++ b/libdiskconfig/diskconfig.c @@ -0,0 +1,536 @@ +/* libs/diskconfig/diskconfig.c + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "diskconfig" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +static int +parse_len(const char *str, uint64_t *plen) +{ + char tmp[64]; + int len_str; + uint32_t multiple = 1; + + strncpy(tmp, str, sizeof(tmp)); + tmp[sizeof(tmp)-1] = '\0'; + len_str = strlen(tmp); + if (!len_str) { + LOGE("Invalid disk length specified."); + return 1; + } + + switch(tmp[len_str - 1]) { + case 'M': case 'm': + /* megabyte */ + multiple <<= 10; + case 'K': case 'k': + /* kilobytes */ + multiple <<= 10; + tmp[len_str - 1] = '\0'; + break; + default: + break; + } + + *plen = strtoull(tmp, NULL, 0); + if (!*plen) { + LOGE("Invalid length specified: %s", str); + return 1; + } + + if (*plen == (uint64_t)-1) { + if (multiple > 1) { + LOGE("Size modifier illegal when len is -1"); + return 1; + } + } else { + /* convert len to kilobytes */ + if (multiple > 1024) + multiple >>= 10; + *plen *= multiple; + + if (*plen > 0xffffffffULL) { + LOGE("Length specified is too large!: %llu KB", *plen); + return 1; + } + } + + return 0; +} + + +static int +load_partitions(cnode *root, struct disk_info *dinfo) +{ + cnode *partnode; + + dinfo->num_parts = 0; + for (partnode = root->first_child; partnode; partnode = partnode->next) { + struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts]; + const char *tmp; + + /* bleh, i will leak memory here, but i DONT CARE since + * the only right thing to do when this function fails + * is to quit */ + pinfo->name = strdup(partnode->name); + + if(config_bool(partnode, "active", 0)) + pinfo->flags |= PART_ACTIVE_FLAG; + + if (!(tmp = config_str(partnode, "type", NULL))) { + LOGE("Partition type required: %s", pinfo->name); + return 1; + } + + /* possible values are: linux, fat32 */ + if (!strcmp(tmp, "linux")) { + pinfo->type = PC_PART_TYPE_LINUX; + } else if (!strcmp(tmp, "fat32")) { + pinfo->type = PC_PART_TYPE_FAT32; + } else { + LOGE("Unsupported partition type found: %s", tmp); + return 1; + } + + if ((tmp = config_str(partnode, "len", NULL)) != NULL) { + uint64_t len; + if (parse_len(tmp, &len)) + return 1; + pinfo->len_kb = (uint32_t) len; + } else + pinfo->len_kb = 0; + + ++dinfo->num_parts; + } + + return 0; +} + +struct disk_info * +load_diskconfig(const char *fn, char *path_override) +{ + struct disk_info *dinfo; + cnode *devroot; + cnode *partnode; + cnode *root = config_node("", ""); + const char *tmp; + + if (!(dinfo = malloc(sizeof(struct disk_info)))) { + LOGE("Could not malloc disk_info"); + return NULL; + } + memset(dinfo, 0, sizeof(struct disk_info)); + + if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) { + LOGE("Could not malloc part_lst"); + goto fail; + } + memset(dinfo->part_lst, 0, + (MAX_NUM_PARTS * sizeof(struct part_info))); + + config_load_file(root, fn); + if (root->first_child == NULL) { + LOGE("Could not read config file %s", fn); + goto fail; + } + + if (!(devroot = config_find(root, "device"))) { + LOGE("Could not find device section in config file '%s'", fn); + goto fail; + } + + + if (!(tmp = config_str(devroot, "path", path_override))) { + LOGE("device path is requried"); + goto fail; + } + dinfo->device = strdup(tmp); + + /* find the partition scheme */ + if (!(tmp = config_str(devroot, "scheme", NULL))) { + LOGE("partition scheme is required"); + goto fail; + } else if (!strcmp(tmp, "mbr")) { + dinfo->scheme = PART_SCHEME_MBR; + } else if (!strcmp(tmp, "gpt")) { + LOGE("'gpt' partition scheme not supported yet."); + goto fail; + } else { + LOGE("Unknown partition scheme specified: %s", tmp); + goto fail; + } + + /* grab the sector size (in bytes) */ + tmp = config_str(devroot, "sector_size", "512"); + dinfo->sect_size = strtol(tmp, NULL, 0); + if (!dinfo->sect_size) { + LOGE("Invalid sector size: %s", tmp); + goto fail; + } + + /* first lba where the partitions will start on disk */ + if (!(tmp = config_str(devroot, "start_lba", NULL))) { + LOGE("start_lba must be provided"); + goto fail; + } + + if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) { + LOGE("Invalid starting LBA (or zero): %s", tmp); + goto fail; + } + + /* Number of LBAs on disk */ + if (!(tmp = config_str(devroot, "num_lba", NULL))) { + LOGE("num_lba is required"); + goto fail; + } + dinfo->num_lba = strtoul(tmp, NULL, 0); + + if (!(partnode = config_find(devroot, "partitions"))) { + LOGE("Device must specify partition list"); + goto fail; + } + + if (load_partitions(partnode, dinfo)) + goto fail; + + return dinfo; + +fail: + if (dinfo->part_lst) + free(dinfo->part_lst); + if (dinfo->device) + free(dinfo->device); + free(dinfo); + return NULL; +} + +static int +sync_ptable(int fd) +{ + struct stat stat; + int rv; + + sync(); + + if (fstat(fd, &stat)) { + LOGE("Cannot stat, errno=%d.", errno); + return -1; + } + + if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) { + LOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno); + return -1; + } + + return 0; +} + +/* This function verifies that the disk info provided is valid, and if so, + * returns an open file descriptor. + * + * This does not necessarily mean that it will later be successfully written + * though. If we use the pc-bios partitioning scheme, we must use extended + * partitions, which eat up some hd space. If the user manually provisioned + * every single partition, but did not account for the extra needed space, + * then we will later fail. + * + * TODO: Make validation more complete. + */ +static int +validate(struct disk_info *dinfo) +{ + int fd; + int sect_sz; + uint64_t disk_size; + uint64_t total_size; + int cnt; + struct stat stat; + + if (!dinfo) + return -1; + + if ((fd = open(dinfo->device, O_RDWR)) < 0) { + LOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno); + return -1; + } + + if (fstat(fd, &stat)) { + LOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno); + goto fail; + } + + + /* XXX: Some of the code below is kind of redundant and should probably + * be refactored a little, but it will do for now. */ + + /* Verify that we can operate on the device that was requested. + * We presently only support block devices and regular file images. */ + if (S_ISBLK(stat.st_mode)) { + /* get the sector size and make sure we agree */ + if (ioctl(fd, BLKSSZGET, §_sz) < 0) { + LOGE("Cannot get sector size (errno=%d)", errno); + goto fail; + } + + if (!sect_sz || sect_sz != dinfo->sect_size) { + LOGE("Device sector size is zero or sector sizes do not match!"); + goto fail; + } + + /* allow the user override the "disk size" if they provided num_lba */ + if (!dinfo->num_lba) { + if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) { + LOGE("Could not get block device size (errno=%d)", errno); + goto fail; + } + /* XXX: we assume that the disk has < 2^32 sectors :-) */ + dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size); + } else + disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size; + } else if (S_ISREG(stat.st_mode)) { + LOGI("Requesting operation on a regular file, not block device."); + if (!dinfo->sect_size) { + LOGE("Sector size for regular file images cannot be zero"); + goto fail; + } + if (dinfo->num_lba) + disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size; + else { + dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size); + disk_size = (uint64_t)stat.st_size; + } + } else { + LOGE("Device does not refer to a regular file or a block device!"); + goto fail; + } + +#if 1 + LOGV("Device/file %s: size=%llu bytes, num_lba=%u, sect_size=%d", + dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size); +#endif + + /* since this is our offset into the disk, we start off with that as + * our size of needed partitions */ + total_size = dinfo->skip_lba * dinfo->sect_size; + + /* add up all the partition sizes and make sure it fits */ + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + struct part_info *part = &dinfo->part_lst[cnt]; + if (part->len_kb != (uint32_t)-1) { + total_size += part->len_kb * 1024; + } else if (part->len_kb == 0) { + LOGE("Zero-size partition '%s' is invalid.", part->name); + goto fail; + } else { + /* the partition requests the rest of the disk. */ + if (cnt + 1 != dinfo->num_parts) { + LOGE("Only the last partition in the list can request to fill " + "the rest of disk."); + goto fail; + } + } + + if ((part->type != PC_PART_TYPE_LINUX) && + (part->type != PC_PART_TYPE_FAT32)) { + LOGE("Unknown partition type (0x%x) encountered for partition " + "'%s'\n", part->type, part->name); + goto fail; + } + } + + /* only matters for disks, not files */ + if (S_ISBLK(stat.st_mode) && total_size > disk_size) { + LOGE("Total requested size of partitions (%llu) is greater than disk " + "size (%llu).", total_size, disk_size); + goto fail; + } + + return fd; + +fail: + close(fd); + return -1; +} + +static int +validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst) +{ + *lst = NULL; + *fd = -1; + + if ((*fd = validate(dinfo)) < 0) + return 1; + + switch (dinfo->scheme) { + case PART_SCHEME_MBR: + *lst = config_mbr(dinfo); + return *lst == NULL; + case PART_SCHEME_GPT: + /* not supported yet */ + default: + LOGE("Uknown partition scheme."); + break; + } + + close(*fd); + *lst = NULL; + return 1; +} + +/* validate and process the disk layout configuration. + * This will cause an update to the partitions' start lba. + * + * Basically, this does the same thing as apply_disk_config in test mode, + * except that wlist_commit is not called to print out the data to be + * written. + */ +int +process_disk_config(struct disk_info *dinfo) +{ + struct write_list *lst; + int fd; + + if (validate_and_config(dinfo, &fd, &lst) != 0) + return 1; + + close(fd); + wlist_free(lst); + return 0; +} + + +int +apply_disk_config(struct disk_info *dinfo, int test) +{ + int fd; + struct write_list *wr_lst = NULL; + int rv; + + if (validate_and_config(dinfo, &fd, &wr_lst) != 0) { + LOGE("Configuration is invalid."); + goto fail; + } + + if ((rv = wlist_commit(fd, wr_lst, test)) >= 0) + rv = test ? 0 : sync_ptable(fd); + + close(fd); + wlist_free(wr_lst); + return rv; + +fail: + close(fd); + if (wr_lst) + wlist_free(wr_lst); + return 1; +} + +int +dump_disk_config(struct disk_info *dinfo) +{ + int cnt; + struct part_info *part; + + printf("Device: %s\n", dinfo->device); + printf("Scheme: "); + switch (dinfo->scheme) { + case PART_SCHEME_MBR: + printf("MBR"); + break; + case PART_SCHEME_GPT: + printf("GPT (unsupported)"); + break; + default: + printf("Unknown"); + break; + } + printf ("\n"); + + printf("Sector size: %d\n", dinfo->sect_size); + printf("Skip leading LBAs: %u\n", dinfo->skip_lba); + printf("Number of LBAs: %u\n", dinfo->num_lba); + printf("Partitions:\n"); + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + part = &dinfo->part_lst[cnt]; + printf("\tname = %s\n", part->name); + printf("\t\tflags = %s\n", + part->flags & PART_ACTIVE_FLAG ? "Active" : "None"); + printf("\t\ttype = %s\n", + part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown"); + if (part->len_kb == (uint32_t)-1) + printf("\t\tlen = rest of disk\n"); + else + printf("\t\tlen = %uKB\n", part->len_kb); + } + printf("Total number of partitions: %d\n", cnt); + printf("\n"); + + return 0; +} + +struct part_info * +find_part(struct disk_info *dinfo, const char *name) +{ + struct part_info *pinfo; + int cnt; + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + pinfo = &dinfo->part_lst[cnt]; + if (!strcmp(pinfo->name, name)) + return pinfo; + } + + return NULL; +} + +/* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */ +char * +find_part_device(struct disk_info *dinfo, const char *name) +{ + switch (dinfo->scheme) { + case PART_SCHEME_MBR: + return find_mbr_part(dinfo, name); + case PART_SCHEME_GPT: + LOGE("GPT is presently not supported"); + break; + default: + LOGE("Unknown partition table scheme"); + break; + } + + return NULL; +} + + diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c new file mode 100644 index 00000000000..22767c00e12 --- /dev/null +++ b/libdiskconfig/diskutils.c @@ -0,0 +1,117 @@ +/* libs/diskconfig/diskutils.c + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "diskutils" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +int +write_raw_image(const char *dst, const char *src, loff_t offset, int test) +{ + int dst_fd = -1; + int src_fd = -1; + uint8_t buffer[2048]; + int nr_bytes; + int tmp; + int done = 0; + uint64_t total = 0; + + LOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, offset); + if ((src_fd = open(src, O_RDONLY)) < 0) { + LOGE("Could not open %s for reading (errno=%d).", src, errno); + goto fail; + } + + if (!test) { + if ((dst_fd = open(dst, O_RDWR)) < 0) { + LOGE("Could not open '%s' for read/write (errno=%d).", dst, errno); + goto fail; + } + + if (lseek64(dst_fd, offset, SEEK_SET) != offset) { + LOGE("Could not seek to offset %lld in %s.", offset, dst); + goto fail; + } + } + + while (!done) { + if ((nr_bytes = read(src_fd, buffer, sizeof(buffer))) < 0) { + /* XXX: Should we not even bother with EINTR? */ + if (errno == EINTR) + continue; + LOGE("Error (%d) while reading from '%s'", errno, src); + goto fail; + } + + if (!nr_bytes) { + /* we're done. */ + done = 1; + break; + } + + total += nr_bytes; + + /* skip the write loop if we're testing */ + if (test) + nr_bytes = 0; + + while (nr_bytes > 0) { + if ((tmp = write(dst_fd, buffer, nr_bytes)) < 0) { + /* XXX: Should we not even bother with EINTR? */ + if (errno == EINTR) + continue; + LOGE("Error (%d) while writing to '%s'", errno, dst); + goto fail; + } + if (!tmp) + continue; + nr_bytes -= tmp; + } + } + + if (!done) { + LOGE("Exited read/write loop without setting flag! WTF?!"); + goto fail; + } + + if (dst_fd >= 0) + fsync(dst_fd); + + LOGI("Wrote %llu bytes to %s @ %lld", total, dst, offset); + + close(src_fd); + if (dst_fd >= 0) + close(dst_fd); + return 0; + +fail: + if (dst_fd >= 0) + close(dst_fd); + if (src_fd >= 0) + close(src_fd); + return 1; +} diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c new file mode 100644 index 00000000000..fff19f5abd5 --- /dev/null +++ b/libdiskconfig/dump_diskconfig.c @@ -0,0 +1,42 @@ +/* libs/diskconfig/dump_diskconfig.c + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "dump_diskconfig" +#include + +#include + +#include "diskconfig.h" + +int +main(int argc, char *argv[]) +{ + struct disk_info *dinfo; + + if (argc < 2) { + LOGE("usage: %s ", argv[0]); + return 1; + } + + if (!(dinfo = load_diskconfig(argv[1], NULL))) + return 1; + + dump_disk_config(dinfo); + + return 0; +} + diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c new file mode 100644 index 00000000000..12b7cd775b9 --- /dev/null +++ b/libdiskconfig/write_lst.c @@ -0,0 +1,92 @@ +/* libs/diskconfig/write_lst.c + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "write_lst" +#include +#include +#include +#include +#include + +#include + +#include + +struct write_list * +alloc_wl(uint32_t data_len) +{ + struct write_list *item; + + if (!(item = malloc(sizeof(struct write_list) + data_len))) { + LOGE("Unable to allocate memory."); + return NULL; + } + + item->len = data_len; + return item; +} + +void +free_wl(struct write_list *item) +{ + if (item) + free(item); +} + +struct write_list * +wlist_add(struct write_list **lst, struct write_list *item) +{ + item->next = (*lst); + *lst = item; + return item; +} + +void +wlist_free(struct write_list *lst) +{ + struct write_list *temp_wr; + while (lst) { + temp_wr = lst->next; + free_wl(lst); + lst = temp_wr; + } +} + +int +wlist_commit(int fd, struct write_list *lst, int test) +{ + for(; lst; lst = lst->next) { + if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) { + LOGE("Cannot seek to the specified position (%lld).", lst->offset); + goto fail; + } + + if (!test) { + if (write(fd, lst->data, lst->len) != (int)lst->len) { + LOGE("Failed writing %u bytes at position %lld.", lst->len, + lst->offset); + goto fail; + } + } else + LOGI("Would write %d bytes @ offset %lld.", lst->len, lst->offset); + } + + return 0; + +fail: + return -1; +} diff --git a/liblinenoise/Android.mk b/liblinenoise/Android.mk new file mode 100644 index 00000000000..b32a5f1ff94 --- /dev/null +++ b/liblinenoise/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# Static library +# ======================================================== + +include $(CLEAR_VARS) +LOCAL_MODULE:= liblinenoise +LOCAL_SRC_FILES := linenoise.c + + +include $(BUILD_STATIC_LIBRARY) diff --git a/libacc/MODULE_LICENSE_BSD_LIKE b/liblinenoise/MODULE_LICENSE_BSD_LIKE similarity index 100% rename from libacc/MODULE_LICENSE_BSD_LIKE rename to liblinenoise/MODULE_LICENSE_BSD_LIKE diff --git a/liblinenoise/NOTICE b/liblinenoise/NOTICE new file mode 100644 index 00000000000..f61419e7b72 --- /dev/null +++ b/liblinenoise/NOTICE @@ -0,0 +1,28 @@ +Copyright (c) 2010, Salvatore Sanfilippo +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Redis nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/liblinenoise/linenoise.c b/liblinenoise/linenoise.c new file mode 100644 index 00000000000..4f6775ccfcc --- /dev/null +++ b/liblinenoise/linenoise.c @@ -0,0 +1,449 @@ +/* linenoise.c -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. + * + * You can find the latest source code at: + * + * http://github.com/antirez/linenoise + * + * Does a number of crazy assumptions that happen to be true in 99.9999% of + * the 2010 UNIX computers around. + * + * Copyright (c) 2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * References: + * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html + * + * Todo list: + * - Switch to gets() if $TERM is something we can't support. + * - Filter bogus Ctrl+ combinations. + * - Win32 support + * + * Bloat: + * - Completion? + * - History search like Ctrl+r in readline? + * + * List of escape sequences used by this program, we do everything just + * with three sequences. In order to be so cheap we may have some + * flickering effect with some slow terminal, but the lesser sequences + * the more compatible. + * + * CHA (Cursor Horizontal Absolute) + * Sequence: ESC [ n G + * Effect: moves cursor to column n + * + * EL (Erase Line) + * Sequence: ESC [ n K + * Effect: if n is 0 or missing, clear from cursor to end of line + * Effect: if n is 1, clear from beginning of line to cursor + * Effect: if n is 2, clear entire line + * + * CUF (CUrsor Forward) + * Sequence: ESC [ n C + * Effect: moves cursor forward of n chars + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LINENOISE_MAX_LINE 4096 +static char *unsupported_term[] = {"dumb","cons25",NULL}; + +static struct termios orig_termios; /* in order to restore at exit */ +static int rawmode = 0; /* for atexit() function to check if restore is needed*/ +static int atexit_registered = 0; /* register atexit just 1 time */ +static int history_max_len = 100; +static int history_len = 0; +char **history = NULL; + +static void linenoiseAtExit(void); +int linenoiseHistoryAdd(const char *line); + +static int isUnsupportedTerm(void) { + char *term = getenv("TERM"); + int j; + + if (term == NULL) return 0; + for (j = 0; unsupported_term[j]; j++) + if (!strcasecmp(term,unsupported_term[j])) return 1; + return 0; +} + +static void freeHistory(void) { + if (history) { + int j; + + for (j = 0; j < history_len; j++) + free(history[j]); + free(history); + } +} + +static int enableRawMode(int fd) { + struct termios raw; + + if (!isatty(STDIN_FILENO)) goto fatal; + if (!atexit_registered) { + atexit(linenoiseAtExit); + atexit_registered = 1; + } + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode */ + if (tcsetattr(fd,TCSADRAIN,&raw) < 0) goto fatal; + rawmode = 1; + return 0; + +fatal: + errno = ENOTTY; + return -1; +} + +static void disableRawMode(int fd) { + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(fd,TCSADRAIN,&orig_termios) != -1) + rawmode = 0; +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +static void linenoiseAtExit(void) { + disableRawMode(STDIN_FILENO); + freeHistory(); +} + +static int getColumns(void) { + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 4096; + if (ws.ws_col == 0) { + return 4096; + } + return ws.ws_col; +} + +static int effectiveLen(const char* prompt) { + int col = 0; + char c; + // TODO: Handle escape sequences. + while ( (c = *prompt++) != 0 ) { + if (c == '\n') { + col = 0; + } else { + col++; + } + } + return col; +} + +static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) { + char seq[64]; + size_t plen = effectiveLen(prompt); + + while((plen+pos) >= cols) { + buf++; + len--; + pos--; + } + while (plen+len > cols) { + len--; + } + + /* Cursor to left edge */ + snprintf(seq,64,"\x1b[0G"); + if (write(fd,seq,strlen(seq)) == -1) return; + /* Write the prompt and the current buffer content */ + if (write(fd,prompt,strlen(prompt)) == -1) return; + if (write(fd,buf,len) == -1) return; + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + if (write(fd,seq,strlen(seq)) == -1) return; + /* Move cursor to original position. */ + snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); + if (write(fd,seq,strlen(seq)) == -1) return; +} + +static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) { + size_t plen = strlen(prompt); + size_t pos = 0; + size_t len = 0; + size_t cols = getColumns(); + int history_index = 0; + + buf[0] = '\0'; + buflen--; /* Make sure there is always space for the nulterm */ + + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + linenoiseHistoryAdd(""); + + if (write(fd,prompt,plen) == -1) return -1; + while(1) { + char c; + int nread; + char seq[2]; + + nread = read(fd,&c,1); + if (nread <= 0) return len; + switch(c) { + case 10: /* line feed. */ + case 13: /* enter */ + history_len--; + return len; + case 4: /* ctrl-d */ + history_len--; + return (len == 0) ? -1 : (int)len; + case 3: /* ctrl-c */ + errno = EAGAIN; + return -1; + case 127: /* backspace */ + case 8: /* ctrl-h */ + if (pos > 0 && len > 0) { + memmove(buf+pos-1,buf+pos,len-pos); + pos--; + len--; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + } + break; + case 20: /* ctrl-t */ + if (pos > 0 && pos < len) { + int aux = buf[pos-1]; + buf[pos-1] = buf[pos]; + buf[pos] = aux; + if (pos != len-1) pos++; + refreshLine(fd,prompt,buf,len,pos,cols); + } + break; + case 2: /* ctrl-b */ + goto left_arrow; + case 6: /* ctrl-f */ + goto right_arrow; + case 16: /* ctrl-p */ + seq[1] = 65; + goto up_down_arrow; + case 14: /* ctrl-n */ + seq[1] = 66; + goto up_down_arrow; + break; + case 27: /* escape sequence */ + if (read(fd,seq,2) == -1) break; + if (seq[0] == 91 && seq[1] == 68) { +left_arrow: + /* left arrow */ + if (pos > 0) { + pos--; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else if (seq[0] == 91 && seq[1] == 67) { +right_arrow: + /* right arrow */ + if (pos != len) { + pos++; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) { +up_down_arrow: + /* up and down arrow: history */ + if (history_len > 1) { + /* Update the current history entry before to + * overwrite it with tne next one. */ + free(history[history_len-1-history_index]); + history[history_len-1-history_index] = strdup(buf); + /* Show the new entry */ + history_index += (seq[1] == 65) ? 1 : -1; + if (history_index < 0) { + history_index = 0; + break; + } else if (history_index >= history_len) { + history_index = history_len-1; + break; + } + strncpy(buf,history[history_len-1-history_index],buflen); + buf[buflen] = '\0'; + len = pos = strlen(buf); + refreshLine(fd,prompt,buf,len,pos,cols); + } + } + break; + default: + if (len < buflen) { + if (len == pos) { + buf[pos] = c; + pos++; + len++; + buf[len] = '\0'; + if (plen+len < cols) { + /* Avoid a full update of the line in the + * trivial case. */ + if (write(fd,&c,1) == -1) return -1; + } else { + refreshLine(fd,prompt,buf,len,pos,cols); + } + } else { + memmove(buf+pos+1,buf+pos,len-pos); + buf[pos] = c; + len++; + pos++; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + } + } + break; + case 21: /* Ctrl+u, delete the whole line. */ + buf[0] = '\0'; + pos = len = 0; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 11: /* Ctrl+k, delete from current to end of line. */ + buf[pos] = '\0'; + len = pos; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 1: /* Ctrl+a, go to the start of the line */ + pos = 0; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 5: /* ctrl+e, go to the end of the line */ + pos = len; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + } + } + return len; +} + +static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { + int fd = STDIN_FILENO; + int count; + + if (buflen == 0) { + errno = EINVAL; + return -1; + } + if (!isatty(STDIN_FILENO)) { + if (fgets(buf, buflen, stdin) == NULL) return -1; + count = strlen(buf); + if (count && buf[count-1] == '\n') { + count--; + buf[count] = '\0'; + } + } else { + if (enableRawMode(fd) == -1) return -1; + count = linenoisePrompt(fd, buf, buflen, prompt); + disableRawMode(fd); + } + return count; +} + +char *linenoise(const char *prompt) { + char buf[LINENOISE_MAX_LINE]; + int count; + + if (isUnsupportedTerm()) { + size_t len; + + printf("%s",prompt); + fflush(stdout); + if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; + len = strlen(buf); + while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + len--; + buf[len] = '\0'; + } + return strdup(buf); + } else { + count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); + if (count == -1) return NULL; + return strdup(buf); + } +} + +/* Using a circular buffer is smarter, but a bit more complex to handle. */ +int linenoiseHistoryAdd(const char *line) { + char *linecopy; + + if (history_max_len == 0) return 0; + if (history == 0) { + history = malloc(sizeof(char*)*history_max_len); + if (history == NULL) return 0; + memset(history,0,(sizeof(char*)*history_max_len)); + } + linecopy = strdup(line); + if (!linecopy) return 0; + if (history_len == history_max_len) { + memmove(history,history+1,sizeof(char*)*(history_max_len-1)); + history_len--; + } + history[history_len] = linecopy; + history_len++; + return 1; +} + +int linenoiseHistorySetMaxLen(int len) { + char **new; + + if (len < 1) return 0; + if (history) { + int tocopy = history_len; + + new = malloc(sizeof(char*)*len); + if (new == NULL) return 0; + if (len < tocopy) tocopy = len; + memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy); + free(history); + history = new; + } + history_max_len = len; + if (history_len > history_max_len) + history_len = history_max_len; + return 1; +} diff --git a/liblinenoise/linenoise.h b/liblinenoise/linenoise.h new file mode 100644 index 00000000000..57bf9d18c1f --- /dev/null +++ b/liblinenoise/linenoise.h @@ -0,0 +1,41 @@ +/* linenoise.h -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. + * + * See linenoise.c for more information. + * + * Copyright (c) 2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LINENOISE_H +#define __LINENOISE_H + +char *linenoise(const char *prompt); +int linenoiseHistoryAdd(const char *line); +int linenoiseHistorySetMaxLen(int len); + +#endif /* __LINENOISE_H */ diff --git a/liblog/Android.mk b/liblog/Android.mk index 0eec87f043b..bd4fed4a063 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -48,25 +48,14 @@ LOCAL_LDLIBS := -lpthread LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 include $(BUILD_HOST_STATIC_LIBRARY) -ifeq ($(TARGET_SIMULATOR),true) - # Shared library for simulator - # ======================================================== - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_SRC_FILES := $(liblog_host_sources) - LOCAL_LDLIBS := -lpthread - LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 - include $(BUILD_SHARED_LIBRARY) -else # !sim - # Shared and static library for target - # ======================================================== - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_SRC_FILES := $(liblog_sources) - include $(BUILD_STATIC_LIBRARY) +# Shared and static library for target +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_SRC_FILES := $(liblog_sources) +include $(BUILD_STATIC_LIBRARY) - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_WHOLE_STATIC_LIBRARIES := liblog - include $(BUILD_SHARED_LIBRARY) -endif # !sim +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := liblog +include $(BUILD_SHARED_LIBRARY) diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index ed9d699ac53..f8b72541276 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -454,7 +454,7 @@ static void showLog(LogState *state, numVecs = numLines*3; // 3 iovecs per line. if (numVecs > INLINE_VECS) { - vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines); + vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs); if (vec == NULL) { msg = "LOG: write failed, no memory"; numVecs = 3; diff --git a/liblog/logd_write.c b/liblog/logd_write.c index 241dbf0c72c..8a76500a781 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -27,6 +27,7 @@ #include #include +#include #define LOG_BUF_SIZE 1024 @@ -41,21 +42,13 @@ #define log_close(filedes) close(filedes) #endif -typedef enum { - LOG_ID_MAIN = 0, - LOG_ID_RADIO, - LOG_ID_EVENTS, - LOG_ID_MAX -} log_id_t; - static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); -static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = - __write_to_log_init; +static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; #ifdef HAVE_PTHREADS static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif -static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1 }; +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; /* * This is used by the C++ code to decide if it should write logs through @@ -63,7 +56,7 @@ static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1 }; * the simulator rather than a desktop tool and want to use the device. */ static enum { - kLogUninitialized, kLogNotAvailable, kLogAvailable + kLogUninitialized, kLogNotAvailable, kLogAvailable } g_log_status = kLogUninitialized; int __android_log_dev_available(void) { @@ -110,6 +103,7 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); write_to_log = __write_to_log_kernel; @@ -123,6 +117,10 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) log_fds[LOG_ID_EVENTS] = -1; write_to_log = __write_to_log_null; } + + if (log_fds[LOG_ID_SYSTEM] < 0) { + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + } } #ifdef HAVE_PTHREADS @@ -148,7 +146,18 @@ int __android_log_write(int prio, const char *tag, const char *msg) !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) + !strcmp(tag, "SMS") || + !strcmp(tag, "KINETO") || + !strncmp(tag, "KIPC", 4) || + !strncmp(tag, "Kineto", 6) || + !strncmp(tag, "QCRIL", 5) || + !strncmp(tag, "QC-RIL", 6) || + !strncmp(tag, "QC-QMI", 6) || + !strncmp(tag, "QC-ONCRPC", 9) || + !strncmp(tag, "QC-DSI", 6) || + !strcmp(tag, "QC-NETMGR-LIB") || + !strcmp(tag, "QC-QDP") + ) log_id = LOG_ID_RADIO; vec[0].iov_base = (unsigned char *) &prio; @@ -161,9 +170,46 @@ int __android_log_write(int prio, const char *tag, const char *msg) return write_to_log(log_id, vec, 3); } +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if (!strcmp(tag, "HTC_RIL") || + !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK") || + !strcmp(tag, "CDMA") || + !strcmp(tag, "PHONE") || + !strcmp(tag, "SMS") || + !strcmp(tag, "KINETO") || + !strncmp(tag, "KIPC", 4) || + !strncmp(tag, "Kineto", 6) || + !strncmp(tag, "QCRIL", 5) || + !strncmp(tag, "QC-RIL", 6) || + !strncmp(tag, "QC-QMI", 6) || + !strncmp(tag, "QC-ONCRPC", 9) || + !strncmp(tag, "QC-DSI", 6) + ) + bufID = LOG_ID_RADIO; + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(bufID, vec, 3); +} + int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) { - char buf[LOG_BUF_SIZE]; + char buf[LOG_BUF_SIZE]; vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); @@ -173,7 +219,7 @@ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) int __android_log_print(int prio, const char *tag, const char *fmt, ...) { va_list ap; - char buf[LOG_BUF_SIZE]; + char buf[LOG_BUF_SIZE]; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); @@ -182,16 +228,39 @@ int __android_log_print(int prio, const char *tag, const char *fmt, ...) return __android_log_write(prio, tag, buf); } -void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...) { va_list ap; - char buf[LOG_BUF_SIZE]; + char buf[LOG_BUF_SIZE]; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); va_end(ap); + return __android_log_buf_write(bufID, prio, tag, buf); +} + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +{ + char buf[LOG_BUF_SIZE]; + + if (fmt) { + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + } else { + /* Msg not provided, log condition. N.B. Do not use cond directly as + * format string as it could contain spurious '%' syntax (e.g. + * "%d" in "blocks%devs == 0"). + */ + if (cond) + snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond); + else + strcpy(buf, "Unspecified assertion failed"); + } + __android_log_write(ANDROID_LOG_FATAL, tag, buf); __builtin_trap(); /* trap so we have a chance to debug the situation */ diff --git a/liblog/logprint.c b/liblog/logprint.c index 080f9e364e4..f630dc9f977 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -17,6 +17,13 @@ #define _GNU_SOURCE /* for asprintf */ +#define COLOR_BLUE 75 +#define COLOR_DEFAULT 231 +#define COLOR_GREEN 40 +#define COLOR_ORANGE 166 +#define COLOR_RED 196 +#define COLOR_YELLOW 226 + #include #include #include @@ -39,6 +46,7 @@ struct AndroidLogFormat_t { android_LogPriority global_pri; FilterInfo *filters; AndroidLogPrintFormat format; + AndroidLogColoredOutput colored_output; }; static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) @@ -118,6 +126,23 @@ static char filterPriToChar (android_LogPriority pri) } } +static int colorFromPri (android_LogPriority pri) +{ + switch (pri) { + case ANDROID_LOG_VERBOSE: return COLOR_DEFAULT; + case ANDROID_LOG_DEBUG: return COLOR_BLUE; + case ANDROID_LOG_INFO: return COLOR_GREEN; + case ANDROID_LOG_WARN: return COLOR_ORANGE; + case ANDROID_LOG_ERROR: return COLOR_RED; + case ANDROID_LOG_FATAL: return COLOR_RED; + case ANDROID_LOG_SILENT: return COLOR_DEFAULT; + + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: return COLOR_DEFAULT; + } +} + static android_LogPriority filterPriForTag( AndroidLogFormat *p_format, const char *tag) { @@ -174,6 +199,7 @@ AndroidLogFormat *android_log_format_new() p_ret->global_pri = ANDROID_LOG_VERBOSE; p_ret->format = FORMAT_BRIEF; + p_ret->colored_output = OUTPUT_COLOR_OFF; return p_ret; } @@ -202,6 +228,11 @@ void android_log_setPrintFormat(AndroidLogFormat *p_format, p_format->format=format; } +void android_log_setColoredOutput(AndroidLogFormat *p_format) +{ + p_format->colored_output = OUTPUT_COLOR_ON; +} + /** * Returns FORMAT_OFF on invalid string */ @@ -350,17 +381,58 @@ static inline char * strip_end(char *str) int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry) { - size_t tag_len; - entry->tv_sec = buf->sec; entry->tv_nsec = buf->nsec; - entry->priority = buf->msg[0]; entry->pid = buf->pid; entry->tid = buf->tid; + + /* + * format: \0\0 + * + * tag str + * starts at buf->msg+1 + * msg + * starts at buf->msg+1+len(tag)+1 + * + * The message may have been truncated by the kernel log driver. + * When that happens, we must null-terminate the message ourselves. + */ + if (buf->len < 3) { + // An well-formed entry must consist of at least a priority + // and two null characters + fprintf(stderr, "+++ LOG: entry too small\n"); + return -1; + } + + int msgStart = -1; + int msgEnd = -1; + + int i; + for (i = 1; i < buf->len; i++) { + if (buf->msg[i] == '\0') { + if (msgStart == -1) { + msgStart = i + 1; + } else { + msgEnd = i; + break; + } + } + } + + if (msgStart == -1) { + fprintf(stderr, "+++ LOG: malformed log message\n"); + return -1; + } + if (msgEnd == -1) { + // incoming message not null-terminated; force it + msgEnd = buf->len - 1; + buf->msg[msgEnd] = '\0'; + } + + entry->priority = buf->msg[0]; entry->tag = buf->msg + 1; - tag_len = strlen(entry->tag); - entry->messageLen = buf->len - tag_len - 3; - entry->message = entry->tag + tag_len + 1; + entry->message = buf->msg + msgStart; + entry->messageLen = msgEnd - msgStart; return 0; } @@ -698,20 +770,32 @@ char *android_log_formatLogLine ( */ size_t prefixLen, suffixLen; + size_t prefixColorLen = 0; + char * prefixBufTmp = prefixBuf; + size_t prefixBufTmpRemainLen = sizeof(prefixBuf); + + if (p_format->colored_output == OUTPUT_COLOR_ON) { + prefixColorLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c[%d;%d;%dm", 0x1B, 38, 5, colorFromPri(entry->priority)); + if(prefixColorLen >= prefixBufTmpRemainLen) + prefixColorLen = prefixBufTmpRemainLen - 1; + prefixBufTmp += prefixColorLen; + prefixBufTmpRemainLen -= prefixColorLen; + } + switch (p_format->format) { case FORMAT_TAG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c/%-8s: ", priChar, entry->tag); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_PROCESS: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c(%5d) ", priChar, entry->pid); suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), " (%s)\n", entry->tag); break; case FORMAT_THREAD: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c(%5d:%p) ", priChar, entry->pid, (void*)entry->tid); strcpy(suffixBuf, "\n"); suffixLen = 1; @@ -723,21 +807,21 @@ char *android_log_formatLogLine ( suffixLen = 1; break; case FORMAT_TIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, priChar, entry->tag, entry->pid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_THREADTIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, (int)entry->pid, (int)entry->tid, priChar, entry->tag); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_LONG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "[ %s.%03ld %5d:%p %c/%-8s ]\n", timeBuf, entry->tv_nsec / 1000000, entry->pid, (void*)entry->tid, priChar, entry->tag); @@ -747,12 +831,33 @@ char *android_log_formatLogLine ( break; case FORMAT_BRIEF: default: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + prefixLen = snprintf(prefixBufTmp, prefixBufTmpRemainLen, "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; } + /* snprintf has a weird return value. It returns what would have been + * written given a large enough buffer. In the case that the prefix is + * longer then our buffer(128), it messes up the calculations below + * possibly causing heap corruption. To avoid this we double check and + * set the length at the maximum (size minus null byte) + */ + if(prefixLen >= prefixBufTmpRemainLen) + prefixLen = prefixBufTmpRemainLen - 1; + if(suffixLen >= sizeof(suffixBuf)) + suffixLen = sizeof(suffixBuf) - 1; + + size_t suffixColorLen = 0; + char * suffixBufTmp = suffixBuf + suffixLen; + size_t suffixBufTmpRemainLen = sizeof(suffixBuf) - suffixLen; + + if (p_format->colored_output == OUTPUT_COLOR_ON) { + suffixColorLen = snprintf(suffixBufTmp, suffixBufTmpRemainLen, "%c[%dm", 0x1B, 0); + if(suffixColorLen >= suffixBufTmpRemainLen) + suffixColorLen = suffixBufTmpRemainLen - 1; + } + /* the following code is tragically unreadable */ @@ -780,7 +885,7 @@ char *android_log_formatLogLine ( // this is an upper bound--newlines in message may be counted // extraneously - bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + bufferSize = (numLines * (prefixColorLen + prefixLen + suffixLen + suffixColorLen)) + entry->messageLen + 1; if (defaultBufferSize >= bufferSize) { ret = defaultBuffer; @@ -799,16 +904,15 @@ char *android_log_formatLogLine ( if (prefixSuffixIsHeaderFooter) { strcat(p, prefixBuf); - p += prefixLen; + p += prefixColorLen + prefixLen; strncat(p, entry->message, entry->messageLen); p += entry->messageLen; strcat(p, suffixBuf); - p += suffixLen; + p += suffixLen + suffixColorLen; } else { while(pm < (entry->message + entry->messageLen)) { const char *lineStart; size_t lineLen; - lineStart = pm; // Find the next end-of-line in message @@ -817,11 +921,11 @@ char *android_log_formatLogLine ( lineLen = pm - lineStart; strcat(p, prefixBuf); - p += prefixLen; + p += prefixColorLen + prefixLen; strncat(p, lineStart, lineLen); p += lineLen; strcat(p, suffixBuf); - p += suffixLen; + p += suffixLen + suffixColorLen; if (*pm == '\n') pm++; } @@ -840,7 +944,7 @@ char *android_log_formatLogLine ( * Returns count bytes written */ -int android_log_filterAndPrintLogLine( +int android_log_printLogLine( AndroidLogFormat *p_format, int fd, const AndroidLogEntry *entry) @@ -850,11 +954,6 @@ int android_log_filterAndPrintLogLine( char *outBuffer = NULL; size_t totalLen; - if (0 == android_log_shouldPrintLine(p_format, entry->tag, - entry->priority)) { - return 0; - } - outBuffer = android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen); diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index c9e7e4d19ca..d2935e09ce2 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -77,7 +77,7 @@ static String print(RSAPublicKey key) throws Exception { // Write out modulus as little endian array of integers. result.append(",{"); for (int i = 0; i < nwords; ++i) { - int n = N.mod(B).intValue(); + long n = N.mod(B).longValue(); result.append(n); if (i != nwords - 1) { @@ -91,7 +91,7 @@ static String print(RSAPublicKey key) throws Exception { // Write R^2 as little endian array of integers. result.append(",{"); for (int i = 0; i < nwords; ++i) { - int rr = RR.mod(B).intValue(); + long rr = RR.mod(B).longValue(); result.append(rr); if (i != nwords - 1) { diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk index 46102d55c16..5f5849fcc8d 100644 --- a/libnetutils/Android.mk +++ b/libnetutils/Android.mk @@ -6,17 +6,10 @@ LOCAL_SRC_FILES:= \ dhcpmsg.c \ dhcp_utils.c \ ifc_utils.c \ - packet.c + packet.c LOCAL_SHARED_LIBRARIES := \ - libcutils - -# need "-lrt" on Linux simulator to pick up clock_gettime -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt -lpthread - endif -endif + libcutils LOCAL_MODULE:= libnetutils diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c old mode 100644 new mode 100755 index bad2e2ffda2..1e08eb63290 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -18,18 +18,23 @@ #include #include +#include #include #include #include #include -static const char DAEMON_NAME[] = "dhcpcd"; -static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; +static const char DAEMON_NAME[] = "dhcpcd"; +static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; +static const char HOSTNAME_PROP_NAME[] = "net.hostname"; static const char DHCP_PROP_NAME_PREFIX[] = "dhcp"; -static const int NAP_TIME = 1; /* wait for 1 second at a time */ +static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ +static const char DAEMON_NAME_RENEW[] = "iprenew"; static char errmsg[100]; +/* interface suffix on dhcpcd */ +#define MAX_DAEMON_SUFFIX 25 /* * Wait for a system property to be assigned a specified value. @@ -40,14 +45,14 @@ static char errmsg[100]; static int wait_for_property(const char *name, const char *desired_value, int maxwait) { char value[PROPERTY_VALUE_MAX] = {'\0'}; - int maxnaps = maxwait / NAP_TIME; + int maxnaps = (maxwait * 1000) / NAP_TIME; if (maxnaps < 1) { maxnaps = 1; } while (maxnaps-- > 0) { - usleep(1000000); + usleep(NAP_TIME * 1000); if (property_get(name, value, NULL)) { if (desired_value == NULL || strcmp(value, desired_value) == 0) { @@ -58,60 +63,83 @@ static int wait_for_property(const char *name, const char *desired_value, int ma return -1; /* failure */ } -static void fill_ip_info(const char *interface, - in_addr_t *ipaddr, - in_addr_t *gateway, - in_addr_t *mask, - in_addr_t *dns1, - in_addr_t *dns2, - in_addr_t *server, +static int fill_ip_info(const char *interface, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns1, + char *dns2, + char *server, uint32_t *lease) { char prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX]; - struct in_addr addr; - in_addr_t iaddr; snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, interface); - if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { - *ipaddr = addr.s_addr; - } else { - *ipaddr = 0; - } + property_get(prop_name, ipaddr, NULL); + snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, interface); - if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { - *gateway = addr.s_addr; - } else { - *gateway = 0; + property_get(prop_name, gateway, NULL); + + snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, interface); + property_get(prop_name, server, NULL); + + //TODO: Handle IPv6 when we change system property usage + if (strcmp(gateway, "0.0.0.0") == 0) { + //DHCP server is our best bet as gateway + strncpy(gateway, server, PROPERTY_VALUE_MAX); } + snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, interface); - if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { - *mask = addr.s_addr; - } else { - *mask = 0; + if (property_get(prop_name, prop_value, NULL)) { + int p; + // this conversion is v4 only, but this dhcp client is v4 only anyway + in_addr_t mask = ntohl(inet_addr(prop_value)); + // Check netmask is a valid IP address. ntohl gives NONE response (all 1's) for + // non 255.255.255.255 inputs. if we get that value check if it is legit.. + if (mask == INADDR_NONE && strcmp(prop_value, "255.255.255.255") != 0) { + snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value); + return -1; + } + for (p = 0; p < 32; p++) { + if (mask == 0) break; + // check for non-contiguous netmask, e.g., 255.254.255.0 + if ((mask & 0x80000000) == 0) { + snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value); + return -1; + } + mask = mask << 1; + } + *prefixLength = p; } snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, interface); - if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { - *dns1 = addr.s_addr; - } else { - *dns1 = 0; - } + property_get(prop_name, dns1, NULL); + snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, interface); - if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { - *dns2 = addr.s_addr; - } else { - *dns2 = 0; - } - snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, interface); - if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) { - *server = addr.s_addr; - } else { - *server = 0; - } + property_get(prop_name, dns2, NULL); + snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, interface); if (property_get(prop_name, prop_value, NULL)) { *lease = atol(prop_value); } + return 0; +} + +static const char *ipaddr_to_string(in_addr_t addr) +{ + struct in_addr in_addr; + + in_addr.s_addr = addr; + return inet_ntoa(in_addr); +} + +void get_daemon_suffix(const char *interface, char *daemon_suffix) { + /* Use p2p suffix for any p2p interface. */ + if (strncmp(interface, "p2p",3) == 0) { + sprintf(daemon_suffix, "p2p"); + } else { + snprintf(daemon_suffix, MAX_DAEMON_SUFFIX, "%s", interface); + } } /* @@ -119,28 +147,44 @@ static void fill_ip_info(const char *interface, * configuring the interface. */ int dhcp_do_request(const char *interface, - in_addr_t *ipaddr, - in_addr_t *gateway, - in_addr_t *mask, - in_addr_t *dns1, - in_addr_t *dns2, - in_addr_t *server, + char *ipaddr, + char *gateway, + uint32_t *prefixLength, + char *dns1, + char *dns2, + char *server, uint32_t *lease) { char result_prop_name[PROPERTY_KEY_MAX]; + char daemon_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; + char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.start"; const char *desired_status = "running"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, interface); + + snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", + DAEMON_PROP_NAME, + daemon_suffix); + /* Erase any previous setting of the dhcp result property */ property_set(result_prop_name, ""); /* Start the daemon and wait until it's ready */ - property_set(ctrl_prop, DAEMON_NAME); - if (wait_for_property(DAEMON_PROP_NAME, desired_status, 10) < 0) { + if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0')) + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, daemon_suffix, + prop_value, interface); + else + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, daemon_suffix, interface); + memset(prop_value, '\0', PROPERTY_VALUE_MAX); + property_set(ctrl_prop, daemon_cmd); + if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) { snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start"); return -1; } @@ -157,7 +201,18 @@ int dhcp_do_request(const char *interface, return -1; } if (strcmp(prop_value, "ok") == 0) { - fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease); + char dns_prop_name[PROPERTY_KEY_MAX]; + if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns1, dns2, server, lease) + == -1) { + return -1; + } + + /* copy dns data to system properties - TODO - remove this after we have async + * notification of renewal's */ + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", interface); + property_set(dns_prop_name, *dns1 ? ipaddr_to_string(*dns1) : ""); + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", interface); + property_set(dns_prop_name, *dns2 ? ipaddr_to_string(*dns2) : ""); return 0; } else { snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value); @@ -171,15 +226,28 @@ int dhcp_do_request(const char *interface, int dhcp_stop(const char *interface) { char result_prop_name[PROPERTY_KEY_MAX]; + char daemon_prop_name[PROPERTY_KEY_MAX]; + char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.stop"; const char *desired_status = "stopped"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, interface); + + snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", + DAEMON_PROP_NAME, + daemon_suffix); + + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, daemon_suffix); + /* Stop the daemon and wait until it's reported to be stopped */ - property_set(ctrl_prop, DAEMON_NAME); - if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) { + property_set(ctrl_prop, daemon_cmd); + if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) { return -1; } property_set(result_prop_name, "failed"); @@ -191,12 +259,24 @@ int dhcp_stop(const char *interface) */ int dhcp_release_lease(const char *interface) { + char daemon_prop_name[PROPERTY_KEY_MAX]; + char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.stop"; const char *desired_status = "stopped"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); + + snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", + DAEMON_PROP_NAME, + daemon_suffix); + + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, daemon_suffix); + /* Stop the daemon and wait until it's reported to be stopped */ - property_set(ctrl_prop, DAEMON_NAME); - if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) { + property_set(ctrl_prop, daemon_cmd); + if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) { return -1; } return 0; @@ -205,3 +285,58 @@ int dhcp_release_lease(const char *interface) char *dhcp_get_errmsg() { return errmsg; } + +/** + * Run WiMAX dhcp renew service. + * "wimax_renew" service shoud be included in init.rc. + */ +int dhcp_do_request_renew(const char *interface, + in_addr_t *ipaddr, + in_addr_t *gateway, + uint32_t *prefixLength, + in_addr_t *dns1, + in_addr_t *dns2, + in_addr_t *server, + uint32_t *lease) +{ + char result_prop_name[PROPERTY_KEY_MAX]; + char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; + char daemon_cmd[PROPERTY_VALUE_MAX * 2]; + const char *ctrl_prop = "ctl.start"; + + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); + + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", + DHCP_PROP_NAME_PREFIX, + interface); + + /* Erase any previous setting of the dhcp result property */ + property_set(result_prop_name, ""); + + /* Start the renew daemon and wait until it's ready */ + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW, + daemon_suffix, interface); + memset(prop_value, '\0', PROPERTY_VALUE_MAX); + property_set(ctrl_prop, daemon_cmd); + + /* Wait for the daemon to return a result */ + if (wait_for_property(result_prop_name, NULL, 30) < 0) { + snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP Renew to finish"); + return -1; + } + + if (!property_get(result_prop_name, prop_value, NULL)) { + /* shouldn't ever happen, given the success of wait_for_property() */ + snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set"); + return -1; + } + if (strcmp(prop_value, "ok") == 0) { + fill_ip_info(interface, ipaddr, gateway, prefixLength, dns1, dns2, server, lease); + return 0; + } else { + snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); + return -1; + } +} diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c index 45e392a66a9..4f2d1c15eac 100644 --- a/libnetutils/dhcpclient.c +++ b/libnetutils/dhcpclient.c @@ -36,8 +36,8 @@ #include +#include #include "dhcpmsg.h" -#include "ifc_utils.h" #include "packet.h" #define VERBOSE 2 @@ -70,7 +70,7 @@ void printerr(char *fmt, ...) vsnprintf(errmsg, sizeof(errmsg), fmt, ap); va_end(ap); - LOGD(errmsg); + LOGD("%s", errmsg); } const char *dhcp_lasterror() @@ -85,18 +85,16 @@ int fatal(const char *reason) // exit(1); } -const char *ipaddr(uint32_t addr) +const char *ipaddr(in_addr_t addr) { - static char buf[32]; - - sprintf(buf,"%d.%d.%d.%d", - addr & 255, - ((addr >> 8) & 255), - ((addr >> 16) & 255), - (addr >> 24)); - return buf; + struct in_addr in_addr; + + in_addr.s_addr = addr; + return inet_ntoa(in_addr); } +extern int ipv4NetmaskToPrefixLength(in_addr_t mask); + typedef struct dhcp_info dhcp_info; struct dhcp_info { @@ -104,7 +102,7 @@ struct dhcp_info { uint32_t ipaddr; uint32_t gateway; - uint32_t netmask; + uint32_t prefixLength; uint32_t dns1; uint32_t dns2; @@ -115,44 +113,24 @@ struct dhcp_info { dhcp_info last_good_info; -void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *mask, +void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength, uint32_t *dns1, uint32_t *dns2, uint32_t *server, uint32_t *lease) { *ipaddr = last_good_info.ipaddr; *gateway = last_good_info.gateway; - *mask = last_good_info.netmask; + *prefixLength = last_good_info.prefixLength; *dns1 = last_good_info.dns1; *dns2 = last_good_info.dns2; *server = last_good_info.serveraddr; *lease = last_good_info.lease; } -static int ifc_configure(const char *ifname, dhcp_info *info) +static int dhcp_configure(const char *ifname, dhcp_info *info) { - char dns_prop_name[PROPERTY_KEY_MAX]; - - if (ifc_set_addr(ifname, info->ipaddr)) { - printerr("failed to set ipaddr %s: %s\n", ipaddr(info->ipaddr), strerror(errno)); - return -1; - } - if (ifc_set_mask(ifname, info->netmask)) { - printerr("failed to set netmask %s: %s\n", ipaddr(info->netmask), strerror(errno)); - return -1; - } - if (ifc_create_default_route(ifname, info->gateway)) { - printerr("failed to set default route %s: %s\n", ipaddr(info->gateway), strerror(errno)); - return -1; - } - - snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname); - property_set(dns_prop_name, info->dns1 ? ipaddr(info->dns1) : ""); - snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname); - property_set(dns_prop_name, info->dns2 ? ipaddr(info->dns2) : ""); - last_good_info = *info; - - return 0; + return ifc_configure(ifname, info->ipaddr, info->prefixLength, info->gateway, + info->dns1, info->dns2); } static const char *dhcp_type_to_name(uint32_t type) @@ -177,8 +155,7 @@ void dump_dhcp_info(dhcp_info *info) dhcp_type_to_name(info->type), info->type); strcpy(addr, ipaddr(info->ipaddr)); strcpy(gway, ipaddr(info->gateway)); - strcpy(mask, ipaddr(info->netmask)); - LOGD("ip %s gw %s mask %s", addr, gway, mask); + LOGD("ip %s gw %s prefixLength %d", addr, gway, info->prefixLength); if (info->dns1) LOGD("dns1: %s", ipaddr(info->dns1)); if (info->dns2) LOGD("dns2: %s", ipaddr(info->dns2)); LOGD("server %s, lease %d seconds", @@ -220,7 +197,7 @@ int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) } switch(opt) { case OPT_SUBNET_MASK: - if (optlen >= 4) memcpy(&info->netmask, x, 4); + if (optlen >= 4) info->prefixLength = ipv4NetmaskToPrefixLength(*((uint32_t*)x)); break; case OPT_GATEWAY: if (optlen >= 4) memcpy(&info->gateway, x, 4); @@ -449,7 +426,7 @@ int dhcp_init_ifc(const char *ifname) printerr("timed out\n"); if ( info.type == DHCPOFFER ) { printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname); - return ifc_configure(ifname, &info); + return dhcp_configure(ifname, &info); } errno = ETIME; close(s); @@ -530,7 +507,7 @@ int dhcp_init_ifc(const char *ifname) if (info.type == DHCPACK) { printerr("configuring %s\n", ifname); close(s); - return ifc_configure(ifname, &info); + return dhcp_configure(ifname, &info); } else if (info.type == DHCPNAK) { printerr("configuration request denied\n"); close(s); diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index bde336f6a58..819c218db89 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -25,11 +25,19 @@ #include #include #include +#include +#include #include -#include +#include +#include +#include #include -#include +#include +#include +#include + +#include "netutils/ifc.h" #ifdef ANDROID #define LOG_TAG "NetUtils" @@ -43,9 +51,41 @@ #endif static int ifc_ctl_sock = -1; +static int ifc_ctl_sock6 = -1; void printerr(char *fmt, ...); -static const char *ipaddr_to_string(uint32_t addr) +#define DBG 0 +#define INET_ADDRLEN 4 +#define INET6_ADDRLEN 16 + +in_addr_t prefixLengthToIpv4Netmask(int prefix_length) +{ + in_addr_t mask = 0; + + // C99 (6.5.7): shifts of 32 bits have undefined results + if (prefix_length <= 0 || prefix_length > 32) { + return 0; + } + + mask = ~mask << (32 - prefix_length); + mask = htonl(mask); + + return mask; +} + +int ipv4NetmaskToPrefixLength(in_addr_t mask) +{ + mask = ntohl(mask); + int prefixLength = 0; + uint32_t m = (uint32_t)mask; + while (m & 0x80000000) { + prefixLength++; + m = m << 1; + } + return prefixLength; +} + +static const char *ipaddr_to_string(in_addr_t addr) { struct in_addr in_addr; @@ -53,25 +93,71 @@ static const char *ipaddr_to_string(uint32_t addr) return inet_ntoa(in_addr); } +int string_to_ip(const char *string, struct sockaddr_storage *ss) { + struct addrinfo hints, *ai; + int ret; + + if (ss == NULL) { + return -EFAULT; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(string, NULL, &hints, &ai); + if (ret == 0) { + memcpy(ss, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + } + + return ret; +} + int ifc_init(void) { + int ret; if (ifc_ctl_sock == -1) { - ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); + ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); if (ifc_ctl_sock < 0) { printerr("socket() failed: %s\n", strerror(errno)); } } - return ifc_ctl_sock < 0 ? -1 : 0; + + ret = ifc_ctl_sock < 0 ? -1 : 0; + if (DBG) printerr("ifc_init_returning %d", ret); + return ret; +} + +int ifc_init6(void) +{ + if (ifc_ctl_sock6 == -1) { + ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM, 0); + if (ifc_ctl_sock6 < 0) { + printerr("socket() failed: %s\n", strerror(errno)); + } + } + return ifc_ctl_sock6 < 0 ? -1 : 0; } void ifc_close(void) { + if (DBG) printerr("ifc_close"); if (ifc_ctl_sock != -1) { (void)close(ifc_ctl_sock); ifc_ctl_sock = -1; } } +void ifc_close6(void) +{ + if (ifc_ctl_sock6 != -1) { + (void)close(ifc_ctl_sock6); + ifc_ctl_sock6 = -1; + } +} + static void ifc_init_ifr(const char *name, struct ifreq *ifr) { memset(ifr, 0, sizeof(struct ifreq)); @@ -88,8 +174,8 @@ int ifc_get_hwaddr(const char *name, void *ptr) r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr); if(r < 0) return -1; - memcpy(ptr, &ifr.ifr_hwaddr.sa_data, 6); - return 0; + memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); + return 0; } int ifc_get_ifindex(const char *name, int *if_indexp) @@ -102,7 +188,7 @@ int ifc_get_ifindex(const char *name, int *if_indexp) if(r < 0) return -1; *if_indexp = ifr.ifr_ifindex; - return 0; + return 0; } static int ifc_set_flags(const char *name, unsigned set, unsigned clr) @@ -117,12 +203,16 @@ static int ifc_set_flags(const char *name, unsigned set, unsigned clr) int ifc_up(const char *name) { - return ifc_set_flags(name, IFF_UP, 0); + int ret = ifc_set_flags(name, IFF_UP, 0); + if (DBG) printerr("ifc_up(%s) = %d", name, ret); + return ret; } int ifc_down(const char *name) { - return ifc_set_flags(name, 0, IFF_UP); + int ret = ifc_set_flags(name, 0, IFF_UP); + if (DBG) printerr("ifc_down(%s) = %d", name, ret); + return ret; } static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr) @@ -136,24 +226,256 @@ static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr) int ifc_set_addr(const char *name, in_addr_t addr) { struct ifreq ifr; + int ret; ifc_init_ifr(name, &ifr); init_sockaddr_in(&ifr.ifr_addr, addr); - - return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); + + ret = ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); + if (DBG) printerr("ifc_set_addr(%s, xx) = %d", name, ret); + return ret; +} + +/* + * Adds or deletes an IP address on an interface. + * + * Action is one of: + * - RTM_NEWADDR (to add a new address) + * - RTM_DELADDR (to delete an existing address) + * + * Returns zero on success and negative errno on failure. + */ +int ifc_act_on_address(int action, const char *name, const char *address, + int prefixlen) { + int ifindex, s, len, ret; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa; + void *addr; + size_t addrlen; + struct { + struct nlmsghdr n; + struct ifaddrmsg r; + // Allow for IPv6 address, headers, and padding. + char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct rtattr)) + + NLMSG_ALIGN(INET6_ADDRLEN)]; + } req; + struct rtattr *rta; + struct nlmsgerr *err; + union { + struct nlmsghdr nh; + char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct nlmsgerr)) + + NLMSG_ALIGN(sizeof(struct nlmsghdr))]; + } buf; + + // Get interface ID. + ifindex = if_nametoindex(name); + if (ifindex == 0) { + return -errno; + } + + // Convert string representation to sockaddr_storage. + ret = string_to_ip(address, &sa.ss); + if (ret) { + return ret; + } + + // Determine address type and length. + if (sa.ss.ss_family == AF_INET) { + addr = &sa.sin.sin_addr; + addrlen = INET_ADDRLEN; + } else if (sa.ss.ss_family == AF_INET6) { + addr = &sa.sin6.sin6_addr; + addrlen = INET6_ADDRLEN; + } else { + return -EAFNOSUPPORT; + } + + // Fill in netlink structures. + memset(&req, 0, sizeof(req)); + + // Netlink message header. + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = action; + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.n.nlmsg_pid = getpid(); + + // Interface address message header. + req.r.ifa_family = sa.ss.ss_family; + req.r.ifa_prefixlen = prefixlen; + req.r.ifa_index = ifindex; + + // Routing attribute. Contains the actual IP address. + rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_type = IFA_LOCAL; + rta->rta_len = RTA_LENGTH(addrlen); + req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen); + memcpy(RTA_DATA(rta), addr, addrlen); + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (send(s, &req, req.n.nlmsg_len, 0) < 0) { + close(s); + return -errno; + } + + len = recv(s, buf.buf, sizeof(buf.buf), 0); + close(s); + if (len < 0) { + return -errno; + } + + // Parse the acknowledgement to find the return code. + if (!NLMSG_OK(&buf.nh, (unsigned) len) || buf.nh.nlmsg_type != NLMSG_ERROR) { + return -EINVAL; + } + err = NLMSG_DATA(&buf.nh); + + // Return code is negative errno. + return err->error; +} + +int ifc_add_address(const char *name, const char *address, int prefixlen) { + return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen); +} + +int ifc_del_address(const char *name, const char * address, int prefixlen) { + return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen); +} + +/* + * Clears IPv6 addresses on the specified interface. + */ +int ifc_clear_ipv6_addresses(const char *name) { + char rawaddrstr[INET6_ADDRSTRLEN], addrstr[INET6_ADDRSTRLEN]; + unsigned int prefixlen; + int lasterror = 0, i, j, ret; + char ifname[64]; // Currently, IFNAMSIZ = 16. + FILE *f = fopen("/proc/net/if_inet6", "r"); + if (!f) { + return -errno; + } + + // Format: + // 20010db8000a0001fc446aa4b5b347ed 03 40 00 01 wlan0 + while (fscanf(f, "%32s %*02x %02x %*02x %*02x %63s\n", + rawaddrstr, &prefixlen, ifname) == 3) { + // Is this the interface we're looking for? + if (strcmp(name, ifname)) { + continue; + } + + // Put the colons back into the address. + for (i = 0, j = 0; i < 32; i++, j++) { + addrstr[j] = rawaddrstr[i]; + if (i % 4 == 3) { + addrstr[++j] = ':'; + } + } + addrstr[j - 1] = '\0'; + + // Don't delete the link-local address as well, or it will disable IPv6 + // on the interface. + if (strncmp(addrstr, "fe80:", 5) == 0) { + continue; + } + + ret = ifc_del_address(ifname, addrstr, prefixlen); + if (ret) { + LOGE("Deleting address %s/%d on %s: %s", addrstr, prefixlen, ifname, + strerror(-ret)); + lasterror = ret; + } + } + + fclose(f); + return lasterror; +} + +/* + * Clears IPv4 addresses on the specified interface. + */ +void ifc_clear_ipv4_addresses(const char *name) { + unsigned count, addr; + ifc_init(); + for (count=0, addr=1;((addr != 0) && (count < 255)); count++) { + if (ifc_get_addr(name, &addr) < 0) + break; + if (addr) + ifc_set_addr(name, 0); + } + ifc_close(); +} + +/* + * Clears all IP addresses on the specified interface. + */ +int ifc_clear_addresses(const char *name) { + ifc_clear_ipv4_addresses(name); + return ifc_clear_ipv6_addresses(name); +} + +int ifc_set_hwaddr(const char *name, const void *ptr) +{ + int r; + struct ifreq ifr; + ifc_init_ifr(name, &ifr); + + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(&ifr.ifr_hwaddr.sa_data, ptr, ETH_ALEN); + return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr); } int ifc_set_mask(const char *name, in_addr_t mask) { struct ifreq ifr; + int ret; ifc_init_ifr(name, &ifr); init_sockaddr_in(&ifr.ifr_addr, mask); - + + ret = ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr); + if (DBG) printerr("ifc_set_mask(%s, xx) = %d", name, ret); + return ret; +} + +int ifc_set_prefixLength(const char *name, int prefixLength) +{ + struct ifreq ifr; + // TODO - support ipv6 + if (prefixLength > 32 || prefixLength < 0) return -1; + + in_addr_t mask = prefixLengthToIpv4Netmask(prefixLength); + ifc_init_ifr(name, &ifr); + init_sockaddr_in(&ifr.ifr_addr, mask); + return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr); } -int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags) +int ifc_get_addr(const char *name, in_addr_t *addr) +{ + struct ifreq ifr; + int ret = 0; + + ifc_init_ifr(name, &ifr); + if (addr != NULL) { + ret = ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr); + if (ret < 0) { + *addr = 0; + } else { + struct sockaddr_in in; + memcpy(&in, &ifr.ifr_addr, sizeof(ifr.ifr_addr)); + memcpy(&addr, &in.sin_addr.s_addr, sizeof(struct sockaddr_in)); + //*addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + } + } + return ret; +} + +int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags) { struct ifreq ifr; ifc_init_ifr(name, &ifr); @@ -162,15 +484,20 @@ int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *f if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) { *addr = 0; } else { - *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + struct sockaddr_in in; + memcpy(&in, &ifr.ifr_addr, sizeof(in)); + *addr = in.sin_addr.s_addr; } } - - if (mask != NULL) { + + if (prefixLength != NULL) { if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) { - *mask = 0; + *prefixLength = 0; } else { - *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + struct sockaddr_in in; + memcpy(&in, &ifr.ifr_addr, sizeof(in)); + *prefixLength = ipv4NetmaskToPrefixLength((int) + in.sin_addr.s_addr); } } @@ -185,45 +512,74 @@ int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *f return 0; } - -int ifc_create_default_route(const char *name, in_addr_t addr) +int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, int prefix_length, + struct in_addr gw) { struct rtentry rt; + int result; + in_addr_t netmask; memset(&rt, 0, sizeof(rt)); - + rt.rt_dst.sa_family = AF_INET; - rt.rt_flags = RTF_UP | RTF_GATEWAY; - rt.rt_dev = (void*) name; - init_sockaddr_in(&rt.rt_genmask, 0); - init_sockaddr_in(&rt.rt_gateway, addr); - - return ioctl(ifc_ctl_sock, SIOCADDRT, &rt); -} + rt.rt_dev = (void*) ifname; -int ifc_add_host_route(const char *name, in_addr_t addr) -{ - struct rtentry rt; - int result; + netmask = prefixLengthToIpv4Netmask(prefix_length); + init_sockaddr_in(&rt.rt_genmask, netmask); + init_sockaddr_in(&rt.rt_dst, dst.s_addr); + rt.rt_flags = RTF_UP; + + if (prefix_length == 32) { + rt.rt_flags |= RTF_HOST; + } + + if (gw.s_addr != 0) { + rt.rt_flags |= RTF_GATEWAY; + init_sockaddr_in(&rt.rt_gateway, gw.s_addr); + } - memset(&rt, 0, sizeof(rt)); - - rt.rt_dst.sa_family = AF_INET; - rt.rt_flags = RTF_UP | RTF_HOST; - rt.rt_dev = (void*) name; - init_sockaddr_in(&rt.rt_dst, addr); - init_sockaddr_in(&rt.rt_genmask, 0); - init_sockaddr_in(&rt.rt_gateway, 0); - ifc_init(); - result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt); - if (result < 0 && errno == EEXIST) { - result = 0; + + if (ifc_ctl_sock < 0) { + return -errno; + } + + result = ioctl(ifc_ctl_sock, action, &rt); + if (result < 0) { + if (errno == EEXIST) { + result = 0; + } else { + result = -errno; + } } ifc_close(); return result; } +/* deprecated - v4 only */ +int ifc_create_default_route(const char *name, in_addr_t gw) +{ + struct in_addr in_dst, in_gw; + + in_dst.s_addr = 0; + in_gw.s_addr = gw; + + int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw); + if (DBG) printerr("ifc_create_default_route(%s, %d) = %d", name, gw, ret); + return ret; +} + +/* deprecated v4-only */ +int ifc_add_host_route(const char *name, in_addr_t dst) +{ + struct in_addr in_dst, in_gw; + + in_dst.s_addr = dst; + in_gw.s_addr = 0; + + return ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 32, in_gw); +} + int ifc_enable(const char *ifname) { int result; @@ -236,29 +592,65 @@ int ifc_enable(const char *ifname) int ifc_disable(const char *ifname) { + unsigned addr, count; int result; ifc_init(); result = ifc_down(ifname); + ifc_set_addr(ifname, 0); + for (count=0, addr=1;((addr != 0) && (count < 255)); count++) { + if (ifc_get_addr(ifname, &addr) < 0) + break; + if (addr) + ifc_set_addr(ifname, 0); + } + ifc_close(); return result; } -int ifc_reset_connections(const char *ifname) +#define RESET_IPV4_ADDRESSES 0x01 +#define RESET_IPV6_ADDRESSES 0x02 +#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) + +int ifc_reset_connections(const char *ifname, const int reset_mask) { #ifdef HAVE_ANDROID_OS - int result; + int result, success; in_addr_t myaddr; struct ifreq ifr; + struct in6_ifreq ifr6; + + if (reset_mask & RESET_IPV4_ADDRESSES) { + /* IPv4. Clear connections on the IP address. */ + ifc_init(); + ifc_get_info(ifname, &myaddr, NULL, NULL); + ifc_init_ifr(ifname, &ifr); + init_sockaddr_in(&ifr.ifr_addr, myaddr); + result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr); + ifc_close(); + } else { + result = 0; + } + + if (reset_mask & RESET_IPV6_ADDRESSES) { + /* + * IPv6. On Linux, when an interface goes down it loses all its IPv6 + * addresses, so we don't know which connections belonged to that interface + * So we clear all unused IPv6 connections on the device by specifying an + * empty IPv6 address. + */ + ifc_init6(); + // This implicitly specifies an address of ::, i.e., kill all IPv6 sockets. + memset(&ifr6, 0, sizeof(ifr6)); + success = ioctl(ifc_ctl_sock6, SIOCKILLADDR, &ifr6); + if (result == 0) { + result = success; + } + ifc_close6(); + } - ifc_init(); - ifc_get_info(ifname, &myaddr, NULL, NULL); - ifc_init_ifr(ifname, &ifr); - init_sockaddr_in(&ifr.ifr_addr, myaddr); - result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr); - ifc_close(); - return result; #else return 0; @@ -318,6 +710,8 @@ int ifc_remove_host_routes(const char *name) * * TODO: factor out common code from this and remove_host_routes() * so that we only scan /proc/net/route in one place. + * + * DEPRECATED */ int ifc_get_default_route(const char *ifname) { @@ -358,6 +752,7 @@ int ifc_get_default_route(const char *ifname) /* * Sets the specified gateway as the default route for the named interface. + * DEPRECATED */ int ifc_set_default_route(const char *ifname, in_addr_t gateway) { @@ -397,7 +792,7 @@ int ifc_remove_default_route(const char *ifname) int ifc_configure(const char *ifname, in_addr_t address, - in_addr_t netmask, + uint32_t prefixLength, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2) { @@ -416,8 +811,8 @@ ifc_configure(const char *ifname, ifc_close(); return -1; } - if (ifc_set_mask(ifname, netmask)) { - printerr("failed to set netmask %s: %s\n", ipaddr_to_string(netmask), strerror(errno)); + if (ifc_set_prefixLength(ifname, prefixLength)) { + printerr("failed to set prefixLength %d: %s\n", prefixLength, strerror(errno)); ifc_close(); return -1; } @@ -429,10 +824,160 @@ ifc_configure(const char *ifname, ifc_close(); - snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns1", ifname); + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname); property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : ""); - snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns2", ifname); + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname); property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : ""); + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.gw", ifname); + property_set(dns_prop_name, gateway ? ipaddr_to_string(gateway) : ""); return 0; } + +int ifc_act_on_ipv6_route(int action, const char *ifname, struct in6_addr dst, int prefix_length, + struct in6_addr gw) +{ + struct in6_rtmsg rtmsg; + int result; + int ifindex; + + memset(&rtmsg, 0, sizeof(rtmsg)); + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + printerr("if_nametoindex() failed: interface %s\n", ifname); + return -ENXIO; + } + + rtmsg.rtmsg_ifindex = ifindex; + rtmsg.rtmsg_dst = dst; + rtmsg.rtmsg_dst_len = prefix_length; + rtmsg.rtmsg_flags = RTF_UP; + + if (prefix_length == 128) { + rtmsg.rtmsg_flags |= RTF_HOST; + } + + if (memcmp(&gw, &in6addr_any, sizeof(in6addr_any))) { + rtmsg.rtmsg_flags |= RTF_GATEWAY; + rtmsg.rtmsg_gateway = gw; + } + + ifc_init6(); + + if (ifc_ctl_sock6 < 0) { + return -errno; + } + + result = ioctl(ifc_ctl_sock6, action, &rtmsg); + if (result < 0) { + if (errno == EEXIST) { + result = 0; + } else { + result = -errno; + } + } + ifc_close6(); + return result; +} + +int ifc_act_on_route(int action, const char *ifname, const char *dst, int prefix_length, + const char *gw) +{ + int ret = 0; + struct sockaddr_in ipv4_dst, ipv4_gw; + struct sockaddr_in6 ipv6_dst, ipv6_gw; + struct addrinfo hints, *addr_ai, *gw_ai; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST; + + ret = getaddrinfo(dst, NULL, &hints, &addr_ai); + + if (ret != 0) { + printerr("getaddrinfo failed: invalid address %s\n", dst); + return -EINVAL; + } + + if (gw == NULL || (strlen(gw) == 0)) { + if (addr_ai->ai_family == AF_INET6) { + gw = "::"; + } else if (addr_ai->ai_family == AF_INET) { + gw = "0.0.0.0"; + } + } + + if (((addr_ai->ai_family == AF_INET6) && (prefix_length < 0 || prefix_length > 128)) || + ((addr_ai->ai_family == AF_INET) && (prefix_length < 0 || prefix_length > 32))) { + printerr("ifc_add_route: invalid prefix length"); + freeaddrinfo(addr_ai); + return -EINVAL; + } + + ret = getaddrinfo(gw, NULL, &hints, &gw_ai); + if (ret != 0) { + printerr("getaddrinfo failed: invalid gateway %s\n", gw); + freeaddrinfo(addr_ai); + return -EINVAL; + } + + if (addr_ai->ai_family != gw_ai->ai_family) { + printerr("ifc_add_route: different address families: %s and %s\n", dst, gw); + freeaddrinfo(addr_ai); + freeaddrinfo(gw_ai); + return -EINVAL; + } + + if (addr_ai->ai_family == AF_INET6) { + memcpy(&ipv6_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in6)); + memcpy(&ipv6_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in6)); + ret = ifc_act_on_ipv6_route(action, ifname, ipv6_dst.sin6_addr, + prefix_length, ipv6_gw.sin6_addr); + } else if (addr_ai->ai_family == AF_INET) { + memcpy(&ipv4_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in)); + memcpy(&ipv4_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in)); + ret = ifc_act_on_ipv4_route(action, ifname, ipv4_dst.sin_addr, + prefix_length, ipv4_gw.sin_addr); + } else { + printerr("ifc_add_route: getaddrinfo returned un supported address family %d\n", + addr_ai->ai_family); + ret = -EAFNOSUPPORT; + } + + freeaddrinfo(addr_ai); + freeaddrinfo(gw_ai); + return ret; +} + +/* + * DEPRECATED + */ +int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length, + struct in_addr gw) +{ + int i =ifc_act_on_ipv4_route(SIOCADDRT, ifname, dst, prefix_length, gw); + if (DBG) printerr("ifc_add_ipv4_route(%s, xx, %d, xx) = %d", ifname, prefix_length, i); + return i; +} + +/* + * DEPRECATED + */ +int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length, + struct in6_addr gw) +{ + return ifc_act_on_ipv6_route(SIOCADDRT, ifname, dst, prefix_length, gw); +} + +int ifc_add_route(const char *ifname, const char *dst, int prefix_length, const char *gw) +{ + int i = ifc_act_on_route(SIOCADDRT, ifname, dst, prefix_length, gw); + if (DBG) printerr("ifc_add_route(%s, %s, %d, %s) = %d", ifname, dst, prefix_length, gw, i); + return i; +} + +int ifc_remove_route(const char *ifname, const char*dst, int prefix_length, const char *gw) +{ + return ifc_act_on_route(SIOCDELRT, ifname, dst, prefix_length, gw); +} diff --git a/libnetutils/ifc_utils.h b/libnetutils/ifc_utils.h deleted file mode 100644 index 49b8747a31c..00000000000 --- a/libnetutils/ifc_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2008, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _IFC_UTILS_H_ -#define _IFC_UTILS_H_ - -int ifc_init(void); - -int ifc_get_ifindex(const char *name, int *if_indexp); -int ifc_get_hwaddr(const char *name, void *ptr); - -int ifc_up(const char *name); -int ifc_down(const char *name); - -int ifc_set_addr(const char *name, unsigned addr); -int ifc_set_mask(const char *name, unsigned mask); - -int ifc_create_default_route(const char *name, unsigned addr); - -int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags); - -#endif diff --git a/libnetutils/packet.c b/libnetutils/packet.c index 9388345c797..c37c34b2cf2 100644 --- a/libnetutils/packet.c +++ b/libnetutils/packet.c @@ -42,20 +42,23 @@ int fatal(); int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index) { int s, flag; - struct sockaddr_ll bindaddr; + union { + struct sockaddr_ll ll; + struct sockaddr generic; + } bindaddr; if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { return fatal("socket(PF_PACKET)"); } memset(&bindaddr, 0, sizeof(bindaddr)); - bindaddr.sll_family = AF_PACKET; - bindaddr.sll_protocol = htons(ETH_P_IP); - bindaddr.sll_halen = ETH_ALEN; - memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN); - bindaddr.sll_ifindex = if_index; + bindaddr.ll.sll_family = AF_PACKET; + bindaddr.ll.sll_protocol = htons(ETH_P_IP); + bindaddr.ll.sll_halen = ETH_ALEN; + memcpy(bindaddr.ll.sll_addr, hwaddr, ETH_ALEN); + bindaddr.ll.sll_ifindex = if_index; - if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { + if (bind(s, &bindaddr.generic, sizeof(bindaddr.ll)) < 0) { return fatal("Cannot bind raw socket to interface"); } diff --git a/libnl_2/.gitignore b/libnl_2/.gitignore new file mode 100644 index 00000000000..d4ca74416e6 --- /dev/null +++ b/libnl_2/.gitignore @@ -0,0 +1,2 @@ +include/netlink/version.h.in +cscope.* diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk new file mode 100644 index 00000000000..1745f5a4b76 --- /dev/null +++ b/libnl_2/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + attr.c \ + cache.c \ + genl/genl.c \ + genl/family.c \ + handlers.c \ + msg.c \ + netlink.c \ + object.c \ + socket.c \ + dbg.c + +LOCAL_C_INCLUDES += \ + external/libnl-headers + +# Static Library +LOCAL_MODULE := libnl_2 +LOCAL_MODULE_TAGS := optional +include $(BUILD_STATIC_LIBRARY) + +####################################### +# Shared library currently unavailiable +# * Netlink cache not implemented +# * Library is not thread safe +####################################### + diff --git a/libnl_2/README b/libnl_2/README new file mode 100644 index 00000000000..14db6db2387 --- /dev/null +++ b/libnl_2/README @@ -0,0 +1,88 @@ +Netlink Protocol Library + +This library is a clean room re-implementation of libnl 2.0 and +re-licensed under Apache 2.0. It was developed primarily to support +wpa_supplicant. However, with additional development can be extended +to support other netlink applications. + +Netlink Protocol Format (RFC3549) + ++-----------------+-+-------------------+-+ +|Netlink Message |P| Generic Netlink |P| +| Header |A| Message Header |A| +|(struct nlmsghdr)|D|(struct genlmsghdr)|D| ++-----------------+-+-------------------+-+-------------+ +|len:4|type:2|flags:2|seq:4 pid:4|cmd:1|ver:1|reserved:2| ++--------------------------------+----------------------+ ++-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+ +|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|...| +| #0 Header |A| #0 Payload |A| #1 Header |A| #1 Payload |A| | +| (struct nlattr) |D| (void) |D| (struct nlattr) |D| (void) |D| | ++-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+ +|len:2(==4+payload)|type:2|payload|pad| ++-------------------------+-------+---+ + +NETLINK OVERVIEW + +* Each netlink message consists of a bitstream with a netlink header. +* After this header a second header *can* be used specific to the netlink + family in use. This library was tested using the generic netlink + protocol defined by struct genlmsghdr to support nl80211. +* After the header(s) netlink attributes can be appended to the message + which hold can hold basic types such as unsigned integers and strings. +* Attributes can also be nested. This is accomplished by calling "nla_nest_start" + which creates an empty attribute with nest attributes as its payload. Then to + close the nest, "nla_nest_end" is called. +* All data structures in this implementation are byte-aligned (Currently 4 bytes). +* Acknowledgements (ACKs) are sent as NLMSG_ERROR netlink message types (0x2) and + have an error value of 0. + +KNOWN ISSUES + + GENERAL + * Not tested for thread safety + + Android.mk + * No dynamic library because of netlink cache not implemented and + not tested for thread safety + + attr.c + * nla_parse - does not use nla_policy argument + + cache.c + * netlink cache not implemented and only supports one netlink family id + which is stored in the nl_cache pointer instead of an actual cache + + netlink.c + * nl_recvmsgs - does not support nl_cb_overwrite_recv() + * nl_recv - sets/unsets asynchronous socket flag + +SOURCE FILES + +* Android.mk - Android makefile +* README - This file +* attr.c - Netlink attributes +* cache.c - Netlink cache +* genl/family.c - Generic netlink family id +* genl/genl.c - Generic netlink +* handlers.c - Netlink callbacks +* msg.c - Netlink messages construction +* netlink.c - Netlink socket communication +* object.c - libnl object wrapper +* socket.c - Netlink kernel socket utils + +IMPORTANT HEADER FILES - NOTE: These are based on the the origin GPL libnl headers + +* netlink-types.h - Contains many important structs for libnl + to represent netlink objects +* netlink/netlink-kernel.h - Netlink kernel headers and field constants. +* netlink/msg.h - macros for iterating over netlink messages +* netlink/attr.h - netlink attribute constants, iteration macros and setters + +REFERENCES + +* nl80211.h +* netlink_types.h +* $LINUX_KERNEL/net/wireless/nl80211.c +* http://www.infradead.org/~tgr/libnl/doc-3.0/index.html +* http://www.netfilter.org/projects/libmnl/doxygen/index.html diff --git a/libnl_2/attr.c b/libnl_2/attr.c new file mode 100644 index 00000000000..f3a2b58c482 --- /dev/null +++ b/libnl_2/attr.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include +#include "netlink/netlink.h" +#include "netlink/msg.h" +#include "netlink/attr.h" +#include "netlink-types.h" + +/* Return payload of string attribute. */ +char *nla_get_string(struct nlattr *nla) +{ + return (char *) nla_data(nla); +} + +/* Return payload of 16 bit integer attribute. */ +uint16_t nla_get_u16(struct nlattr *nla) +{ + return *((uint16_t *) nla_data(nla)); +} + +/* Return payload of 32 bit integer attribute. */ +uint32_t nla_get_u32(struct nlattr *nla) +{ + return *((uint32_t *) nla_data(nla)); +} + +/* Return value of 8 bit integer attribute. */ +uint8_t nla_get_u8(struct nlattr *nla) +{ + return *((uint8_t *) nla_data(nla)); +} + +/* Return payload of uint64_t attribute. */ +uint64_t nla_get_u64(struct nlattr *nla) +{ + uint64_t tmp; + nla_memcpy(&tmp, nla, sizeof(tmp)); + return tmp; +} + +/* Head of payload */ +void *nla_data(const struct nlattr *nla) +{ + return (void *) ((char *) nla + NLA_HDRLEN); +} + +/* Return length of the payload . */ +int nla_len(const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +int nla_padlen(int payload) +{ + return NLA_ALIGN(payload) - payload; +} + +/* Start a new level of nested attributes. */ +struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *)nlmsg_tail(msg->nm_nlh); + int rc; + + rc = nla_put(msg, attrtype, 0, NULL); + if (rc < 0) + return NULL; + + return start; +} + +/* Finalize nesting of attributes. */ +int nla_nest_end(struct nl_msg *msg, struct nlattr *start) +{ + /* Set attribute size */ + start->nla_len = (unsigned char *)nlmsg_tail(nlmsg_hdr(msg)) - + (unsigned char *)start; + return 0; +} + +/* Return next attribute in a stream of attributes. */ +struct nlattr *nla_next(const struct nlattr *nla, int *remaining) +{ + struct nlattr *next_nla = NULL; + if (nla->nla_len >= sizeof(struct nlattr) && + nla->nla_len <= *remaining){ + next_nla = (struct nlattr *) \ + ((char *) nla + NLA_ALIGN(nla->nla_len)); + *remaining = *remaining - NLA_ALIGN(nla->nla_len); + } + + return next_nla; + +} + +/* Check if the attribute header and payload can be accessed safely. */ +int nla_ok(const struct nlattr *nla, int remaining) +{ + return remaining > 0 && + nla->nla_len >= sizeof(struct nlattr) && + sizeof(struct nlattr) <= (unsigned int) remaining && + nla->nla_len <= remaining; +} + +/* Create attribute index based on a stream of attributes. */ +/* NOTE: Policy not used ! */ +int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, + int len, struct nla_policy *policy) +{ + struct nlattr *pos; + int rem; + + /* First clear table */ + memset(tb, 0, (maxtype + 1) * sizeof(struct nlattr *)); + + nla_for_each_attr(pos, head, len, rem) { + int type = nla_type(pos); + + if ((type <= maxtype) && (type != 0)) + tb[type] = pos; + } + + return 0; +} + + +/* Create attribute index based on nested attribute. */ +int nla_parse_nested(struct nlattr *tb[], int maxtype, + struct nlattr *nla, struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + + +/* Add a unspecific attribute to netlink message. */ +int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + /* Reserve space and init nla header */ + nla = nla_reserve(msg, attrtype, datalen); + if (nla) { + memcpy(nla_data(nla), data, datalen); + return 0; + } + + return -EINVAL; + +} + + +/* Add nested attributes to netlink message. */ +/* Takes the attributes found in the nested message and appends them + * to the message msg nested in a container of the type attrtype. The + * nested message may not have a family specific header */ +int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested) +{ + int rc; + + rc = nla_put(msg, attrtype, nlmsg_attrlen(nlmsg_hdr(nested), 0), + nlmsg_attrdata(nlmsg_hdr(nested), 0)); + return rc; + +} + +/* Return type of the attribute. */ +int nla_type(const struct nlattr *nla) +{ + return (int)nla->nla_type & NLA_TYPE_MASK; +} + +/* Reserves room for an attribute in specified netlink message and fills + * in the attribute header (type,length). Return NULL if insufficient space */ +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int data_len) +{ + + struct nlattr *nla; + const unsigned int NEW_SIZE = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + + NLA_ALIGN(NLA_HDRLEN + data_len); + + /* Check enough space for attribute */ + if (NEW_SIZE > msg->nm_size) + return NULL; + + nla = (struct nlattr *)nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = NLA_HDRLEN + data_len; + memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(data_len)); + msg->nm_nlh->nlmsg_len = NEW_SIZE; + return nla; +} + +/* Copy attribute payload to another memory area. */ +int nla_memcpy(void *dest, struct nlattr *src, int count) +{ + if (!src || !dest) + return 0; + if (count > nla_len(src)) + count = nla_len(src); + memcpy(dest, nla_data(src), count); + return count; +} diff --git a/libnl_2/cache.c b/libnl_2/cache.c new file mode 100644 index 00000000000..c21974d6d87 --- /dev/null +++ b/libnl_2/cache.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include "netlink/cache.h" +#include "netlink/object.h" + +void nl_cache_free(struct nl_cache *cache) +{ + +} + +void nl_cache_clear(struct nl_cache *cache) +{ + +} + +void nl_cache_remove(struct nl_object *obj) +{ + +} + + diff --git a/libnl_2/dbg.c b/libnl_2/dbg.c new file mode 100644 index 00000000000..9764de6e758 --- /dev/null +++ b/libnl_2/dbg.c @@ -0,0 +1,12 @@ +#include "netlink/netlink.h" +#include + +void libnl_printf(int level, char *format, ...) +{ + va_list ap; + + level = ANDROID_LOG_ERROR; + va_start(ap, format); + __android_log_vprint(level, "libnl_2", format, ap); + va_end(ap); +} diff --git a/libnl_2/genl/family.c b/libnl_2/genl/family.c new file mode 100644 index 00000000000..1beee6e7f09 --- /dev/null +++ b/libnl_2/genl/family.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include "netlink-types.h" + +static struct genl_family *genl_family_find_byname(const char *name) +{ + return NULL; +} + +/* Release reference and none outstanding */ +void genl_family_put(struct genl_family *family) +{ + family->ce_refcnt--; + if (family->ce_refcnt <= 0) + free(family); +} + +unsigned int genl_family_get_id(struct genl_family *family) +{ + const int NO_FAMILY_ID = 0; + + if (!family) + return NO_FAMILY_ID; + else + return family->gf_id; + +} + diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c new file mode 100644 index 00000000000..244299327c3 --- /dev/null +++ b/libnl_2/genl/genl.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include +#include +#include +#include +#include +#include "netlink-types.h" + +/* Get head of attribute data. */ +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return (struct nlattr *) \ + ((char *) gnlh + GENL_HDRLEN + NLMSG_ALIGN(hdrlen)); + +} + +/* Get length of attribute data. */ +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) +{ + struct nlattr *nla; + struct nlmsghdr *nlh; + + nla = genlmsg_attrdata(gnlh, hdrlen); + nlh = (struct nlmsghdr *) ((char *) gnlh - NLMSG_HDRLEN); + return (char *) nlmsg_tail(nlh) - (char *) nla; +} + +/* Add generic netlink header to netlink message. */ +void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + int new_size; + struct nlmsghdr *nlh; + struct timeval tv; + struct genlmsghdr *gmh; + + /* Make sure nl_msg has enough space */ + new_size = NLMSG_HDRLEN + GENL_HDRLEN + hdrlen; + if ((sizeof(struct nl_msg) + new_size) > msg->nm_size) + goto fail; + + /* Fill in netlink header */ + nlh = msg->nm_nlh; + nlh->nlmsg_len = new_size; + nlh->nlmsg_type = family; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK; + + /* Get current time for sequence number */ + if (gettimeofday(&tv, NULL)) + nlh->nlmsg_seq = 1; + else + nlh->nlmsg_seq = (int) tv.tv_sec; + + /* Setup genlmsghdr in new message */ + gmh = (struct genlmsghdr *) ((char *)nlh + NLMSG_HDRLEN); + gmh->cmd = (__u8) cmd; + gmh->version = version; + + return gmh; +fail: + return NULL; + +} + +/* Socket has already been alloced to connect it to kernel? */ +int genl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_GENERIC); + +} + +int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +{ + int rc = -1; + int nl80211_genl_id = -1; + char sendbuf[sizeof(struct nlmsghdr)+sizeof(struct genlmsghdr)]; + struct nlmsghdr nlmhdr; + struct genlmsghdr gmhhdr; + struct iovec sendmsg_iov; + struct msghdr msg; + int num_char; + const int RECV_BUF_SIZE = getpagesize(); + char *recvbuf; + struct iovec recvmsg_iov; + int nl80211_flag = 0, nlm_f_multi = 0, nlmsg_done = 0; + struct nlmsghdr *nlh; + + /* REQUEST GENERIC NETLINK FAMILY ID */ + /* Message buffer */ + nlmhdr.nlmsg_len = sizeof(sendbuf); + nlmhdr.nlmsg_type = NETLINK_GENERIC; + nlmhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; + nlmhdr.nlmsg_seq = sock->s_seq_next; + nlmhdr.nlmsg_pid = sock->s_local.nl_pid; + + /* Generic netlink header */ + memset(&gmhhdr, 0, sizeof(gmhhdr)); + gmhhdr.cmd = CTRL_CMD_GETFAMILY; + gmhhdr.version = CTRL_ATTR_FAMILY_ID; + + /* Combine netlink and generic netlink headers */ + memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr)); + memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr)); + + /* Create IO vector with Netlink message */ + sendmsg_iov.iov_base = &sendbuf; + sendmsg_iov.iov_len = sizeof(sendbuf); + + /* Socket message */ + msg.msg_name = (void *) &sock->s_peer; + msg.msg_namelen = sizeof(sock->s_peer); + msg.msg_iov = &sendmsg_iov; + msg.msg_iovlen = 1; /* Only sending one iov */ + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* Send message and verify sent */ + num_char = sendmsg(sock->s_fd, &msg, 0); + if (num_char == -1) + return -errno; + + /* RECEIVE GENL CMD RESPONSE */ + + /* Create receive iov buffer */ + recvbuf = (char *) malloc(RECV_BUF_SIZE); + + /* Attach to iov */ + recvmsg_iov.iov_base = recvbuf; + recvmsg_iov.iov_len = RECV_BUF_SIZE; + + msg.msg_iov = &recvmsg_iov; + msg.msg_iovlen = 1; + + /***************************************************************/ + /* Receive message. If multipart message, keep receiving until */ + /* message type is NLMSG_DONE */ + /***************************************************************/ + + do { + + int recvmsg_len, nlmsg_rem; + + /* Receive message */ + memset(recvbuf, 0, RECV_BUF_SIZE); + recvmsg_len = recvmsg(sock->s_fd, &msg, 0); + + /* Make sure receive successful */ + if (recvmsg_len < 0) { + rc = -errno; + goto error_recvbuf; + } + + /* Parse nlmsghdr */ + nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \ + recvmsg_len, nlmsg_rem) { + struct nlattr *nla; + int nla_rem; + + /* Check type */ + switch (nlh->nlmsg_type) { + case NLMSG_DONE: + goto return_genl_id; + break; + case NLMSG_ERROR: + + /* Should check nlmsgerr struct received */ + fprintf(stderr, "Receive message error\n"); + goto error_recvbuf; + case NLMSG_OVERRUN: + fprintf(stderr, "Receive data partly lost\n"); + goto error_recvbuf; + case NLMSG_MIN_TYPE: + case NLMSG_NOOP: + break; + default: + break; + } + + + + /* Check flags */ + if (nlh->nlmsg_flags & NLM_F_MULTI) + nlm_f_multi = 1; + else + nlm_f_multi = 0; + + if (nlh->nlmsg_type & NLMSG_DONE) + nlmsg_done = 1; + else + nlmsg_done = 0; + + /* Iteratve over attributes */ + nla_for_each_attr(nla, + nlmsg_attrdata(nlh, GENL_HDRLEN), + nlmsg_attrlen(nlh, GENL_HDRLEN), + nla_rem){ + + /* If this family is nl80211 */ + if (nla->nla_type == CTRL_ATTR_FAMILY_NAME && + !strcmp((char *)nla_data(nla), + "nl80211")) + nl80211_flag = 1; + + /* Save the family id */ + else if (nl80211_flag && + nla->nla_type == CTRL_ATTR_FAMILY_ID) { + nl80211_genl_id = + *((int *)nla_data(nla)); + nl80211_flag = 0; + } + + } + + } + + } while (nlm_f_multi && !nlmsg_done); + +return_genl_id: + /* Return family id as cache pointer */ + *result = (struct nl_cache *) nl80211_genl_id; + rc = 0; +error_recvbuf: + free(recvbuf); +error: + return rc; +} + +/* Checks the netlink cache to find family reference by name string */ +/* NOTE: Caller needs to call genl_family_put() when done with * + * returned object */ +struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ + const char *name) +{ + /* TODO: When will we release this memory ? */ + struct genl_family *gf = (struct genl_family *) \ + malloc(sizeof(struct genl_family)); + if (!gf) + goto fail; + memset(gf, 0, sizeof(*gf)); + + /* Add ref */ + gf->ce_refcnt++; + + /* Overriding cache pointer as family id for now */ + gf->gf_id = (uint16_t) ((uint32_t) cache); + strcpy(gf->gf_name, "nl80211"); + + return gf; +fail: + return NULL; + +} + +int genl_ctrl_resolve(struct nl_sock *sk, const char *name) +{ + /* Hack to support wpa_supplicant */ + if (strcmp(name, "nlctrl") == 0) + return NETLINK_GENERIC; + else { + int errsv = errno; + fprintf(stderr, \ + "Only nlctrl supported by genl_ctrl_resolve!\n"); + return -errsv; + } + +} + diff --git a/libnl_2/handlers.c b/libnl_2/handlers.c new file mode 100644 index 00000000000..48dcab4a82f --- /dev/null +++ b/libnl_2/handlers.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include +#include "netlink-types.h" +#include "netlink/handlers.h" + +/* Allocate a new callback handle. */ +struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) +{ + struct nl_cb *cb; + + cb = (struct nl_cb *) malloc(sizeof(struct nl_cb)); + if (cb == NULL) + goto fail; + memset(cb, 0, sizeof(*cb)); + + return nl_cb_get(cb); +fail: + return NULL; +} + +/* Clone an existing callback handle */ +struct nl_cb *nl_cb_clone(struct nl_cb *orig) +{ + struct nl_cb *new_cb; + + new_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (new_cb == NULL) + goto fail; + + /* Copy original and set refcount to 1 */ + memcpy(new_cb, orig, sizeof(*orig)); + new_cb->cb_refcnt = 1; + + return new_cb; +fail: + return NULL; +} + +/* Set up a callback. */ +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, \ + nl_recvmsg_msg_cb_t func, void *arg) +{ + cb->cb_set[type] = func; + cb->cb_args[type] = arg; + return 0; +} + + + +/* Set up an error callback. */ +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, \ + nl_recvmsg_err_cb_t func, void *arg) +{ + cb->cb_err = func; + cb->cb_err_arg = arg; + return 0; + +} + +struct nl_cb *nl_cb_get(struct nl_cb *cb) +{ + cb->cb_refcnt++; + return cb; +} + +void nl_cb_put(struct nl_cb *cb) +{ + if (!cb) + return; + cb->cb_refcnt--; + if (cb->cb_refcnt <= 0) + free(cb); +} diff --git a/libnl_2/msg.c b/libnl_2/msg.c new file mode 100644 index 00000000000..283da6e9406 --- /dev/null +++ b/libnl_2/msg.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include +#include +#include +#include "netlink-types.h" + +/* Allocate a new netlink message with the default maximum payload size. */ +struct nl_msg *nlmsg_alloc(void) +{ + /* Whole page will store nl_msg + nlmsghdr + genlmsghdr + payload */ + const int page_sz = getpagesize(); + struct nl_msg *nm; + struct nlmsghdr *nlh; + + /* Netlink message */ + nm = (struct nl_msg *) malloc(page_sz); + if (!nm) + goto fail; + + /* Netlink message header pointer */ + nlh = (struct nlmsghdr *) ((char *) nm + sizeof(struct nl_msg)); + + /* Initialize */ + memset(nm, 0, page_sz); + nm->nm_size = page_sz; + + nm->nm_src.nl_family = AF_NETLINK; + nm->nm_src.nl_pid = getpid(); + + nm->nm_dst.nl_family = AF_NETLINK; + nm->nm_dst.nl_pid = 0; /* Kernel */ + + /* Initialize and add to netlink message */ + nlh->nlmsg_len = NLMSG_HDRLEN; + nm->nm_nlh = nlh; + + /* Add to reference count and return nl_msg */ + nlmsg_get(nm); + return nm; +fail: + return NULL; +} + +/* Return pointer to message payload. */ +void *nlmsg_data(const struct nlmsghdr *nlh) +{ + return (char *) nlh + NLMSG_HDRLEN; +} + +/* Add reference count to nl_msg */ +void nlmsg_get(struct nl_msg *nm) +{ + nm->nm_refcnt++; +} + +/* Release a reference from an netlink message. */ +void nlmsg_free(struct nl_msg *nm) +{ + if (nm) { + nm->nm_refcnt--; + if (nm->nm_refcnt <= 0) + free(nm); + } + +} + +/* Return actual netlink message. */ +struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) +{ + return n->nm_nlh; +} + +/* Return head of attributes data / payload section */ +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *)(data + NLMSG_ALIGN(hdrlen)); +} + +/* Returns pointer to end of netlink message */ +void *nlmsg_tail(const struct nlmsghdr *nlh) +{ + return (void *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); +} + +/* Next netlink message in message stream */ +struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + struct nlmsghdr *next_nlh = NULL; + int len = nlmsg_len(nlh); + + len = NLMSG_ALIGN(len); + if (*remaining > 0 && + len <= *remaining && + len >= (int) sizeof(struct nlmsghdr)) { + next_nlh = (struct nlmsghdr *)((char *)nlh + len); + *remaining -= len; + } + + return next_nlh; +} + +int nlmsg_datalen(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +/* Length of attributes data */ +int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen); +} + +/* Length of netlink message */ +int nlmsg_len(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len; +} + +/* Check if the netlink message fits into the remaining bytes */ +int nlmsg_ok(const struct nlmsghdr *nlh, int rem) +{ + return rem >= (int)sizeof(struct nlmsghdr) && + rem >= nlmsg_len(nlh) && + nlmsg_len(nlh) >= (int) sizeof(struct nlmsghdr) && + nlmsg_len(nlh) <= (rem); +} + +int nlmsg_padlen(int payload) +{ + return NLMSG_ALIGN(payload) - payload; +} diff --git a/libnl_2/netlink.c b/libnl_2/netlink.c new file mode 100644 index 00000000000..ee3d600f7a9 --- /dev/null +++ b/libnl_2/netlink.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include +#include +#include +#include +#include +#include "netlink-types.h" + +#define NL_BUFFER_SZ (32768U) + +/* Checks message for completeness and sends it out */ +int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = msg->nm_nlh; + struct timeval tv; + + if (!nlh) { + int errsv = errno; + fprintf(stderr, "Netlink message header is NULL!\n"); + return -errsv; + } + + /* Complete the nl_msg header */ + if (gettimeofday(&tv, NULL)) + nlh->nlmsg_seq = 1; + else + nlh->nlmsg_seq = (int) tv.tv_sec; + nlh->nlmsg_pid = sk->s_local.nl_pid; + nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK; + + return nl_send(sk, msg); +} + +/* Receives a netlink message, allocates a buffer in *buf and stores + * the message content. The peer's netlink address is stored in + * *nla. The caller is responsible for freeing the buffer allocated in + * *buf if a positive value is returned. Interrupted system calls are + * handled by repeating the read. The input buffer size is determined + * by peeking before the actual read is done */ +int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, \ + unsigned char **buf, struct ucred **creds) +{ + int rc = -1; + int sk_flags; + int RECV_BUF_SIZE = getpagesize(); + int errsv; + struct iovec recvmsg_iov; + struct msghdr msg; + + /* Allocate buffer */ + *buf = (unsigned char *) malloc(RECV_BUF_SIZE); + if (!(*buf)) { + rc = -ENOMEM; + goto fail; + } + + /* Prepare to receive message */ + recvmsg_iov.iov_base = *buf; + recvmsg_iov.iov_len = RECV_BUF_SIZE; + + msg.msg_name = (void *) &sk->s_peer; + msg.msg_namelen = sizeof(sk->s_peer); + msg.msg_iov = &recvmsg_iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* Make non blocking and then restore previous setting */ + sk_flags = fcntl(sk->s_fd, F_GETFL, 0); + fcntl(sk->s_fd, F_SETFL, O_NONBLOCK); + rc = recvmsg(sk->s_fd, &msg, 0); + errsv = errno; + fcntl(sk->s_fd, F_SETFL, sk_flags); + + if (rc < 0) { + rc = -errsv; + free(*buf); + *buf = NULL; + } + +fail: + return rc; +} + +/* Receive a set of messages from a netlink socket */ +/* NOTE: Does not currently support callback replacements!!! */ +int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) +{ + struct sockaddr_nl nla; + struct ucred *creds; + + int rc, cb_rc = NL_OK, done = 0; + + do { + unsigned char *buf; + int i, rem, flags; + struct nlmsghdr *nlh; + struct nlmsgerr *nlme; + struct nl_msg *msg; + + done = 0; + rc = nl_recv(sk, &nla, &buf, &creds); + if (rc < 0) + break; + + nlmsg_for_each_msg(nlh, (struct nlmsghdr *) buf, rc, rem) { + + if (rc <= 0 || cb_rc == NL_STOP) + break; + + /* Check for callbacks */ + + msg = (struct nl_msg *) malloc(sizeof(struct nl_msg)); + memset(msg, 0, sizeof(*msg)); + msg->nm_nlh = nlh; + + /* Check netlink message type */ + + switch (msg->nm_nlh->nlmsg_type) { + case NLMSG_ERROR: /* Used for ACK too */ + /* Certainly we should be doing some + * checking here to make sure this + * message is intended for us */ + nlme = nlmsg_data(msg->nm_nlh); + if (nlme->error == 0) + msg->nm_nlh->nlmsg_flags |= NLM_F_ACK; + + rc = nlme->error; + cb_rc = cb->cb_err(&nla, nlme, cb->cb_err_arg); + nlme = NULL; + break; + + case NLMSG_DONE: + done = 1; + + case NLMSG_OVERRUN: + case NLMSG_NOOP: + default: + break; + }; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) { + + if (cb->cb_set[i]) { + switch (i) { + case NL_CB_VALID: + if (rc > 0) + cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); + break; + + case NL_CB_FINISH: + if ((msg->nm_nlh->nlmsg_flags & NLM_F_MULTI) && + (msg->nm_nlh->nlmsg_type & NLMSG_DONE)) + cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); + + break; + + case NL_CB_ACK: + if (msg->nm_nlh->nlmsg_flags & NLM_F_ACK) + cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); + + break; + default: + break; + } + } + } + + free(msg); + if (done) + break; + } + free(buf); + buf = NULL; + + if (done) + break; + } while (rc > 0 && cb_rc != NL_STOP); + +success: +fail: + return rc; +} + +/* Send raw data over netlink socket */ +int nl_send(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct iovec msg_iov; + + /* Create IO vector with Netlink message */ + msg_iov.iov_base = nlh; + msg_iov.iov_len = nlh->nlmsg_len; + + return nl_send_iovec(sk, msg, &msg_iov, 1); +} + +/* Send netlink message */ +int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, + struct iovec *iov, unsigned iovlen) +{ + int rc; + + /* Socket message */ + struct msghdr mh = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof(sk->s_peer), + .msg_iov = iov, + .msg_iovlen = iovlen, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + + /* Send message and verify sent */ + rc = nl_sendmsg(sk, (struct nl_msg *) &mh, 0); + if (rc < 0) + fprintf(stderr, "Error sending netlink message: %d\n", errno); + return rc; + +} + +/* Send netlink message with control over sendmsg() message header */ +int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + return sendmsg(sk->s_fd, (struct msghdr *) msg, (int) hdr); +} + +/* Create and connect netlink socket */ +int nl_connect(struct nl_sock *sk, int protocol) +{ + struct sockaddr addr; + socklen_t addrlen; + int rc; + + /* Create RX socket */ + sk->s_fd = socket(PF_NETLINK, SOCK_RAW, protocol); + if (sk->s_fd < 0) + return -errno; + + /* Set size of RX and TX buffers */ + if (nl_socket_set_buffer_size(sk, NL_BUFFER_SZ, NL_BUFFER_SZ) < 0) + return -errno; + + /* Bind RX socket */ + rc = bind(sk->s_fd, (struct sockaddr *)&sk->s_local, \ + sizeof(sk->s_local)); + if (rc < 0) + return -errno; + addrlen = sizeof(addr); + getsockname(sk->s_fd, &addr, &addrlen); + + return 0; + +} diff --git a/libnl_2/object.c b/libnl_2/object.c new file mode 100644 index 00000000000..c53accf016f --- /dev/null +++ b/libnl_2/object.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include "netlink-types.h" + +void nl_object_put(struct nl_object *obj) +{ + obj->ce_refcnt--; + if (!obj->ce_refcnt) + nl_object_free(obj); +} + +void nl_object_free(struct nl_object *obj) +{ + nl_cache_remove(obj); +} + + diff --git a/libnl_2/socket.c b/libnl_2/socket.c new file mode 100644 index 00000000000..d6f9a85afe8 --- /dev/null +++ b/libnl_2/socket.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include +#include +#include +#include +#include +#include "netlink-types.h" + +/* Join group */ +int nl_socket_add_membership(struct nl_sock *sk, int group) +{ + return setsockopt(sk->s_fd, SOL_NETLINK, + NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); +} + +/* Allocate new netlink socket. */ +static struct nl_sock *_nl_socket_alloc(void) +{ + struct nl_sock *sk; + struct timeval tv; + struct nl_cb *cb; + + sk = (struct nl_sock *) malloc(sizeof(struct nl_sock)); + if (!sk) + return NULL; + memset(sk, 0, sizeof(*sk)); + + /* Get current time */ + + if (gettimeofday(&tv, NULL)) + goto fail; + else + sk->s_seq_next = (int) tv.tv_sec; + + /* Create local socket */ + sk->s_local.nl_family = AF_NETLINK; + sk->s_local.nl_pid = 0; /* Kernel fills in pid */ + sk->s_local.nl_groups = 0; /* No groups */ + + /* Create peer socket */ + sk->s_peer.nl_family = AF_NETLINK; + sk->s_peer.nl_pid = 0; /* Kernel */ + sk->s_peer.nl_groups = 0; /* No groups */ + + return sk; +fail: + free(sk); + return NULL; +} + +/* Allocate new netlink socket. */ +struct nl_sock *nl_socket_alloc(void) +{ + struct nl_sock *sk = _nl_socket_alloc(); + struct nl_cb *cb; + + if (!sk) + return NULL; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto cb_fail; + sk->s_cb = cb; + return sk; +cb_fail: + free(sk); + return NULL; +} + +/* Allocate new socket with custom callbacks. */ +struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) +{ + struct nl_sock *sk = _nl_socket_alloc(); + + if (!sk) + return NULL; + + sk->s_cb = cb; + nl_cb_get(cb); + + return sk; +} + +/* Callback Handler */ +struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) +{ + return nl_cb_get(sk->s_cb); +} + +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) +{ + nl_cb_put(sk->s_cb); + sk->s_cb = nl_cb_get(cb); +} + +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, + enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, + void *arg) +{ + return nl_cb_set(sk->s_cb, type, kind, func, arg); +} + +/* Free a netlink socket. */ +void nl_socket_free(struct nl_sock *sk) +{ + nl_cb_put(sk->s_cb); + close(sk->s_fd); + free(sk); +} + +/* Sets socket buffer size of netlink socket */ +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) +{ + if (setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, \ + &rxbuf, (socklen_t) sizeof(rxbuf))) + goto error; + + if (setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, \ + &txbuf, (socklen_t) sizeof(txbuf))) + goto error; + + return 0; +error: + return -errno; + +} + +int nl_socket_get_fd(struct nl_sock *sk) +{ + return sk->s_fd; +} diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index 0cc85d9dcc3..ed2ab5e7255 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -1,17 +1,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -# -# ARMv6 specific objects -# - -ifeq ($(TARGET_ARCH),arm) -LOCAL_ASFLAGS := -march=armv6 -LOCAL_SRC_FILES := rotate90CW_4x4_16v6.S -LOCAL_MODULE := libpixelflinger_armv6 -include $(BUILD_STATIC_LIBRARY) -endif - # # C/C++ and ARMv5 objects # @@ -40,7 +29,13 @@ PIXELFLINGER_SRC_FILES:= \ buffer.cpp ifeq ($(TARGET_ARCH),arm) +ifeq ($(TARGET_ARCH_VERSION),armv7-a) +PIXELFLINGER_SRC_FILES += col32cb16blend_neon.S +PIXELFLINGER_SRC_FILES += col32cb16blend.S +else PIXELFLINGER_SRC_FILES += t32cb16blend.S +PIXELFLINGER_SRC_FILES += col32cb16blend.S +endif endif ifeq ($(TARGET_ARCH),arm) @@ -71,10 +66,6 @@ ifneq ($(BUILD_TINY_ANDROID),true) LOCAL_SHARED_LIBRARIES += libhardware_legacy LOCAL_CFLAGS += -DWITH_LIB_HARDWARE endif - -ifeq ($(TARGET_ARCH),arm) -LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6 -endif include $(BUILD_SHARED_LIBRARY) # @@ -85,9 +76,6 @@ include $(CLEAR_VARS) LOCAL_MODULE:= libpixelflinger_static LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) -ifeq ($(TARGET_ARCH),arm) -LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6 -endif include $(BUILD_STATIC_LIBRARY) diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp index ff7b0b3e0d6..4726a08ecd4 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.cpp +++ b/libpixelflinger/codeflinger/ARMAssembler.cpp @@ -334,7 +334,7 @@ void ARMAssembler::LDM(int cc, int dir, void ARMAssembler::STM(int cc, int dir, int Rn, int W, uint32_t reg_list) -{ // FA EA FD ED IB IA DB DA +{ // ED FD EA FA IB IA DB DA const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 }; const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 }; *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | @@ -424,5 +424,25 @@ void ARMAssembler::SMLAW(int cc, int y, *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm; } +#if 0 +#pragma mark - +#pragma mark Byte/half word extract and extend (ARMv6+ only)... +#endif + +void ARMAssembler::UXTB16(int cc, int Rd, int Rm, int rotate) +{ + *mPC++ = (cc<<28) | 0x6CF0070 | (Rd<<12) | ((rotate >> 3) << 10) | Rm; +} +#if 0 +#pragma mark - +#pragma mark Bit manipulation (ARMv7+ only)... +#endif + +// Bit manipulation (ARMv7+ only)... +void ARMAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) +{ + *mPC++ = (cc<<28) | 0x7E00000 | ((width-1)<<16) | (Rd<<12) | (lsb<<7) | 0x50 | Rn; +} + }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h index ef3b66af7b9..e7f038ae494 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.h +++ b/libpixelflinger/codeflinger/ARMAssembler.h @@ -123,6 +123,8 @@ class ARMAssembler : public ARMAssemblerInterface int RdHi, int RdLo, int Rs, int Rm); virtual void SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn); + virtual void UXTB16(int cc, int Rd, int Rm, int rotate); + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); private: ARMAssembler(const ARMAssembler& rhs); diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h index 465b3bd9d98..796342a06ae 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h @@ -203,6 +203,12 @@ class ARMAssemblerInterface virtual void SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn) = 0; + // byte/half word extract... + virtual void UXTB16(int cc, int Rd, int Rm, int rotate) = 0; + + // bit manipulation... + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width) = 0; + // ----------------------------------------------------------------------- // convenience... // ----------------------------------------------------------------------- diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp index 18c46186499..c57d7daa962 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp @@ -195,6 +195,13 @@ void ARMAssemblerProxy::SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn) { mTarget->SMLAW(cc, y, Rd, Rm, Rs, Rn); } +void ARMAssemblerProxy::UXTB16(int cc, int Rd, int Rm, int rotate) { + mTarget->UXTB16(cc, Rd, Rm, rotate); +} + +void ARMAssemblerProxy::UBFX(int cc, int Rd, int Rn, int lsb, int width) { + mTarget->UBFX(cc, Rd, Rn, lsb, width); +} }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h index 4bdca9cf5ca..8c7f2707f06 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h @@ -114,6 +114,9 @@ class ARMAssemblerProxy : public ARMAssemblerInterface virtual void SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn); + virtual void UXTB16(int cc, int Rd, int Rm, int rotate); + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); + private: ARMAssemblerInterface* mTarget; }; diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp index 29410c8ab62..125c3ce05c5 100644 --- a/libpixelflinger/codeflinger/CodeCache.cpp +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -39,15 +41,14 @@ namespace android { Assembly::Assembly(size_t size) : mCount(1), mSize(0) { - mBase = (uint32_t*)malloc(size); - if (mBase) { - mSize = size; - } + mBase = (uint32_t*)mspace_malloc(getMspace(), size); + mSize = size; + ensureMbaseExecutable(); } Assembly::~Assembly() { - free(mBase); + mspace_free(getMspace(), mBase); } void Assembly::incStrong(const void*) const @@ -75,11 +76,32 @@ uint32_t* Assembly::base() const ssize_t Assembly::resize(size_t newSize) { - mBase = (uint32_t*)realloc(mBase, newSize); + mBase = (uint32_t*)mspace_realloc(getMspace(), mBase, newSize); mSize = newSize; + ensureMbaseExecutable(); return size(); } +mspace Assembly::getMspace() +{ + static mspace msp = create_contiguous_mspace(2 * 1024, 1024 * 1024, /*locked=*/ false); + return msp; +} + +void Assembly::ensureMbaseExecutable() +{ + long pagesize = sysconf(_SC_PAGESIZE); + long pagemask = ~(pagesize - 1); // assumes pagesize is a power of 2 + + uint32_t* pageStart = (uint32_t*) (((uintptr_t) mBase) & pagemask); + size_t adjustedLength = (mBase - pageStart) * sizeof(uint32_t) + mSize; + + if (mBase && mprotect(pageStart, adjustedLength, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { + mspace_free(getMspace(), mBase); + mBase = NULL; + } +} + // ---------------------------------------------------------------------------- CodeCache::CodeCache(size_t size) diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h index 8ff1366822c..aaafd26cc94 100644 --- a/libpixelflinger/codeflinger/CodeCache.h +++ b/libpixelflinger/codeflinger/CodeCache.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "tinyutils/KeyedVector.h" #include "tinyutils/smartpointer.h" @@ -67,9 +68,12 @@ class Assembly typedef void weakref_type; private: + static mspace getMspace(); + void ensureMbaseExecutable(); + mutable int32_t mCount; uint32_t* mBase; - ssize_t mSize; + size_t mSize; }; // ---------------------------------------------------------------------------- diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c index 4676da0d82d..aeb80340a7a 100644 --- a/libpixelflinger/codeflinger/disassem.c +++ b/libpixelflinger/codeflinger/disassem.c @@ -80,6 +80,9 @@ * f - 1st fp operand (register) (bits 12-14) * g - 2nd fp operand (register) (bits 16-18) * h - 3rd fp operand (register/immediate) (bits 0-4) + * j - xtb rotate literal (bits 10-11) + * i - bfx lsb literal (bits 7-11) + * w - bfx width literal (bits 16-20) * b - branch address * t - thumb branch address (bits 24, 0-23) * k - breakpoint comment (bits 0-3, 8-19) @@ -122,6 +125,8 @@ static const struct arm32_insn arm32_i[] = { { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" }, { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" }, { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" }, + { 0x0fff03f0, 0x06cf0070, "uxtb16", "dmj" }, + { 0x0fe00070, 0x07e00050, "ubfx", "dmiw" }, { 0x0d700000, 0x04200000, "strt", "daW" }, { 0x0d700000, 0x04300000, "ldrt", "daW" }, { 0x0d700000, 0x04600000, "strbt", "daW" }, @@ -276,7 +281,7 @@ static char const insn_fpaconstants[][8] = { #define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f] #define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3] -#define insn_stkblktrans(x) insn_stack_block_transfers[(x >> 23) & 3] +#define insn_stkblktrans(x) insn_stack_block_transfers[(3*((x >> 20)&1))^((x >> 23)&3)] #define op2_shift(x) op_shifts[(x >> 5) & 3] #define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03] #define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1] @@ -406,6 +411,18 @@ disasm(const disasm_interface_t *di, u_int loc, int altfmt) else di->di_printf("f%d", insn & 7); break; + /* j - xtb rotate literal (bits 10-11) */ + case 'j': + di->di_printf("ror #%d", ((insn >> 10) & 3) << 3); + break; + /* i - bfx lsb literal (bits 7-11) */ + case 'i': + di->di_printf("#%d", (insn >> 7) & 31); + break; + /* w - bfx width literal (bits 16-20) */ + case 'w': + di->di_printf("#%d", 1 + ((insn >> 16) & 31)); + break; /* b - branch address */ case 'b': branch = ((insn << 2) & 0x03ffffff); diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp index 93c58257972..ed20a00117e 100644 --- a/libpixelflinger/codeflinger/load_store.cpp +++ b/libpixelflinger/codeflinger/load_store.cpp @@ -18,9 +18,12 @@ #include #include #include - #include "codeflinger/GGLAssembler.h" +#ifdef __ARM_ARCH__ +#include +#endif + namespace android { // ---------------------------------------------------------------------------- @@ -110,6 +113,20 @@ void GGLAssembler::extract(integer_t& d, int s, int h, int l, int bits) assert(maskLen<=8); assert(h); +#if __ARM_ARCH__ >= 7 + const int mask = (1<> l; + } else if (!l && isValidImmediate(mask)) { + AND(AL, 0, d.reg, s, imm(mask)); // component = packed & mask; + } else if (!l && isValidImmediate(~mask)) { + BIC(AL, 0, d.reg, s, imm(~mask)); // component = packed & mask; + } else { + UBFX(AL, d.reg, s, l, maskLen); // component = (packed & mask) >> l; + } +#else if (h != bits) { const int mask = ((1< +#endif namespace android { @@ -567,7 +570,7 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, RSB(GE, 0, height, height, imm(0)); MUL(AL, 0, height, stride, height); } else { - // u has not been CLAMPed yet + // v has not been CLAMPed yet CMP(AL, height, reg_imm(v, ASR, FRAC_BITS)); MOV(LE, 0, v, reg_imm(height, LSL, FRAC_BITS)); MOV(LE, 0, height, imm(0)); @@ -868,6 +871,106 @@ void GGLAssembler::filter24( load(txPtr, texel, 0); } +#if __ARM_ARCH__ >= 6 +// ARMv6 version, using UXTB16, and scheduled for Cortex-A8 pipeline +void GGLAssembler::filter32( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS) +{ + const int adjust = FRAC_BITS*2 - 8; + const int round = 0; + const int prescale = 16 - adjust; + + Scratch scratches(registerFile()); + + int pixel= scratches.obtain(); + int dh = scratches.obtain(); + int u = scratches.obtain(); + int k = scratches.obtain(); + + int temp = scratches.obtain(); + int dl = scratches.obtain(); + + int offsetrt = scratches.obtain(); + int offsetlb = scratches.obtain(); + + int pixellb = offsetlb; + + // RB -> U * V + CONTEXT_LOAD(offsetrt, generated_vars.rt); + CONTEXT_LOAD(offsetlb, generated_vars.lb); + if(!round) { + MOV(AL, 0, U, reg_imm(U, LSL, prescale)); + } + ADD(AL, 0, u, offsetrt, offsetlb); + + LDR(AL, pixel, txPtr.reg, reg_scale_pre(u)); + if (round) { + SMULBB(AL, u, U, V); + RSB(AL, 0, U, U, imm(1< (1-U) * V + if (round) { + SMULBB(AL, u, U, V); + } else { + SMULWB(AL, u, U, V); + } + UXTB16(AL, temp, pixellb, 0); + if (round) { + ADD(AL, 0, u, u, imm(1<<(adjust-1))); + MOV(AL, 0, u, reg_imm(u, LSR, adjust)); + } + MLA(AL, 0, dh, temp, u, dh); + UXTB16(AL, temp, pixellb, 8); + MLA(AL, 0, dl, temp, u, dl); + SUB(AL, 0, k, k, u); + + // LT -> (1-U)*(1-V) + RSB(AL, 0, V, V, imm(1< U*(1-V) + LDR(AL, pixel, txPtr.reg, reg_scale_pre(offsetrt)); + SUB(AL, 0, u, k, u); + UXTB16(AL, temp, pixel, 0); + MLA(AL, 0, dh, temp, u, dh); + UXTB16(AL, temp, pixel, 8); + MLA(AL, 0, dl, temp, u, dl); + + UXTB16(AL, dh, dh, 8); + UXTB16(AL, dl, dl, 8); + ORR(AL, 0, texel.reg, dh, reg_imm(dl, LSL, 8)); +} +#else void GGLAssembler::filter32( const fragment_parts_t& parts, pixel_t& texel, const texture_unit_t& tmu, @@ -955,6 +1058,7 @@ void GGLAssembler::filter32( AND(AL, 0, dl, dl, reg_imm(mask, LSL, 8)); ORR(AL, 0, texel.reg, dh, dl); } +#endif void GGLAssembler::build_texture_environment( component_t& fragment, diff --git a/libpixelflinger/col32cb16blend.S b/libpixelflinger/col32cb16blend.S new file mode 100644 index 00000000000..1831255d2ad --- /dev/null +++ b/libpixelflinger/col32cb16blend.S @@ -0,0 +1,77 @@ +/* libs/pixelflinger/col32cb16blend.S + * + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + .text + .align + + .global scanline_col32cb16blend_arm + +// +// This function alpha blends a fixed color into a destination scanline, using +// the formula: +// +// d = s + (((a + (a >> 7)) * d) >> 8) +// +// where d is the destination pixel, +// s is the source color, +// a is the alpha channel of the source color. +// + +// r0 = destination buffer pointer +// r1 = color value +// r2 = count + + +scanline_col32cb16blend_arm: + push {r4-r10, lr} // stack ARM regs + + mov r5, r1, lsr #24 // shift down alpha + mov r9, #0xff // create mask + add r5, r5, r5, lsr #7 // add in top bit + rsb r5, r5, #256 // invert alpha + and r10, r1, #0xff // extract red + and r12, r9, r1, lsr #8 // extract green + and r4, r9, r1, lsr #16 // extract blue + mov r10, r10, lsl #5 // prescale red + mov r12, r12, lsl #6 // prescale green + mov r4, r4, lsl #5 // prescale blue + mov r9, r9, lsr #2 // create dest green mask + +1: + ldrh r8, [r0] // load dest pixel + subs r2, r2, #1 // decrement loop counter + mov r6, r8, lsr #11 // extract dest red + and r7, r9, r8, lsr #5 // extract dest green + and r8, r8, #0x1f // extract dest blue + + smlabb r6, r6, r5, r10 // dest red * alpha + src red + smlabb r7, r7, r5, r12 // dest green * alpha + src green + smlabb r8, r8, r5, r4 // dest blue * alpha + src blue + + mov r6, r6, lsr #8 // shift down red + mov r7, r7, lsr #8 // shift down green + mov r6, r6, lsl #11 // shift red into 565 + orr r6, r7, lsl #5 // shift green into 565 + orr r6, r8, lsr #8 // shift blue into 565 + + strh r6, [r0], #2 // store pixel to dest, update ptr + bne 1b // if count != 0, loop + + pop {r4-r10, pc} // return + + + diff --git a/libpixelflinger/col32cb16blend_neon.S b/libpixelflinger/col32cb16blend_neon.S new file mode 100644 index 00000000000..cbd54d1feb7 --- /dev/null +++ b/libpixelflinger/col32cb16blend_neon.S @@ -0,0 +1,153 @@ +/* libs/pixelflinger/col32cb16blend_neon.S + * + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + .text + .align + + .global scanline_col32cb16blend_neon + +// +// This function alpha blends a fixed color into a destination scanline, using +// the formula: +// +// d = s + (((a + (a >> 7)) * d) >> 8) +// +// where d is the destination pixel, +// s is the source color, +// a is the alpha channel of the source color. +// +// The NEON implementation processes 16 pixels per iteration. The remaining 0 - 15 +// pixels are processed in ARM code. +// + +// r0 = destination buffer pointer +// r1 = color pointer +// r2 = count + + +scanline_col32cb16blend_neon: + push {r4-r11, lr} // stack ARM regs + + vmov.u16 q15, #256 // create alpha constant + movs r3, r2, lsr #4 // calc. sixteens iterations + vmov.u16 q14, #0x1f // create blue mask + + beq 2f // if r3 == 0, branch to singles + + vld4.8 {d0[], d2[], d4[], d6[]}, [r1] // load color into four registers + // split and duplicate them, such that + // d0 = 8 equal red values + // d2 = 8 equal green values + // d4 = 8 equal blue values + // d6 = 8 equal alpha values + vshll.u8 q0, d0, #5 // shift up red and widen + vshll.u8 q1, d2, #6 // shift up green and widen + vshll.u8 q2, d4, #5 // shift up blue and widen + + vshr.u8 d7, d6, #7 // extract top bit of alpha + vaddl.u8 q3, d6, d7 // add top bit into alpha + vsub.u16 q3, q15, q3 // invert alpha + +1: + // This loop processes 16 pixels per iteration. In the comments, references to + // the first eight pixels are suffixed with "0" (red0, green0, blue0), + // the second eight are suffixed "1". + // q8 = dst red0 + // q9 = dst green0 + // q10 = dst blue0 + // q13 = dst red1 + // q12 = dst green1 + // q11 = dst blue1 + + vld1.16 {d20, d21, d22, d23}, [r0] // load 16 dest pixels + vshr.u16 q8, q10, #11 // shift dst red0 to low 5 bits + pld [r0, #63] // preload next dest pixels + vshl.u16 q9, q10, #5 // shift dst green0 to top 6 bits + vand q10, q10, q14 // extract dst blue0 + vshr.u16 q9, q9, #10 // shift dst green0 to low 6 bits + vmul.u16 q8, q8, q3 // multiply dst red0 by src alpha + vshl.u16 q12, q11, #5 // shift dst green1 to top 6 bits + vmul.u16 q9, q9, q3 // multiply dst green0 by src alpha + vshr.u16 q13, q11, #11 // shift dst red1 to low 5 bits + vmul.u16 q10, q10, q3 // multiply dst blue0 by src alpha + vshr.u16 q12, q12, #10 // shift dst green1 to low 6 bits + vand q11, q11, q14 // extract dst blue1 + vadd.u16 q8, q8, q0 // add src red to dst red0 + vmul.u16 q13, q13, q3 // multiply dst red1 by src alpha + vadd.u16 q9, q9, q1 // add src green to dst green0 + vmul.u16 q12, q12, q3 // multiply dst green1 by src alpha + vadd.u16 q10, q10, q2 // add src blue to dst blue0 + vmul.u16 q11, q11, q3 // multiply dst blue1 by src alpha + vshr.u16 q8, q8, #8 // shift down red0 + vadd.u16 q13, q13, q0 // add src red to dst red1 + vshr.u16 q9, q9, #8 // shift down green0 + vadd.u16 q12, q12, q1 // add src green to dst green1 + vshr.u16 q10, q10, #8 // shift down blue0 + vadd.u16 q11, q11, q2 // add src blue to dst blue1 + vsli.u16 q10, q9, #5 // shift & insert green0 into blue0 + vshr.u16 q13, q13, #8 // shift down red1 + vsli.u16 q10, q8, #11 // shift & insert red0 into blue0 + vshr.u16 q12, q12, #8 // shift down green1 + vshr.u16 q11, q11, #8 // shift down blue1 + subs r3, r3, #1 // decrement loop counter + vsli.u16 q11, q12, #5 // shift & insert green1 into blue1 + vsli.u16 q11, q13, #11 // shift & insert red1 into blue1 + + vst1.16 {d20, d21, d22, d23}, [r0]! // write 16 pixels back to dst + bne 1b // if count != 0, loop + +2: + ands r3, r2, #15 // calc. single iterations + beq 4f // if r3 == 0, exit + + ldr r4, [r1] // load source color + mov r5, r4, lsr #24 // shift down alpha + add r5, r5, r5, lsr #7 // add in top bit + rsb r5, r5, #256 // invert alpha + and r11, r4, #0xff // extract red + ubfx r12, r4, #8, #8 // extract green + ubfx r4, r4, #16, #8 // extract blue + mov r11, r11, lsl #5 // prescale red + mov r12, r12, lsl #6 // prescale green + mov r4, r4, lsl #5 // prescale blue + +3: + ldrh r8, [r0] // load dest pixel + subs r3, r3, #1 // decrement loop counter + mov r6, r8, lsr #11 // extract dest red + ubfx r7, r8, #5, #6 // extract dest green + and r8, r8, #0x1f // extract dest blue + + smlabb r6, r6, r5, r11 // dest red * alpha + src red + smlabb r7, r7, r5, r12 // dest green * alpha + src green + smlabb r8, r8, r5, r4 // dest blue * alpha + src blue + + mov r6, r6, lsr #8 // shift down red + mov r7, r7, lsr #8 // shift down green + mov r6, r6, lsl #11 // shift red into 565 + orr r6, r7, lsl #5 // shift green into 565 + orr r6, r8, lsr #8 // shift blue into 565 + + strh r6, [r0], #2 // store pixel to dest, update ptr + bne 3b // if count != 0, loop +4: + + pop {r4-r11, pc} // return + + + diff --git a/libpixelflinger/format.cpp b/libpixelflinger/format.cpp index 161e6d6ee63..6546e8c063c 100644 --- a/libpixelflinger/format.cpp +++ b/libpixelflinger/format.cpp @@ -40,12 +40,12 @@ static GGLFormat const gPixelFormatInfos[] = { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE - { 1, 16, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_SP },// PIXEL_FORMAT_YCbCr_422_SP - { 1, 12, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_SP },// PIXEL_FORMAT_YCbCr_420_SP - { 1, 16, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_P }, // PIXEL_FORMAT_YCbCr_422_P - { 1, 12, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_P }, // PIXEL_FORMAT_YCbCr_420_P - { 1, 16, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_I }, // PIXEL_FORMAT_YCbCr_422_I - { 1, 12, {{ 0, 8, 0, 8, 0, 8, 0, 0 }}, GGL_Y_CB_CR_I }, // PIXEL_FORMAT_YCbCr_420_I + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE @@ -53,6 +53,15 @@ static GGLFormat const gPixelFormatInfos[] = { 1, 8, {{ 8, 0, 0, 0, 0, 0, 0, 0 }}, GGL_STENCIL_INDEX }, { 4, 24, {{ 0, 0, 24, 0, 0, 0, 0, 0 }}, GGL_DEPTH_COMPONENT}, { 4, 8, {{ 32,24, 0, 0, 0, 0, 0, 0 }}, GGL_STENCIL_INDEX }, + + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE + }; }; // namespace android diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp index b54da0c6139..84e584e4937 100644 --- a/libpixelflinger/pixelflinger.cpp +++ b/libpixelflinger/pixelflinger.cpp @@ -281,6 +281,7 @@ static void ggl_fogColor3xv(void* con, const GGLclampx* color) const int32_t r = gglClampx(color[0]); const int32_t g = gglClampx(color[1]); const int32_t b = gglClampx(color[2]); + c->state.fog.color[GGLFormat::ALPHA]= 0xFF; // unused c->state.fog.color[GGLFormat::RED] = (r - (r>>8))>>8; c->state.fog.color[GGLFormat::GREEN]= (g - (g>>8))>>8; c->state.fog.color[GGLFormat::BLUE] = (b - (b>>8))>>8; diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp index d751202945a..32b2a97aaf5 100644 --- a/libpixelflinger/raster.cpp +++ b/libpixelflinger/raster.cpp @@ -143,7 +143,7 @@ void ggl_copyPixels(void* con, GGLint xs, GGLint ys, using namespace android; -GGLint gglBitBlti(GGLContext* con, int tmu, GGLint crop[4], GGLint where[4]) +GGLint gglBitBlit(GGLContext* con, int tmu, GGLint crop[4], GGLint where[4]) { GGL_CONTEXT(c, (void*)con); diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index f7003068069..a37b47e183a 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -1,6 +1,6 @@ /* libs/pixelflinger/scanline.cpp ** -** Copyright 2006, The Android Open Source Project +** Copyright 2006-2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -57,6 +57,11 @@ #define DEBUG__CODEGEN_ONLY 0 +/* Set to 1 to dump to the log the states that need a new + * code-generated scanline callback, i.e. those that don't + * have a corresponding shortcut function. + */ +#define DEBUG_NEEDS 0 #define ASSEMBLY_SCRATCH_SIZE 2048 @@ -79,7 +84,21 @@ static void scanline(context_t* c); static void scanline_perspective(context_t* c); static void scanline_perspective_single(context_t* c); static void scanline_t32cb16blend(context_t* c); +static void scanline_t32cb16blend_dither(context_t* c); +static void scanline_t32cb16blend_srca(context_t* c); +static void scanline_t32cb16blend_clamp(context_t* c); +static void scanline_t32cb16blend_clamp_dither(context_t* c); +static void scanline_t32cb16blend_clamp_mod(context_t* c); +static void scanline_x32cb16blend_clamp_mod(context_t* c); +static void scanline_t32cb16blend_clamp_mod_dither(context_t* c); +static void scanline_x32cb16blend_clamp_mod_dither(context_t* c); static void scanline_t32cb16(context_t* c); +static void scanline_t32cb16_dither(context_t* c); +static void scanline_t32cb16_clamp(context_t* c); +static void scanline_t32cb16_clamp_dither(context_t* c); +static void scanline_col32cb16blend(context_t* c); +static void scanline_t16cb16_clamp(context_t* c); +static void scanline_t16cb16blend_clamp_mod(context_t* c); static void scanline_memcpy(context_t* c); static void scanline_memset8(context_t* c); static void scanline_memset16(context_t* c); @@ -93,9 +112,18 @@ static void rect_memcpy(context_t* c, size_t yc); extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t); extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct); +extern "C" void scanline_col32cb16blend_neon(uint16_t *dst, uint32_t *col, size_t ct); +extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t ct); // ---------------------------------------------------------------------------- +static inline uint16_t convertAbgr8888ToRgb565(uint32_t pix) +{ + return uint16_t( ((pix << 8) & 0xf800) | + ((pix >> 5) & 0x07e0) | + ((pix >> 19) & 0x001f) ); +} + struct shortcut_t { needs_filter_t filter; const char* desc; @@ -104,13 +132,98 @@ struct shortcut_t { }; // Keep in sync with needs + +/* To understand the values here, have a look at: + * system/core/include/private/pixelflinger/ggl_context.h + * + * Especially the lines defining and using GGL_RESERVE_NEEDS + * + * Quick reminders: + * - the last nibble of the first value is the destination buffer format. + * - the last nibble of the third value is the source texture format + * - formats: 4=rgb565 1=abgr8888 2=xbgr8888 + * + * In the descriptions below: + * + * SRC means we copy the source pixels to the destination + * + * SRC_OVER means we blend the source pixels to the destination + * with dstFactor = 1-srcA, srcFactor=1 (premultiplied source). + * This mode is otherwise called 'blend'. + * + * SRCA_OVER means we blend the source pixels to the destination + * with dstFactor=srcA*(1-srcA) srcFactor=srcA (non-premul source). + * This mode is otherwise called 'blend_srca' + * + * clamp means we fetch source pixels from a texture with u/v clamping + * + * mod means the source pixels are modulated (multiplied) by the + * a/r/g/b of the current context's color. Typically used for + * fade-in / fade-out. + * + * dither means we dither 32 bit values to 16 bits + */ static shortcut_t shortcuts[] = { { { { 0x03515104, 0x00000077, { 0x00000A01, 0x00000000 } }, { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, - "565 fb, 8888 tx, blend", scanline_t32cb16blend, init_y_noop }, + "565 fb, 8888 tx, blend SRC_OVER", scanline_t32cb16blend, init_y_noop }, { { { 0x03010104, 0x00000077, { 0x00000A01, 0x00000000 } }, { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, - "565 fb, 8888 tx", scanline_t32cb16, init_y_noop }, + "565 fb, 8888 tx, SRC", scanline_t32cb16, init_y_noop }, + /* same as first entry, but with dithering */ + { { { 0x03515104, 0x00000177, { 0x00000A01, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, blend SRC_OVER dither", scanline_t32cb16blend_dither, init_y_noop }, + /* same as second entry, but with dithering */ + { { { 0x03010104, 0x00000177, { 0x00000A01, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC dither", scanline_t32cb16_dither, init_y_noop }, + /* this is used during the boot animation - CHEAT: ignore dithering */ + { { { 0x03545404, 0x00000077, { 0x00000A01, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFEFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, blend dst:ONE_MINUS_SRCA src:SRCA", scanline_t32cb16blend_srca, init_y_noop }, + /* special case for arbitrary texture coordinates (think scaling) */ + { { { 0x03515104, 0x00000077, { 0x00000001, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC_OVER clamp", scanline_t32cb16blend_clamp, init_y }, + { { { 0x03515104, 0x00000177, { 0x00000001, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC_OVER clamp dither", scanline_t32cb16blend_clamp_dither, init_y }, + /* another case used during emulation */ + { { { 0x03515104, 0x00000077, { 0x00001001, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC_OVER clamp modulate", scanline_t32cb16blend_clamp_mod, init_y }, + /* and this */ + { { { 0x03515104, 0x00000077, { 0x00001002, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, x888 tx, SRC_OVER clamp modulate", scanline_x32cb16blend_clamp_mod, init_y }, + { { { 0x03515104, 0x00000177, { 0x00001001, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC_OVER clamp modulate dither", scanline_t32cb16blend_clamp_mod_dither, init_y }, + { { { 0x03515104, 0x00000177, { 0x00001002, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, x888 tx, SRC_OVER clamp modulate dither", scanline_x32cb16blend_clamp_mod_dither, init_y }, + { { { 0x03010104, 0x00000077, { 0x00000001, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC clamp", scanline_t32cb16_clamp, init_y }, + { { { 0x03010104, 0x00000077, { 0x00000002, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, x888 tx, SRC clamp", scanline_t32cb16_clamp, init_y }, + { { { 0x03010104, 0x00000177, { 0x00000001, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 8888 tx, SRC clamp dither", scanline_t32cb16_clamp_dither, init_y }, + { { { 0x03010104, 0x00000177, { 0x00000002, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, x888 tx, SRC clamp dither", scanline_t32cb16_clamp_dither, init_y }, + { { { 0x03010104, 0x00000077, { 0x00000004, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 565 tx, SRC clamp", scanline_t16cb16_clamp, init_y }, + { { { 0x03515104, 0x00000077, { 0x00001004, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } }, + "565 fb, 565 tx, SRC_OVER clamp", scanline_t16cb16blend_clamp_mod, init_y }, + { { { 0x03515104, 0x00000077, { 0x00000000, 0x00000000 } }, + { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0xFFFFFFFF } } }, + "565 fb, 8888 fixed color", scanline_col32cb16blend, init_y_packed }, { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } }, { 0x00000000, 0x00000007, { 0x00000000, 0x00000000 } } }, "(nop) alpha test", scanline_noop, init_y_noop }, @@ -237,6 +350,12 @@ static void pick_scanline(context_t* c) } } +#if DEBUG_NEEDS + LOGI("Needs: n=0x%08x p=0x%08x t0=0x%08x t1=0x%08x", + c->state.needs.n, c->state.needs.p, + c->state.needs.t[0], c->state.needs.t[1]); +#endif + #endif // DEBUG__CODEGEN_ONLY c->init_y = init_y; @@ -791,6 +910,678 @@ void scanline(context_t* c) #pragma mark Scanline #endif +/* Used to parse a 32-bit source texture linearly. Usage is: + * + * horz_iterator32 hi(context); + * while (...) { + * uint32_t src_pixel = hi.get_pixel32(); + * ... + * } + * + * Use only for one-to-one texture mapping. + */ +struct horz_iterator32 { + horz_iterator32(context_t* c) { + const int x = c->iterators.xl; + const int y = c->iterators.y; + texture_t& tx = c->state.texture[0]; + const int32_t u = (tx.shade.is0>>16) + x; + const int32_t v = (tx.shade.it0>>16) + y; + m_src = reinterpret_cast(tx.surface.data)+(u+(tx.surface.stride*v)); + } + uint32_t get_pixel32() { + return *m_src++; + } +protected: + uint32_t* m_src; +}; + +/* A variant for 16-bit source textures. */ +struct horz_iterator16 { + horz_iterator16(context_t* c) { + const int x = c->iterators.xl; + const int y = c->iterators.y; + texture_t& tx = c->state.texture[0]; + const int32_t u = (tx.shade.is0>>16) + x; + const int32_t v = (tx.shade.it0>>16) + y; + m_src = reinterpret_cast(tx.surface.data)+(u+(tx.surface.stride*v)); + } + uint16_t get_pixel16() { + return *m_src++; + } +protected: + uint16_t* m_src; +}; + +/* A clamp iterator is used to iterate inside a texture with GGL_CLAMP. + * After initialization, call get_src16() or get_src32() to get the current + * texture pixel value. + */ +struct clamp_iterator { + clamp_iterator(context_t* c) { + const int xs = c->iterators.xl; + texture_t& tx = c->state.texture[0]; + texture_iterators_t& ti = tx.iterators; + m_s = (xs * ti.dsdx) + ti.ydsdy; + m_t = (xs * ti.dtdx) + ti.ydtdy; + m_ds = ti.dsdx; + m_dt = ti.dtdx; + m_width_m1 = tx.surface.width - 1; + m_height_m1 = tx.surface.height - 1; + m_data = tx.surface.data; + m_stride = tx.surface.stride; + } + uint16_t get_pixel16() { + int u, v; + get_uv(u, v); + uint16_t* src = reinterpret_cast(m_data) + (u + (m_stride*v)); + return src[0]; + } + uint32_t get_pixel32() { + int u, v; + get_uv(u, v); + uint32_t* src = reinterpret_cast(m_data) + (u + (m_stride*v)); + return src[0]; + } +private: + void get_uv(int& u, int& v) { + int uu = m_s >> 16; + int vv = m_t >> 16; + if (uu < 0) + uu = 0; + if (uu > m_width_m1) + uu = m_width_m1; + if (vv < 0) + vv = 0; + if (vv > m_height_m1) + vv = m_height_m1; + u = uu; + v = vv; + m_s += m_ds; + m_t += m_dt; + } + + GGLfixed m_s, m_t; + GGLfixed m_ds, m_dt; + int m_width_m1, m_height_m1; + uint8_t* m_data; + int m_stride; +}; + +/* + * The 'horizontal clamp iterator' variant corresponds to the case where + * the 'v' coordinate doesn't change. This is useful to avoid one mult and + * extra adds / checks per pixels, if the blending/processing operation after + * this is very fast. + */ +static int is_context_horizontal(const context_t* c) { + return (c->state.texture[0].iterators.dtdx == 0); +} + +struct horz_clamp_iterator { + uint16_t get_pixel16() { + int u = m_s >> 16; + m_s += m_ds; + if (u < 0) + u = 0; + if (u > m_width_m1) + u = m_width_m1; + const uint16_t* src = reinterpret_cast(m_data); + return src[u]; + } + uint32_t get_pixel32() { + int u = m_s >> 16; + m_s += m_ds; + if (u < 0) + u = 0; + if (u > m_width_m1) + u = m_width_m1; + const uint32_t* src = reinterpret_cast(m_data); + return src[u]; + } +protected: + void init(const context_t* c, int shift); + GGLfixed m_s; + GGLfixed m_ds; + int m_width_m1; + const uint8_t* m_data; +}; + +void horz_clamp_iterator::init(const context_t* c, int shift) +{ + const int xs = c->iterators.xl; + const texture_t& tx = c->state.texture[0]; + const texture_iterators_t& ti = tx.iterators; + m_s = (xs * ti.dsdx) + ti.ydsdy; + m_ds = ti.dsdx; + m_width_m1 = tx.surface.width-1; + m_data = tx.surface.data; + + GGLfixed t = (xs * ti.dtdx) + ti.ydtdy; + int v = t >> 16; + if (v < 0) + v = 0; + else if (v >= (int)tx.surface.height) + v = (int)tx.surface.height-1; + + m_data += (tx.surface.stride*v) << shift; +} + +struct horz_clamp_iterator16 : horz_clamp_iterator { + horz_clamp_iterator16(const context_t* c) { + init(c,1); + }; +}; + +struct horz_clamp_iterator32 : horz_clamp_iterator { + horz_clamp_iterator32(context_t* c) { + init(c,2); + }; +}; + +/* This is used to perform dithering operations. + */ +struct ditherer { + ditherer(const context_t* c) { + const int x = c->iterators.xl; + const int y = c->iterators.y; + m_line = &c->ditherMatrix[ ((y & GGL_DITHER_MASK)<> 8) & 0xff; + uint32_t b = (s >> 16) & 0xff; + return rgb888ToRgb565(r,g,b); + } + /* The following assumes that r/g/b are in the 0..255 range each */ + uint16_t rgb888ToRgb565(uint32_t& r, uint32_t& g, uint32_t &b) { + int threshold = get_value(); + /* dither in on GGL_DITHER_BITS, and each of r, g, b is on 8 bits */ + r += (threshold >> (GGL_DITHER_BITS-8 +5)); + g += (threshold >> (GGL_DITHER_BITS-8 +6)); + b += (threshold >> (GGL_DITHER_BITS-8 +5)); + if (r > 0xff) + r = 0xff; + if (g > 0xff) + g = 0xff; + if (b > 0xff) + b = 0xff; + return uint16_t(((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3)); + } +protected: + const uint8_t* m_line; + int m_index; +}; + +/* This structure is used to blend (SRC_OVER) 32-bit source pixels + * onto 16-bit destination ones. Usage is simply: + * + * blender.blend(<32-bit-src-pixel-value>,) + */ +struct blender_32to16 { + blender_32to16(context_t* c) { } + void write(uint32_t s, uint16_t* dst) { + if (s == 0) + return; + s = GGL_RGBA_TO_HOST(s); + int sA = (s>>24); + if (sA == 0xff) { + *dst = convertAbgr8888ToRgb565(s); + } else { + int f = 0x100 - (sA + (sA>>7)); + int sR = (s >> ( 3))&0x1F; + int sG = (s >> ( 8+2))&0x3F; + int sB = (s >> (16+3))&0x1F; + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR += (f*dR)>>8; + sG += (f*dG)>>8; + sB += (f*dB)>>8; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } + } + void write(uint32_t s, uint16_t* dst, ditherer& di) { + if (s == 0) { + di.step(); + return; + } + s = GGL_RGBA_TO_HOST(s); + int sA = (s>>24); + if (sA == 0xff) { + *dst = di.abgr8888ToRgb565(s); + } else { + int threshold = di.get_value() << (8 - GGL_DITHER_BITS); + int f = 0x100 - (sA + (sA>>7)); + int sR = (s >> ( 3))&0x1F; + int sG = (s >> ( 8+2))&0x3F; + int sB = (s >> (16+3))&0x1F; + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = ((sR << 8) + f*dR + threshold)>>8; + sG = ((sG << 8) + f*dG + threshold)>>8; + sB = ((sB << 8) + f*dB + threshold)>>8; + if (sR > 0x1f) sR = 0x1f; + if (sG > 0x3f) sG = 0x3f; + if (sB > 0x1f) sB = 0x1f; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } + } +}; + +/* This blender does the same for the 'blend_srca' operation. + * where dstFactor=srcA*(1-srcA) srcFactor=srcA + */ +struct blender_32to16_srcA { + blender_32to16_srcA(const context_t* c) { } + void write(uint32_t s, uint16_t* dst) { + if (!s) { + return; + } + uint16_t d = *dst; + s = GGL_RGBA_TO_HOST(s); + int sR = (s >> ( 3))&0x1F; + int sG = (s >> ( 8+2))&0x3F; + int sB = (s >> (16+3))&0x1F; + int sA = (s>>24); + int f1 = (sA + (sA>>7)); + int f2 = 0x100-f1; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = (f1*sR + f2*dR)>>8; + sG = (f1*sG + f2*dG)>>8; + sB = (f1*sB + f2*dB)>>8; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } +}; + +/* Common init code the modulating blenders */ +struct blender_modulate { + void init(const context_t* c) { + const int r = c->iterators.ydrdy >> (GGL_COLOR_BITS-8); + const int g = c->iterators.ydgdy >> (GGL_COLOR_BITS-8); + const int b = c->iterators.ydbdy >> (GGL_COLOR_BITS-8); + const int a = c->iterators.ydady >> (GGL_COLOR_BITS-8); + m_r = r + (r >> 7); + m_g = g + (g >> 7); + m_b = b + (b >> 7); + m_a = a + (a >> 7); + } +protected: + int m_r, m_g, m_b, m_a; +}; + +/* This blender does a normal blend after modulation. + */ +struct blender_32to16_modulate : blender_modulate { + blender_32to16_modulate(const context_t* c) { + init(c); + } + void write(uint32_t s, uint16_t* dst) { + // blend source and destination + if (!s) { + return; + } + s = GGL_RGBA_TO_HOST(s); + + /* We need to modulate s */ + uint32_t sA = (s >> 24); + uint32_t sB = (s >> 16) & 0xff; + uint32_t sG = (s >> 8) & 0xff; + uint32_t sR = s & 0xff; + + sA = (sA*m_a) >> 8; + /* Keep R/G/B scaled to 5.8 or 6.8 fixed float format */ + sR = (sR*m_r) >> (8 - 5); + sG = (sG*m_g) >> (8 - 6); + sB = (sB*m_b) >> (8 - 5); + + /* Now do a normal blend */ + int f = 0x100 - (sA + (sA>>7)); + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = (sR + f*dR)>>8; + sG = (sG + f*dG)>>8; + sB = (sB + f*dB)>>8; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } + void write(uint32_t s, uint16_t* dst, ditherer& di) { + // blend source and destination + if (!s) { + di.step(); + return; + } + s = GGL_RGBA_TO_HOST(s); + + /* We need to modulate s */ + uint32_t sA = (s >> 24); + uint32_t sB = (s >> 16) & 0xff; + uint32_t sG = (s >> 8) & 0xff; + uint32_t sR = s & 0xff; + + sA = (sA*m_a) >> 8; + /* keep R/G/B scaled to 5.8 or 6.8 fixed float format */ + sR = (sR*m_r) >> (8 - 5); + sG = (sG*m_g) >> (8 - 6); + sB = (sB*m_b) >> (8 - 5); + + /* Scale threshold to 0.8 fixed float format */ + int threshold = di.get_value() << (8 - GGL_DITHER_BITS); + int f = 0x100 - (sA + (sA>>7)); + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = (sR + f*dR + threshold)>>8; + sG = (sG + f*dG + threshold)>>8; + sB = (sB + f*dB + threshold)>>8; + if (sR > 0x1f) sR = 0x1f; + if (sG > 0x3f) sG = 0x3f; + if (sB > 0x1f) sB = 0x1f; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } +}; + +/* same as 32to16_modulate, except that the input is xRGB, instead of ARGB */ +struct blender_x32to16_modulate : blender_modulate { + blender_x32to16_modulate(const context_t* c) { + init(c); + } + void write(uint32_t s, uint16_t* dst) { + s = GGL_RGBA_TO_HOST(s); + + uint32_t sB = (s >> 16) & 0xff; + uint32_t sG = (s >> 8) & 0xff; + uint32_t sR = s & 0xff; + + /* Keep R/G/B in 5.8 or 6.8 format */ + sR = (sR*m_r) >> (8 - 5); + sG = (sG*m_g) >> (8 - 6); + sB = (sB*m_b) >> (8 - 5); + + int f = 0x100 - m_a; + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = (sR + f*dR)>>8; + sG = (sG + f*dG)>>8; + sB = (sB + f*dB)>>8; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } + void write(uint32_t s, uint16_t* dst, ditherer& di) { + s = GGL_RGBA_TO_HOST(s); + + uint32_t sB = (s >> 16) & 0xff; + uint32_t sG = (s >> 8) & 0xff; + uint32_t sR = s & 0xff; + + sR = (sR*m_r) >> (8 - 5); + sG = (sG*m_g) >> (8 - 6); + sB = (sB*m_b) >> (8 - 5); + + /* Now do a normal blend */ + int threshold = di.get_value() << (8 - GGL_DITHER_BITS); + int f = 0x100 - m_a; + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = (sR + f*dR + threshold)>>8; + sG = (sG + f*dG + threshold)>>8; + sB = (sB + f*dB + threshold)>>8; + if (sR > 0x1f) sR = 0x1f; + if (sG > 0x3f) sG = 0x3f; + if (sB > 0x1f) sB = 0x1f; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } +}; + +/* Same as above, but source is 16bit rgb565 */ +struct blender_16to16_modulate : blender_modulate { + blender_16to16_modulate(const context_t* c) { + init(c); + } + void write(uint16_t s16, uint16_t* dst) { + uint32_t s = s16; + + uint32_t sR = s >> 11; + uint32_t sG = (s >> 5) & 0x3f; + uint32_t sB = s & 0x1f; + + sR = (sR*m_r); + sG = (sG*m_g); + sB = (sB*m_b); + + int f = 0x100 - m_a; + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + sR = (sR + f*dR)>>8; + sG = (sG + f*dG)>>8; + sB = (sB + f*dB)>>8; + *dst = uint16_t((sR<<11)|(sG<<5)|sB); + } +}; + +/* This is used to iterate over a 16-bit destination color buffer. + * Usage is: + * + * dst_iterator16 di(context); + * while (di.count--) { + * + * di.dst++; + * } + */ +struct dst_iterator16 { + dst_iterator16(const context_t* c) { + const int x = c->iterators.xl; + const int width = c->iterators.xr - x; + const int32_t y = c->iterators.y; + const surface_t* cb = &(c->state.buffers.color); + count = width; + dst = reinterpret_cast(cb->data) + (x+(cb->stride*y)); + } + int count; + uint16_t* dst; +}; + + +static void scanline_t32cb16_clamp(context_t* c) +{ + dst_iterator16 di(c); + + if (is_context_horizontal(c)) { + /* Special case for simple horizontal scaling */ + horz_clamp_iterator32 ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + *di.dst++ = convertAbgr8888ToRgb565(s); + } + } else { + /* General case */ + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + *di.dst++ = convertAbgr8888ToRgb565(s); + } + } +} + +static void scanline_t32cb16_dither(context_t* c) +{ + horz_iterator32 si(c); + dst_iterator16 di(c); + ditherer dither(c); + + while (di.count--) { + uint32_t s = si.get_pixel32(); + *di.dst++ = dither.abgr8888ToRgb565(s); + } +} + +static void scanline_t32cb16_clamp_dither(context_t* c) +{ + dst_iterator16 di(c); + ditherer dither(c); + + if (is_context_horizontal(c)) { + /* Special case for simple horizontal scaling */ + horz_clamp_iterator32 ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + *di.dst++ = dither.abgr8888ToRgb565(s); + } + } else { + /* General case */ + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + *di.dst++ = dither.abgr8888ToRgb565(s); + } + } +} + +static void scanline_t32cb16blend_dither(context_t* c) +{ + dst_iterator16 di(c); + ditherer dither(c); + blender_32to16 bl(c); + horz_iterator32 hi(c); + while (di.count--) { + uint32_t s = hi.get_pixel32(); + bl.write(s, di.dst, dither); + di.dst++; + } +} + +static void scanline_t32cb16blend_clamp(context_t* c) +{ + dst_iterator16 di(c); + blender_32to16 bl(c); + + if (is_context_horizontal(c)) { + horz_clamp_iterator32 ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst); + di.dst++; + } + } else { + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst); + di.dst++; + } + } +} + +static void scanline_t32cb16blend_clamp_dither(context_t* c) +{ + dst_iterator16 di(c); + ditherer dither(c); + blender_32to16 bl(c); + + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst, dither); + di.dst++; + } +} + +void scanline_t32cb16blend_clamp_mod(context_t* c) +{ + dst_iterator16 di(c); + blender_32to16_modulate bl(c); + + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst); + di.dst++; + } +} + +void scanline_t32cb16blend_clamp_mod_dither(context_t* c) +{ + dst_iterator16 di(c); + blender_32to16_modulate bl(c); + ditherer dither(c); + + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst, dither); + di.dst++; + } +} + +/* Variant of scanline_t32cb16blend_clamp_mod with a xRGB texture */ +void scanline_x32cb16blend_clamp_mod(context_t* c) +{ + dst_iterator16 di(c); + blender_x32to16_modulate bl(c); + + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst); + di.dst++; + } +} + +void scanline_x32cb16blend_clamp_mod_dither(context_t* c) +{ + dst_iterator16 di(c); + blender_x32to16_modulate bl(c); + ditherer dither(c); + + clamp_iterator ci(c); + while (di.count--) { + uint32_t s = ci.get_pixel32(); + bl.write(s, di.dst, dither); + di.dst++; + } +} + +void scanline_t16cb16_clamp(context_t* c) +{ + dst_iterator16 di(c); + + /* Special case for simple horizontal scaling */ + if (is_context_horizontal(c)) { + horz_clamp_iterator16 ci(c); + while (di.count--) { + *di.dst++ = ci.get_pixel16(); + } + } else { + clamp_iterator ci(c); + while (di.count--) { + *di.dst++ = ci.get_pixel16(); + } + } +} + + + template static inline __attribute__((const)) T interpolate(int y, T v0, U dvdx, U dvdy) { @@ -943,6 +1734,8 @@ void init_y_packed(context_t* c, int32_t y0) uint8_t f = c->state.buffers.color.format; c->packed = ggl_pack_color(c, f, c->shade.r0, c->shade.g0, c->shade.b0, c->shade.a0); + c->packed8888 = ggl_pack_color(c, GGL_PIXEL_FORMAT_RGBA_8888, + c->shade.r0, c->shade.g0, c->shade.b0, c->shade.a0); c->iterators.y = y0; c->step_y = step_y__nop; // choose the rectangle blitter @@ -1253,6 +2046,45 @@ void scanline_perspective_single(context_t* c) // ---------------------------------------------------------------------------- +void scanline_col32cb16blend(context_t* c) +{ + int32_t x = c->iterators.xl; + size_t ct = c->iterators.xr - x; + int32_t y = c->iterators.y; + surface_t* cb = &(c->state.buffers.color); + union { + uint16_t* dst; + uint32_t* dst32; + }; + dst = reinterpret_cast(cb->data) + (x+(cb->stride*y)); + +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__)) +#if defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN + scanline_col32cb16blend_neon(dst, &(c->packed8888), ct); +#else // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN + scanline_col32cb16blend_arm(dst, GGL_RGBA_TO_HOST(c->packed8888), ct); +#endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN +#else + uint32_t s = GGL_RGBA_TO_HOST(c->packed8888); + int sA = (s>>24); + int f = 0x100 - (sA + (sA>>7)); + while (ct--) { + uint16_t d = *dst; + int dR = (d>>11)&0x1f; + int dG = (d>>5)&0x3f; + int dB = (d)&0x1f; + int sR = (s >> ( 3))&0x1F; + int sG = (s >> ( 8+2))&0x3F; + int sB = (s >> (16+3))&0x1F; + sR += (f*dR)>>8; + sG += (f*dG)>>8; + sB += (f*dB)>>8; + *dst++ = uint16_t((sR<<11)|(sG<<5)|sB); + } +#endif + +} + void scanline_t32cb16(context_t* c) { int32_t x = c->iterators.xl; @@ -1275,30 +2107,24 @@ void scanline_t32cb16(context_t* c) if (ct==1 || uint32_t(dst)&2) { last_one: s = GGL_RGBA_TO_HOST( *src++ ); - sR = (s >> ( 3))&0x1F; - sG = (s >> ( 8+2))&0x3F; - sB = (s >> (16+3))&0x1F; - *dst++ = uint16_t((sR<<11)|(sG<<5)|sB); + *dst++ = convertAbgr8888ToRgb565(s); ct--; } while (ct >= 2) { +#if BYTE_ORDER == BIG_ENDIAN s = GGL_RGBA_TO_HOST( *src++ ); - sR = (s >> ( 3))&0x1F; - sG = (s >> ( 8+2))&0x3F; - sB = (s >> (16+3))&0x1F; - d = (sR<<11)|(sG<<5)|sB; - + d = convertAbgr8888ToRgb565_hi16(s); + s = GGL_RGBA_TO_HOST( *src++ ); - sR = (s >> ( 3))&0x1F; - sG = (s >> ( 8+2))&0x3F; - sB = (s >> (16+3))&0x1F; - d |= ((sR<<11)|(sG<<5)|sB)<<16; + d |= convertAbgr8888ToRgb565(s); +#else + s = GGL_RGBA_TO_HOST( *src++ ); + d = convertAbgr8888ToRgb565(s); -#if BYTE_ORDER == BIG_ENDIAN - d = (d>>16) | (d<<16); + s = GGL_RGBA_TO_HOST( *src++ ); + d |= convertAbgr8888ToRgb565(s) << 16; #endif - *dst32++ = d; ct -= 2; } @@ -1310,6 +2136,7 @@ void scanline_t32cb16(context_t* c) void scanline_t32cb16blend(context_t* c) { +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__)) int32_t x = c->iterators.xl; size_t ct = c->iterators.xr - x; int32_t y = c->iterators.y; @@ -1321,33 +2148,55 @@ void scanline_t32cb16blend(context_t* c) const int32_t v = (c->state.texture[0].shade.it0>>16) + y; uint32_t *src = reinterpret_cast(tex->data)+(u+(tex->stride*v)); -#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__)) scanline_t32cb16blend_arm(dst, src, ct); #else - while (ct--) { - uint32_t s = *src++; - if (!s) { - dst++; - continue; - } - uint16_t d = *dst; - s = GGL_RGBA_TO_HOST(s); - int sR = (s >> ( 3))&0x1F; - int sG = (s >> ( 8+2))&0x3F; - int sB = (s >> (16+3))&0x1F; - int sA = (s>>24); - int f = 0x100 - (sA + (sA>>7)); - int dR = (d>>11)&0x1f; - int dG = (d>>5)&0x3f; - int dB = (d)&0x1f; - sR += (f*dR)>>8; - sG += (f*dG)>>8; - sB += (f*dB)>>8; - *dst++ = uint16_t((sR<<11)|(sG<<5)|sB); + dst_iterator16 di(c); + horz_iterator32 hi(c); + blender_32to16 bl(c); + while (di.count--) { + uint32_t s = hi.get_pixel32(); + bl.write(s, di.dst); + di.dst++; } #endif } +void scanline_t32cb16blend_srca(context_t* c) +{ + dst_iterator16 di(c); + horz_iterator32 hi(c); + blender_32to16_srcA blender(c); + + while (di.count--) { + uint32_t s = hi.get_pixel32(); + blender.write(s,di.dst); + di.dst++; + } +} + +void scanline_t16cb16blend_clamp_mod(context_t* c) +{ + const int a = c->iterators.ydady >> (GGL_COLOR_BITS-8); + if (a == 0) { + return; + } + + if (a == 255) { + scanline_t16cb16_clamp(c); + return; + } + + dst_iterator16 di(c); + blender_16to16_modulate blender(c); + clamp_iterator ci(c); + + while (di.count--) { + uint16_t s = ci.get_pixel16(); + blender.write(s, di.dst); + di.dst++; + } +} + void scanline_memcpy(context_t* c) { int32_t x = c->iterators.xl; @@ -1471,26 +2320,3 @@ void rect_memcpy(context_t* c, size_t yc) // ---------------------------------------------------------------------------- }; // namespace android -using namespace android; -extern "C" void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) -{ -#if ANDROID_ARM_CODEGEN - GGLContext* c; - gglInit(&c); - needs_t needs; - needs.n = n; - needs.p = p; - needs.t[0] = t0; - needs.t[1] = t1; - sp a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); - GGLAssembler assembler( new ARMAssembler(a) ); - int err = assembler.scanline(needs, (context_t*)c); - if (err != 0) { - printf("error %08x (%s)\n", err, strerror(-err)); - } - gglUninit(c); -#else - printf("This test runs only on ARM\n"); -#endif -} - diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk index 1bc42148743..aa320fc0a1b 100644 --- a/libpixelflinger/tests/codegen/Android.mk +++ b/libpixelflinger/tests/codegen/Android.mk @@ -2,12 +2,15 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - codegen.cpp + codegen.cpp.arm LOCAL_SHARED_LIBRARIES := \ libcutils \ libpixelflinger +LOCAL_C_INCLUDES := \ + system/core/libpixelflinger + LOCAL_MODULE:= test-opengl-codegen LOCAL_MODULE_TAGS := tests diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index 18658881bbf..94e24810d65 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -1,9 +1,54 @@ #include #include -extern "C" void ggl_test_codegen( - uint32_t n, uint32_t p, uint32_t t0, uint32_t t1); +#include "private/pixelflinger/ggl_context.h" +#include "buffer.h" +#include "scanline.h" + +#include "codeflinger/CodeCache.h" +#include "codeflinger/GGLAssembler.h" +#include "codeflinger/ARMAssembler.h" + +#if defined(__arm__) +# define ANDROID_ARM_CODEGEN 1 +#else +# define ANDROID_ARM_CODEGEN 0 +#endif + +#define ASSEMBLY_SCRATCH_SIZE 2048 + +using namespace android; + +class ScanlineAssembly : public Assembly { + AssemblyKey mKey; +public: + ScanlineAssembly(needs_t needs, size_t size) + : Assembly(size), mKey(needs) { } + const AssemblyKey& key() const { return mKey; } +}; + +static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) +{ +#if ANDROID_ARM_CODEGEN + GGLContext* c; + gglInit(&c); + needs_t needs; + needs.n = n; + needs.p = p; + needs.t[0] = t0; + needs.t[1] = t1; + sp a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); + GGLAssembler assembler( new ARMAssembler(a) ); + int err = assembler.scanline(needs, (context_t*)c); + if (err != 0) { + printf("error %08x (%s)\n", err, strerror(-err)); + } + gglUninit(c); +#else + printf("This test runs only on ARM\n"); +#endif +} int main(int argc, char** argv) { diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index dd2b32d8deb..cccf484ce95 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -1,9 +1,4 @@ -BUILD_LIBSYSUTILS := false -ifneq ($(TARGET_SIMULATOR),true) - BUILD_LIBSYSUTILS := true -endif - -ifeq ($(BUILD_LIBSYSUTILS),true) +ifneq ($(BUILD_TINY_ANDROID),true) LOCAL_PATH:= $(call my-dir) @@ -26,10 +21,6 @@ LOCAL_CFLAGS := LOCAL_SHARED_LIBRARIES := libcutils -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) endif diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp index 16869961dcc..2f3705535de 100644 --- a/libsysutils/src/FrameworkClient.cpp +++ b/libsysutils/src/FrameworkClient.cpp @@ -14,27 +14,29 @@ FrameworkClient::FrameworkClient(int socket) { } int FrameworkClient::sendMsg(const char *msg) { + int ret; if (mSocket < 0) { errno = EHOSTUNREACH; return -1; } pthread_mutex_lock(&mWriteMutex); - if (write(mSocket, msg, strlen(msg) +1) < 0) { - LOGW("Unable to send msg '%s' (%s)", msg, strerror(errno)); + ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1)); + if (ret < 0) { + SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno)); } pthread_mutex_unlock(&mWriteMutex); return 0; } int FrameworkClient::sendMsg(const char *msg, const char *data) { - char *buffer = (char *) alloca(strlen(msg) + strlen(data) + 1); + size_t bufflen = strlen(msg) + strlen(data) + 1; + char *buffer = (char *) alloca(bufflen); if (!buffer) { errno = -ENOMEM; return -1; } - strcpy(buffer, msg); - strcat(buffer, data); + snprintf(buffer, bufflen, "%s%s", msg, data); return sendMsg(buffer); } diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp index c52eac76f3a..038d87edfa3 100644 --- a/libsysutils/src/FrameworkCommand.cpp +++ b/libsysutils/src/FrameworkCommand.cpp @@ -26,7 +26,7 @@ FrameworkCommand::FrameworkCommand(const char *cmd) { } int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) { - LOGW("Command %s has no run handler!", getCommand()); + SLOGW("Command %s has no run handler!", getCommand()); errno = ENOSYS; return -1; } diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 4c747650100..3416ceb124c 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -15,6 +15,7 @@ */ #include #include +#include #define LOG_TAG "FrameworkListener" @@ -33,9 +34,10 @@ bool FrameworkListener::onDataAvailable(SocketClient *c) { char buffer[255]; int len; - if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) { - LOGE("read() failed (%s)", strerror(errno)); - return errno; + len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer))); + if (len < 0) { + SLOGE("read() failed (%s)", strerror(errno)); + return false; } else if (!len) return false; @@ -44,6 +46,7 @@ bool FrameworkListener::onDataAvailable(SocketClient *c) { for (i = 0; i < len; i++) { if (buffer[i] == '\0') { + /* IMPORTANT: dispatchCommand() expects a zero-terminated string */ dispatchCommand(c, buffer + offset); offset = i + 1; } @@ -62,6 +65,7 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { char tmp[255]; char *p = data; char *q = tmp; + char *qlimit = tmp + sizeof(tmp) - 1; bool esc = false; bool quote = false; int k; @@ -71,6 +75,8 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { while(*p) { if (*p == '\\') { if (esc) { + if (q >= qlimit) + goto overflow; *q++ = '\\'; esc = false; } else @@ -78,11 +84,15 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { p++; continue; } else if (esc) { - if (*p == '"') + if (*p == '"') { + if (q >= qlimit) + goto overflow; *q++ = '"'; - else if (*p == '\\') + } else if (*p == '\\') { + if (q >= qlimit) + goto overflow; *q++ = '\\'; - else { + } else { cli->sendMsg(500, "Unsupported escape sequence", false); goto out; } @@ -100,9 +110,13 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { continue; } + if (q >= qlimit) + goto overflow; *q = *p++; if (!quote && *q == ' ') { *q = '\0'; + if (argc >= CMD_ARGS_MAX) + goto overflow; argv[argc++] = strdup(tmp); memset(tmp, 0, sizeof(tmp)); q = tmp; @@ -111,10 +125,13 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { q++; } + *q = '\0'; + if (argc >= CMD_ARGS_MAX) + goto overflow; argv[argc++] = strdup(tmp); #if 0 for (k = 0; k < argc; k++) { - LOGD("arg[%d] = '%s'", k, argv[k]); + SLOGD("arg[%d] = '%s'", k, argv[k]); } #endif @@ -122,13 +139,13 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { cli->sendMsg(500, "Unclosed quotes error", false); goto out; } - + for (i = mCommands->begin(); i != mCommands->end(); ++i) { FrameworkCommand *c = *i; if (!strcmp(argv[0], c->getCommand())) { if (c->runCommand(cli, argc, argv)) { - LOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno)); + SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno)); } goto out; } @@ -140,4 +157,8 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { for (j = 0; j < argc; j++) free(argv[j]); return; + +overflow: + cli->sendMsg(500, "Command too long", false); + goto out; } diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 5573c3f0b4a..4beebb73d00 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -21,13 +21,29 @@ #include +#include +#include +#include +#include +#include +/* From kernel's net/netfilter/xt_quota2.c */ +const int QLOG_NL_EVENT = 112; + +#include +#include + const int NetlinkEvent::NlActionUnknown = 0; const int NetlinkEvent::NlActionAdd = 1; const int NetlinkEvent::NlActionRemove = 2; const int NetlinkEvent::NlActionChange = 3; +const int NetlinkEvent::NlActionLinkUp = 4; +const int NetlinkEvent::NlActionLinkDown = 5; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; + memset(mParams, 0, sizeof(mParams)); + mPath = NULL; + mSubsystem = NULL; } NetlinkEvent::~NetlinkEvent() { @@ -43,52 +59,170 @@ NetlinkEvent::~NetlinkEvent() { } } -bool NetlinkEvent::decode(char *buffer, int size) { - char *s = buffer; - char *end; +void NetlinkEvent::dump() { + int i; + + for (i = 0; i < NL_PARAMS_MAX; i++) { + if (!mParams[i]) + break; + SLOGD("NL param '%s'\n", mParams[i]); + } +} + +/* + * Parse an binary message from a NETLINK_ROUTE netlink socket. + */ +bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { + size_t sz = size; + const struct nlmsghdr *nh = (struct nlmsghdr *) buffer; + + while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) { + + if (nh->nlmsg_type == RTM_NEWLINK) { + int len = nh->nlmsg_len - sizeof(*nh); + struct ifinfomsg *ifi; + + if (sizeof(*ifi) > (size_t) len) { + SLOGE("Got a short RTM_NEWLINK message\n"); + continue; + } + + ifi = (ifinfomsg *)NLMSG_DATA(nh); + if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) { + continue; + } + + struct rtattr *rta = (struct rtattr *) + ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi))); + len = NLMSG_PAYLOAD(nh, sizeof(*ifi)); + + while(RTA_OK(rta, len)) { + switch(rta->rta_type) { + case IFLA_IFNAME: + char buffer[16 + IFNAMSIZ]; + snprintf(buffer, sizeof(buffer), "INTERFACE=%s", + (char *) RTA_DATA(rta)); + mParams[0] = strdup(buffer); + mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? + NlActionLinkUp : NlActionLinkDown; + mSubsystem = strdup("net"); + break; + } + + rta = RTA_NEXT(rta, len); + } + + } else if (nh->nlmsg_type == QLOG_NL_EVENT) { + char *devname; + ulog_packet_msg_t *pm; + size_t len = nh->nlmsg_len - sizeof(*nh); + if (sizeof(*pm) > len) { + SLOGE("Got a short QLOG message\n"); + continue; + } + pm = (ulog_packet_msg_t *)NLMSG_DATA(nh); + devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name; + asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); + asprintf(&mParams[1], "INTERFACE=%s", devname); + mSubsystem = strdup("qlog"); + mAction = NlActionChange; + + } else { + SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); + } + nh = NLMSG_NEXT(nh, size); + } + + return true; +} + +/* If the string between 'str' and 'end' begins with 'prefixlen' characters + * from the 'prefix' array, then return 'str + prefixlen', otherwise return + * NULL. + */ +static const char* +has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen) +{ + if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen)) + return str + prefixlen; + else + return NULL; +} + +/* Same as strlen(x) for constant string literals ONLY */ +#define CONST_STRLEN(x) (sizeof(x)-1) + +/* Convenience macro to call has_prefix with a constant string literal */ +#define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix)) + + +/* + * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT + * netlink socket. + */ +bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) { + const char *s = buffer; + const char *end; int param_idx = 0; int i; int first = 1; + if (size == 0) + return false; + + /* Ensure the buffer is zero-terminated, the code below depends on this */ + buffer[size-1] = '\0'; + end = s + size; while (s < end) { if (first) { - char *p; - for (p = s; *p != '@'; p++); - p++; - mPath = strdup(p); + const char *p; + /* buffer is 0-terminated, no need to check p < end */ + for (p = s; *p != '@'; p++) { + if (!*p) { /* no '@', should not happen */ + return false; + } + } + mPath = strdup(p+1); first = 0; } else { - if (!strncmp(s, "ACTION=", strlen("ACTION="))) { - char *a = s + strlen("ACTION="); + const char* a; + if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) { if (!strcmp(a, "add")) mAction = NlActionAdd; else if (!strcmp(a, "remove")) mAction = NlActionRemove; else if (!strcmp(a, "change")) mAction = NlActionChange; - } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM="))) - mSeq = atoi(s + strlen("SEQNUM=")); - else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM="))) - mSubsystem = strdup(s + strlen("SUBSYSTEM=")); - else + } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) { + mSeq = atoi(a); + } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) { + mSubsystem = strdup(a); + } else if (param_idx < NL_PARAMS_MAX) { mParams[param_idx++] = strdup(s); + } } - s+= strlen(s) + 1; + s += strlen(s) + 1; } return true; } -const char *NetlinkEvent::findParam(const char *paramName) { - int i; +bool NetlinkEvent::decode(char *buffer, int size, int format) { + if (format == NetlinkListener::NETLINK_FORMAT_BINARY) { + return parseBinaryNetlinkMessage(buffer, size); + } else { + return parseAsciiNetlinkMessage(buffer, size); + } +} - for (i = 0; i < NL_PARAMS_MAX; i++) { - if (!mParams[i]) - break; - if (!strncmp(mParams[i], paramName, strlen(paramName))) - return &mParams[i][strlen(paramName) + 1]; +const char *NetlinkEvent::findParam(const char *paramName) { + size_t len = strlen(paramName); + for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) { + const char *ptr = mParams[i] + len; + if (!strncmp(mParams[i], paramName, len) && *ptr == '=') + return ++ptr; } - LOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName); + SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName); return NULL; } diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp index 3ec9d9dfdb0..e67b5c6aba9 100644 --- a/libsysutils/src/NetlinkListener.cpp +++ b/libsysutils/src/NetlinkListener.cpp @@ -17,39 +17,48 @@ #include #include +#include #include #define LOG_TAG "NetlinkListener" #include +#include -#include #include +#if 1 +/* temporary version until we can get Motorola to update their + * ril.so. Their prebuilt ril.so is using this private class + * so changing the NetlinkListener() constructor breaks their ril. + */ NetlinkListener::NetlinkListener(int socket) : SocketListener(socket, false) { + mFormat = NETLINK_FORMAT_ASCII; +} +#endif + +NetlinkListener::NetlinkListener(int socket, int format) : + SocketListener(socket, false), mFormat(format) { } bool NetlinkListener::onDataAvailable(SocketClient *cli) { int socket = cli->getSocket(); - LOGD("NetlinkListener::onDataAvailable()"); + ssize_t count; - int count; - - if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) { - LOGE("recv failed (%s)", strerror(errno)); + count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer))); + if (count < 0) { + SLOGE("recvmsg failed (%s)", strerror(errno)); return false; } NetlinkEvent *evt = new NetlinkEvent(); - if (!evt->decode(mBuffer, count)) { - LOGE("Error decoding NetlinkEvent"); - goto out; + if (!evt->decode(mBuffer, count, mFormat)) { + SLOGE("Error decoding NetlinkEvent"); + } else { + onEvent(evt); } - LOGD("Ignoring '%s' netlink event", evt->getSubsystem()); - -out: delete evt; return true; } diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp index 700ac91b9c3..41ac1dd31e3 100644 --- a/libsysutils/src/ServiceManager.cpp +++ b/libsysutils/src/ServiceManager.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -9,61 +10,103 @@ ServiceManager::ServiceManager() { } +/* The service name should not exceed SERVICE_NAME_MAX to avoid + * some weird things. This is due to the fact that: + * + * - Starting a service is done by writing its name to the "ctl.start" + * system property. This triggers the init daemon to actually start + * the service for us. + * + * - Stopping the service is done by writing its name to "ctl.stop" + * in a similar way. + * + * - Reading the status of a service is done by reading the property + * named "init.svc." + * + * If strlen() > (PROPERTY_KEY_MAX-1)-9, then you can start/stop + * the service by writing to ctl.start/stop, but you won't be able to + * read its state due to the truncation of "init.svc." into a + * zero-terminated buffer of PROPERTY_KEY_MAX characters. + */ +#define SERVICE_NAME_MAX (PROPERTY_KEY_MAX-10) + +/* The maximum amount of time to wait for a service to start or stop, + * in micro-seconds (really an approximation) */ +#define SLEEP_MAX_USEC 2000000 /* 2 seconds */ + +/* The minimal sleeping interval between checking for the service's state + * when looping for SLEEP_MAX_USEC */ +#define SLEEP_MIN_USEC 200000 /* 200 msec */ + int ServiceManager::start(const char *name) { + if (strlen(name) > SERVICE_NAME_MAX) { + SLOGE("Service name '%s' is too long", name); + return 0; + } if (isRunning(name)) { - LOGW("Service '%s' is already running", name); + SLOGW("Service '%s' is already running", name); return 0; } - LOGD("Starting service '%s'", name); + SLOGD("Starting service '%s'", name); property_set("ctl.start", name); - int count = 200; - while(count--) { - sched_yield(); + int count = SLEEP_MAX_USEC; + while(count > 0) { + usleep(SLEEP_MIN_USEC); + count -= SLEEP_MIN_USEC; if (isRunning(name)) break; } - if (!count) { - LOGW("Timed out waiting for service '%s' to start", name); + if (count <= 0) { + SLOGW("Timed out waiting for service '%s' to start", name); errno = ETIMEDOUT; return -1; } - LOGD("Sucessfully started '%s'", name); + SLOGD("Sucessfully started '%s'", name); return 0; } int ServiceManager::stop(const char *name) { + if (strlen(name) > SERVICE_NAME_MAX) { + SLOGE("Service name '%s' is too long", name); + return 0; + } if (!isRunning(name)) { - LOGW("Service '%s' is already stopped", name); + SLOGW("Service '%s' is already stopped", name); return 0; } - LOGD("Stopping service '%s'", name); + SLOGD("Stopping service '%s'", name); property_set("ctl.stop", name); - int count = 200; - while(count--) { - sched_yield(); + int count = SLEEP_MAX_USEC; + while(count > 0) { + usleep(SLEEP_MIN_USEC); + count -= SLEEP_MIN_USEC; if (!isRunning(name)) break; } - if (!count) { - LOGW("Timed out waiting for service '%s' to stop", name); + if (count <= 0) { + SLOGW("Timed out waiting for service '%s' to stop", name); errno = ETIMEDOUT; return -1; } - LOGD("Sucessfully stopped '%s'", name); + SLOGD("Successfully stopped '%s'", name); return 0; } bool ServiceManager::isRunning(const char *name) { char propVal[PROPERTY_VALUE_MAX]; - char propName[255]; - - snprintf(propName, sizeof(propVal), "init.svc.%s", name); + char propName[PROPERTY_KEY_MAX]; + int ret; + ret = snprintf(propName, sizeof(propName), "init.svc.%s", name); + if (ret > (int)sizeof(propName)-1) { + SLOGD("Service name '%s' is too long", name); + return false; + } if (property_get(propName, propVal, NULL)) { if (!strcmp(propVal, "running")) diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index 857ed4d1045..69d165acc95 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -9,50 +10,125 @@ #include -SocketClient::SocketClient(int socket) { - mSocket = socket; +SocketClient::SocketClient(int socket, bool owned) + : mSocket(socket) + , mSocketOwned(owned) + , mPid(-1) + , mUid(-1) + , mGid(-1) + , mRefCount(1) +{ pthread_mutex_init(&mWriteMutex, NULL); + pthread_mutex_init(&mRefCountMutex, NULL); + + struct ucred creds; + socklen_t szCreds = sizeof(creds); + memset(&creds, 0, szCreds); + + int err = getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); + if (err == 0) { + mPid = creds.pid; + mUid = creds.uid; + mGid = creds.gid; + } +} + +SocketClient::~SocketClient() +{ + if (mSocketOwned) { + close(mSocket); + } } int SocketClient::sendMsg(int code, const char *msg, bool addErrno) { char *buf; + const char* arg; + const char* fmt; + char tmp[1]; + int len; if (addErrno) { - buf = (char *) alloca(strlen(msg) + strlen(strerror(errno)) + 8); - sprintf(buf, "%.3d %s (%s)", code, msg, strerror(errno)); + fmt = "%.3d %s (%s)"; + arg = strerror(errno); } else { - buf = (char *) alloca(strlen(msg) + strlen("XXX ")); - sprintf(buf, "%.3d %s", code, msg); + fmt = "%.3d %s"; + arg = NULL; } + /* Measure length of required buffer */ + len = snprintf(tmp, sizeof tmp, fmt, code, msg, arg); + /* Allocate in the stack, then write to it */ + buf = (char*)alloca(len+1); + snprintf(buf, len+1, fmt, code, msg, arg); + /* Send the zero-terminated message */ return sendMsg(buf); } int SocketClient::sendMsg(const char *msg) { + // Send the message including null character + if (sendData(msg, strlen(msg) + 1) != 0) { + SLOGW("Unable to send msg '%s'", msg); + return -1; + } + return 0; +} + +int SocketClient::sendData(const void* data, int len) { + int rc = 0; + const char *p = (const char*) data; + int brtw = len; + if (mSocket < 0) { errno = EHOSTUNREACH; return -1; } - // Send the message including null character - int rc = 0; - const char *p = msg; - int brtw = strlen(msg) + 1; + if (len == 0) { + return 0; + } pthread_mutex_lock(&mWriteMutex); - while(brtw) { - if ((rc = write(mSocket,p, brtw)) < 0) { - LOGW("Unable to send msg '%s' (%s)", msg, strerror(errno)); - pthread_mutex_unlock(&mWriteMutex); - return -1; - } else if (!rc) { - LOGW("0 length write :("); + while (brtw > 0) { + rc = write(mSocket, p, brtw); + if (rc > 0) { + p += rc; + brtw -= rc; + continue; + } + + if (rc < 0 && errno == EINTR) + continue; + + pthread_mutex_unlock(&mWriteMutex); + if (rc == 0) { + SLOGW("0 length write :("); errno = EIO; - pthread_mutex_unlock(&mWriteMutex); - return -1; + } else { + SLOGW("write error (%s)", strerror(errno)); } - p += rc; - brtw -= rc; + return -1; } pthread_mutex_unlock(&mWriteMutex); return 0; } + +void SocketClient::incRef() { + pthread_mutex_lock(&mRefCountMutex); + mRefCount++; + pthread_mutex_unlock(&mRefCountMutex); +} + +bool SocketClient::decRef() { + bool deleteSelf = false; + pthread_mutex_lock(&mRefCountMutex); + mRefCount--; + if (mRefCount == 0) { + deleteSelf = true; + } else if (mRefCount < 0) { + SLOGE("SocketClient refcount went negative!"); + } + pthread_mutex_unlock(&mRefCountMutex); + if (deleteSelf) { + delete this; + } + return deleteSelf; +} diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 72e128dbb00..3f871ea88ff 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -54,8 +54,8 @@ SocketListener::~SocketListener() { close(mCtrlPipe[1]); } SocketClientCollection::iterator it; - for (it = mClients->begin(); it != mClients->end(); ++it) { - delete (*it); + for (it = mClients->begin(); it != mClients->end();) { + (*it)->decRef(); it = mClients->erase(it); } delete mClients; @@ -64,30 +64,30 @@ SocketListener::~SocketListener() { int SocketListener::startListener() { if (!mSocketName && mSock == -1) { - LOGE("Failed to start unbound listener"); + SLOGE("Failed to start unbound listener"); errno = EINVAL; return -1; } else if (mSocketName) { if ((mSock = android_get_control_socket(mSocketName)) < 0) { - LOGE("Obtaining file descriptor socket '%s' failed: %s", + SLOGE("Obtaining file descriptor socket '%s' failed: %s", mSocketName, strerror(errno)); return -1; } } if (mListen && listen(mSock, 4) < 0) { - LOGE("Unable to listen on socket (%s)", strerror(errno)); + SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen) - mClients->push_back(new SocketClient(mSock)); + mClients->push_back(new SocketClient(mSock, false)); if (pipe(mCtrlPipe)) { - LOGE("pipe failed (%s)", strerror(errno)); + SLOGE("pipe failed (%s)", strerror(errno)); return -1; } if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { - LOGE("pthread_create (%s)", strerror(errno)); + SLOGE("pthread_create (%s)", strerror(errno)); return -1; } @@ -96,15 +96,17 @@ int SocketListener::startListener() { int SocketListener::stopListener() { char c = 0; + int rc; - if (write(mCtrlPipe[1], &c, 1) != 1) { - LOGE("Error writing to control pipe (%s)", strerror(errno)); + rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1)); + if (rc != 1) { + SLOGE("Error writing to control pipe (%s)", strerror(errno)); return -1; } void *ret; if (pthread_join(mThread, &ret)) { - LOGE("Error joining to listener thread (%s)", strerror(errno)); + SLOGE("Error joining to listener thread (%s)", strerror(errno)); return -1; } close(mCtrlPipe[0]); @@ -118,7 +120,7 @@ int SocketListener::stopListener() { } SocketClientCollection::iterator it; - for (it = mClients->begin(); it != mClients->end(); ++it) { + for (it = mClients->begin(); it != mClients->end();) { delete (*it); it = mClients->erase(it); } @@ -135,11 +137,13 @@ void *SocketListener::threadStart(void *obj) { void SocketListener::runListener() { + SocketClientCollection *pendingList = new SocketClientCollection(); + while(1) { SocketClientCollection::iterator it; fd_set read_fds; int rc = 0; - int max = 0; + int max = -1; FD_ZERO(&read_fds); @@ -154,14 +158,17 @@ void SocketListener::runListener() { pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { - FD_SET((*it)->getSocket(), &read_fds); - if ((*it)->getSocket() > max) - max = (*it)->getSocket(); + int fd = (*it)->getSocket(); + FD_SET(fd, &read_fds); + if (fd > max) + max = fd; } pthread_mutex_unlock(&mClientsLock); if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { - LOGE("select failed (%s)", strerror(errno)); + if (errno == EINTR) + continue; + SLOGE("select failed (%s)", strerror(errno)); sleep(1); continue; } else if (!rc) @@ -171,39 +178,59 @@ void SocketListener::runListener() { break; if (mListen && FD_ISSET(mSock, &read_fds)) { struct sockaddr addr; - socklen_t alen = sizeof(addr); + socklen_t alen; int c; - if ((c = accept(mSock, &addr, &alen)) < 0) { - LOGE("accept failed (%s)", strerror(errno)); + do { + alen = sizeof(addr); + c = accept(mSock, &addr, &alen); + } while (c < 0 && errno == EINTR); + if (c < 0) { + SLOGE("accept failed (%s)", strerror(errno)); sleep(1); continue; } pthread_mutex_lock(&mClientsLock); - mClients->push_back(new SocketClient(c)); + mClients->push_back(new SocketClient(c, true)); pthread_mutex_unlock(&mClientsLock); } - do { - pthread_mutex_lock(&mClientsLock); - for (it = mClients->begin(); it != mClients->end(); ++it) { - int fd = (*it)->getSocket(); - if (FD_ISSET(fd, &read_fds)) { - pthread_mutex_unlock(&mClientsLock); - if (!onDataAvailable(*it)) { - close(fd); - pthread_mutex_lock(&mClientsLock); - delete *it; - it = mClients->erase(it); - pthread_mutex_unlock(&mClientsLock); + /* Add all active clients to the pending list first */ + pendingList->clear(); + pthread_mutex_lock(&mClientsLock); + for (it = mClients->begin(); it != mClients->end(); ++it) { + int fd = (*it)->getSocket(); + if (FD_ISSET(fd, &read_fds)) { + pendingList->push_back(*it); + } + } + pthread_mutex_unlock(&mClientsLock); + + /* Process the pending list, since it is owned by the thread, + * there is no need to lock it */ + while (!pendingList->empty()) { + /* Pop the first item from the list */ + it = pendingList->begin(); + SocketClient* c = *it; + pendingList->erase(it); + /* Process it, if false is returned and our sockets are + * connection-based, remove and destroy it */ + if (!onDataAvailable(c) && mListen) { + /* Remove the client from our array */ + pthread_mutex_lock(&mClientsLock); + for (it = mClients->begin(); it != mClients->end(); ++it) { + if (*it == c) { + mClients->erase(it); + break; } - FD_CLR(fd, &read_fds); - continue; } + pthread_mutex_unlock(&mClientsLock); + /* Remove our reference to the client */ + c->decRef(); } - pthread_mutex_unlock(&mClientsLock); - } while (0); + } } + delete pendingList; } void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { @@ -212,7 +239,7 @@ void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { for (i = mClients->begin(); i != mClients->end(); ++i) { if ((*i)->sendMsg(code, msg, addErrno)) { - LOGW("Error sending broadcast (%s)", strerror(errno)); + SLOGW("Error sending broadcast (%s)", strerror(errno)); } } pthread_mutex_unlock(&mClientsLock); @@ -224,7 +251,7 @@ void SocketListener::sendBroadcast(const char *msg) { for (i = mClients->begin(); i != mClients->end(); ++i) { if ((*i)->sendMsg(msg)) { - LOGW("Error sending broadcast (%s)", strerror(errno)); + SLOGW("Error sending broadcast (%s)", strerror(errno)); } } pthread_mutex_unlock(&mClientsLock); diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk new file mode 100644 index 00000000000..52b4eadc99f --- /dev/null +++ b/libusbhost/Android.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(my-dir) + +# Static library for Linux host +# ======================================================== + +ifeq ($(HOST_OS),linux) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libusbhost +LOCAL_SRC_FILES := usbhost.c + +include $(BUILD_HOST_STATIC_LIBRARY) + +endif + +# Shared library for target +# ======================================================== + +include $(CLEAR_VARS) + +LOCAL_MODULE := libusbhost +LOCAL_SRC_FILES := usbhost.c + +LOCAL_CFLAGS := -g -DUSE_LIBLOG + +# needed for logcat +LOCAL_SHARED_LIBRARIES := libcutils + +include $(BUILD_SHARED_LIBRARY) diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c new file mode 100644 index 00000000000..b1c967d2a77 --- /dev/null +++ b/libusbhost/usbhost.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define DEBUG 1 +#if DEBUG + +#ifdef USE_LIBLOG +#define LOG_TAG "usbhost" +#include "utils/Log.h" +#define D LOGD +#else +#define D printf +#endif + +#else +#define D(...) +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "usbhost/usbhost.h" + +#define USB_FS_DIR "/dev/bus/usb" +#define USB_FS_ID_SCANNER "/dev/bus/usb/%d/%d" +#define USB_FS_ID_FORMAT "/dev/bus/usb/%03d/%03d" + +// From drivers/usb/core/devio.c +// I don't know why this isn't in a kernel header +#define MAX_USBFS_BUFFER_SIZE 16384 + +struct usb_host_context { + int fd; +}; + +struct usb_device { + char dev_name[64]; + unsigned char desc[4096]; + int desc_length; + int fd; + int writeable; +}; + +static inline int badname(const char *name) +{ + while(*name) { + if(!isdigit(*name++)) return 1; + } + return 0; +} + +/* returns true if one of the callbacks indicates we are done */ +static int find_existing_devices(usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + void *client_data) +{ + char busname[32], devname[32]; + DIR *busdir , *devdir ; + struct dirent *de; + int done = 0; + + busdir = opendir(USB_FS_DIR); + if(busdir == 0) return 1; + + while ((de = readdir(busdir)) != 0 && !done) { + if(badname(de->d_name)) continue; + + snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name); + devdir = opendir(busname); + if(devdir == 0) continue; + + while ((de = readdir(devdir)) && !done) { + if(badname(de->d_name)) continue; + + snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); + done = added_cb(devname, client_data); + } // end of devdir while + closedir(devdir); + } //end of busdir while + closedir(busdir); + + return done; +} + +struct usb_host_context *usb_host_init() +{ + struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context)); + if (!context) { + fprintf(stderr, "out of memory in usb_host_context\n"); + return NULL; + } + context->fd = inotify_init(); + if (context->fd < 0) { + fprintf(stderr, "inotify_init failed\n"); + free(context); + return NULL; + } + return context; +} + +void usb_host_cleanup(struct usb_host_context *context) +{ + close(context->fd); + free(context); +} + +void usb_host_run(struct usb_host_context *context, + usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + usb_discovery_done_cb discovery_done_cb, + void *client_data) +{ + struct inotify_event* event; + char event_buf[512]; + char path[100]; + int i, ret, done = 0; + int wd, wds[10]; + int wd_count = sizeof(wds) / sizeof(wds[0]); + + D("Created device discovery thread\n"); + + /* watch for files added and deleted within USB_FS_DIR */ + memset(wds, 0, sizeof(wds)); + /* watch the root for new subdirectories */ + wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); + if (wds[0] < 0) { + fprintf(stderr, "inotify_add_watch failed\n"); + if (discovery_done_cb) + discovery_done_cb(client_data); + return; + } + + /* watch existing subdirectories of USB_FS_DIR */ + for (i = 1; i < wd_count; i++) { + snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i); + ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); + if (ret > 0) + wds[i] = ret; + } + + /* check for existing devices first, after we have inotify set up */ + done = find_existing_devices(added_cb, removed_cb, client_data); + if (discovery_done_cb) + done |= discovery_done_cb(client_data); + + while (!done) { + ret = read(context->fd, event_buf, sizeof(event_buf)); + if (ret >= (int)sizeof(struct inotify_event)) { + event = (struct inotify_event *)event_buf; + wd = event->wd; + if (wd == wds[0]) { + i = atoi(event->name); + snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name); + D("new subdirectory %s: index: %d\n", path, i); + if (i > 0 && i < wd_count) { + ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); + if (ret > 0) + wds[i] = ret; + } + } else { + for (i = 1; i < wd_count && !done; i++) { + if (wd == wds[i]) { + snprintf(path, sizeof(path), "%s/%03d/%s", USB_FS_DIR, i, event->name); + if (event->mask == IN_CREATE) { + D("new device %s\n", path); + done = added_cb(path, client_data); + } else if (event->mask == IN_DELETE) { + D("gone device %s\n", path); + done = removed_cb(path, client_data); + } + } + } + } + } + } +} + +struct usb_device *usb_device_open(const char *dev_name) +{ + int fd, did_retry = 0, writeable = 1; + + D("usb_device_open %s\n", dev_name); + +retry: + fd = open(dev_name, O_RDWR); + if (fd < 0) { + /* if we fail, see if have read-only access */ + fd = open(dev_name, O_RDONLY); + D("usb_device_open open returned %d errno %d\n", fd, errno); + if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) { + /* work around race condition between inotify and permissions management */ + sleep(1); + did_retry = 1; + goto retry; + } + + if (fd < 0) + return NULL; + writeable = 0; + D("[ usb open read-only %s fd = %d]\n", dev_name, fd); + } + + struct usb_device* result = usb_device_new(dev_name, fd); + if (result) + result->writeable = writeable; + return result; +} + +void usb_device_close(struct usb_device *device) +{ + close(device->fd); + free(device); +} + +struct usb_device *usb_device_new(const char *dev_name, int fd) +{ + struct usb_device *device = calloc(1, sizeof(struct usb_device)); + int length; + + D("usb_device_new %s fd: %d\n", dev_name, fd); + + if (lseek(fd, 0, SEEK_SET) != 0) + goto failed; + length = read(fd, device->desc, sizeof(device->desc)); + D("usb_device_new read returned %d errno %d\n", length, errno); + if (length < 0) + goto failed; + + strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1); + device->fd = fd; + device->desc_length = length; + // assume we are writeable, since usb_device_get_fd will only return writeable fds + device->writeable = 1; + return device; + +failed: + close(fd); + free(device); + return NULL; +} + +static int usb_device_reopen_writeable(struct usb_device *device) +{ + if (device->writeable) + return 1; + + int fd = open(device->dev_name, O_RDWR); + if (fd >= 0) { + close(device->fd); + device->fd = fd; + device->writeable = 1; + return 1; + } + D("usb_device_reopen_writeable failed errno %d\n", errno); + return 0; +} + +int usb_device_get_fd(struct usb_device *device) +{ + if (!usb_device_reopen_writeable(device)) + return -1; + return device->fd; +} + +const char* usb_device_get_name(struct usb_device *device) +{ + return device->dev_name; +} + +int usb_device_get_unique_id(struct usb_device *device) +{ + int bus = 0, dev = 0; + sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev); + return bus * 1000 + dev; +} + +int usb_device_get_unique_id_from_name(const char* name) +{ + int bus = 0, dev = 0; + sscanf(name, USB_FS_ID_SCANNER, &bus, &dev); + return bus * 1000 + dev; +} + +char* usb_device_get_name_from_unique_id(int id) +{ + int bus = id / 1000; + int dev = id % 1000; + char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT)); + snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev); + return result; +} + +uint16_t usb_device_get_vendor_id(struct usb_device *device) +{ + struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc; + return __le16_to_cpu(desc->idVendor); +} + +uint16_t usb_device_get_product_id(struct usb_device *device) +{ + struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc; + return __le16_to_cpu(desc->idProduct); +} + +const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device) +{ + return (struct usb_device_descriptor*)device->desc; +} + +char* usb_device_get_string(struct usb_device *device, int id) +{ + char string[256]; + __u16 buffer[128]; + __u16 languages[128]; + int i, result; + int languageCount = 0; + + string[0] = 0; + memset(languages, 0, sizeof(languages)); + + // read list of supported languages + result = usb_device_control_transfer(device, + USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, + (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0); + if (result > 0) + languageCount = (result - 2) / 2; + + for (i = 1; i <= languageCount; i++) { + memset(buffer, 0, sizeof(buffer)); + + result = usb_device_control_transfer(device, + USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, + (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0); + if (result > 0) { + int i; + // skip first word, and copy the rest to the string, changing shorts to bytes. + result /= 2; + for (i = 1; i < result; i++) + string[i - 1] = buffer[i]; + string[i - 1] = 0; + return strdup(string); + } + } + + return NULL; +} + +char* usb_device_get_manufacturer_name(struct usb_device *device) +{ + struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; + + if (desc->iManufacturer) + return usb_device_get_string(device, desc->iManufacturer); + else + return NULL; +} + +char* usb_device_get_product_name(struct usb_device *device) +{ + struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; + + if (desc->iProduct) + return usb_device_get_string(device, desc->iProduct); + else + return NULL; +} + +char* usb_device_get_serial(struct usb_device *device) +{ + struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; + + if (desc->iSerialNumber) + return usb_device_get_string(device, desc->iSerialNumber); + else + return NULL; +} + +int usb_device_is_writeable(struct usb_device *device) +{ + return device->writeable; +} + +void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter) +{ + iter->config = device->desc; + iter->config_end = device->desc + device->desc_length; + iter->curr_desc = device->desc; +} + +struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter) +{ + struct usb_descriptor_header* next; + if (iter->curr_desc >= iter->config_end) + return NULL; + next = (struct usb_descriptor_header*)iter->curr_desc; + iter->curr_desc += next->bLength; + return next; +} + +int usb_device_claim_interface(struct usb_device *device, unsigned int interface) +{ + return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface); +} + +int usb_device_release_interface(struct usb_device *device, unsigned int interface) +{ + return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface); +} + +int usb_device_connect_kernel_driver(struct usb_device *device, + unsigned int interface, int connect) +{ + struct usbdevfs_ioctl ctl; + + ctl.ifno = interface; + ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT); + ctl.data = NULL; + return ioctl(device->fd, USBDEVFS_IOCTL, &ctl); +} + +int usb_device_control_transfer(struct usb_device *device, + int requestType, + int request, + int value, + int index, + void* buffer, + int length, + unsigned int timeout) +{ + struct usbdevfs_ctrltransfer ctrl; + + // this usually requires read/write permission + if (!usb_device_reopen_writeable(device)) + return -1; + + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.bRequestType = requestType; + ctrl.bRequest = request; + ctrl.wValue = value; + ctrl.wIndex = index; + ctrl.wLength = length; + ctrl.data = buffer; + ctrl.timeout = timeout; + return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl); +} + +int usb_device_bulk_transfer(struct usb_device *device, + int endpoint, + void* buffer, + int length, + unsigned int timeout) +{ + struct usbdevfs_bulktransfer ctrl; + + // need to limit request size to avoid EINVAL + if (length > MAX_USBFS_BUFFER_SIZE) + length = MAX_USBFS_BUFFER_SIZE; + + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.ep = endpoint; + ctrl.len = length; + ctrl.data = buffer; + ctrl.timeout = timeout; + return ioctl(device->fd, USBDEVFS_BULK, &ctrl); +} + +struct usb_request *usb_request_new(struct usb_device *dev, + const struct usb_endpoint_descriptor *ep_desc) +{ + struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb)); + if (!urb) + return NULL; + + if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) + urb->type = USBDEVFS_URB_TYPE_BULK; + else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + urb->type = USBDEVFS_URB_TYPE_INTERRUPT; + else { + D("Unsupported endpoint type %d", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + free(urb); + return NULL; + } + urb->endpoint = ep_desc->bEndpointAddress; + + struct usb_request *req = calloc(1, sizeof(struct usb_request)); + if (!req) { + free(urb); + return NULL; + } + + req->dev = dev; + req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize); + req->private_data = urb; + req->endpoint = urb->endpoint; + urb->usercontext = req; + + return req; +} + +void usb_request_free(struct usb_request *req) +{ + free(req->private_data); + free(req); +} + +int usb_request_queue(struct usb_request *req) +{ + struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data; + int res; + + urb->status = -1; + urb->buffer = req->buffer; + // need to limit request size to avoid EINVAL + if (req->buffer_length > MAX_USBFS_BUFFER_SIZE) + urb->buffer_length = MAX_USBFS_BUFFER_SIZE; + else + urb->buffer_length = req->buffer_length; + + do { + res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb); + } while((res < 0) && (errno == EINTR)); + + return res; +} + +struct usb_request *usb_request_wait(struct usb_device *dev) +{ + struct usbdevfs_urb *urb = NULL; + struct usb_request *req = NULL; + int res; + + while (1) { + int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb); + D("USBDEVFS_REAPURB returned %d\n", res); + if (res < 0) { + if(errno == EINTR) { + continue; + } + D("[ reap urb - error ]\n"); + return NULL; + } else { + D("[ urb @%p status = %d, actual = %d ]\n", + urb, urb->status, urb->actual_length); + req = (struct usb_request*)urb->usercontext; + req->actual_length = urb->actual_length; + } + break; + } + return req; +} + +int usb_request_cancel(struct usb_request *req) +{ + struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data); + return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, &urb); +} + diff --git a/logcat/Android.mk b/logcat/Android.mk index 5a9f12da040..7b8eb01e3e0 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -3,24 +3,10 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= logcat.cpp +LOCAL_SRC_FILES:= logcat.cpp event.logtags LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE:= logcat include $(BUILD_EXECUTABLE) - -######################## -include $(CLEAR_VARS) - -LOCAL_MODULE := event-log-tags - -# This will install the file in /system/etc -# -LOCAL_MODULE_CLASS := ETC - -LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_PREBUILT_STRIP_COMMENTS := 1 - -include $(BUILD_PREBUILT) diff --git a/logcat/event-log-tags b/logcat/event-log-tags deleted file mode 100644 index 5c4c96206b2..00000000000 --- a/logcat/event-log-tags +++ /dev/null @@ -1,404 +0,0 @@ -# The entries in this file map a sparse set of log tag numbers to tag names. -# This is installed on the device, in /system/etc, and parsed by logcat. -# -# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the -# negative values alone for now.) -# -# Tag names are one or more ASCII letters and numbers or underscores, i.e. -# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former -# impacts log readability, the latter makes regex searches more annoying). -# -# Tag numbers and names are separated by whitespace. Blank lines and lines -# starting with '#' are ignored. -# -# Optionally, after the tag names can be put a description for the value(s) -# of the tag. Description are in the format -# (|data type[|data unit]) -# Multiple values are separated by commas. -# -# The data type is a number from the following values: -# 1: int -# 2: long -# 3: string -# 4: list -# -# The data unit is a number taken from the following list: -# 1: Number of objects -# 2: Number of bytes -# 3: Number of milliseconds -# 4: Number of allocations -# 5: Id -# 6: Percent -# Default value for data of type int/long is 2 (bytes). -# -# TODO: generate ".java" and ".h" files with integer constants from this file. - -# These are used for testing, do not modify without updating -# tests/framework-tests/src/android/util/EventLogFunctionalTest.java. -42 answer (to life the universe etc|3) -314 pi -2718 e - -2719 configuration_changed (config mask|1|5) -# "account" is the java hash of the account name -2720 sync (id|3),(event|1|5),(source|1|5),(account|1|5) -2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6) -2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1) -2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3) -# This is logged when the device is being forced to sleep (typically by -# the user pressing the power button). -2724 power_sleep_requested (wakeLocksCleared|1|1) -# This is logged when the screen on broadcast has completed -2725 power_screen_broadcast_send (wakelockCount|1|1) -# This is logged when the screen broadcast has completed -2726 power_screen_broadcast_done (on|1|5),(broadcastDuration|2|3),(wakelockCount|1|1) -# This is logged when the screen on broadcast has completed -2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1) -# This is logged when the screen is turned on or off. -2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1) -# This is logged when the partial wake lock (keeping the device awake -# regardless of whether the screen is off) is acquired or released. -2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3) -# This is logged when battery goes from discharging to charging. -# It lets us count the total amount of time between charges and the discharge level -2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6) -# -# Leave IDs through 2739 for more power logs -# - -# This event is logged when the location service uploads location data. -2740 location_controller -# This event is logged when someone is deciding to force a garbage collection -2741 force_gc (reason|3) -# This event is logged on each tickle -2742 tickle (authority|3) -# What happens in a sync operation (bytes sent and received, and -# operation details) -2743 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3) - -# The disk space free on the /data partition, in bytes -2744 free_storage_changed (data|2|2) -# Device low memory notification and disk space free on the /data partition, in bytes at that time -2745 low_storage (data|2|2) -# disk space free on the /data, /system, and /cache partitions in bytes -2746 free_storage_left (data|2|2),(system|2|2),(cache|2|2) - -# contacts aggregation: time and number of contacts. -# count is negative for query phase, positive for merge phase -2747 contacts_aggregation (aggregation time|2|3), (count|1|1) - -# when a NotificationManager.notify is called -2750 notification_enqueue (pkg|3),(id|1|5),(notification|3) -# when someone tries to cancel a notification, the notification manager sometimes -# calls this with flags too -2751 notification_cancel (pkg|3),(id|1|5),(required_flags|1) -# when someone tries to cancel all of the notifications for a particular package -2752 notification_cancel_all (pkg|3),(required_flags|1) - -# This event is logged when GTalkService encounters important events -2800 gtalkservice (eventType|1) -# This event is logged for GTalk connection state changes. The status field is an int, but -# it really contains 4 separate values, each taking up a byte -# (eventType << 24) + (connection state << 16) + (connection error << 8) + network state -2801 gtalk_connection (status|1) - -2802 watchdog (Service|3) -2803 watchdog_proc_pss (Process|3),(Pid|1|5),(Pss|1|2) -2804 watchdog_soft_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2),(Skip|3) -2805 watchdog_hard_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2) -2806 watchdog_pss_stats (EmptyPss|1|2),(EmptyCount|1|1),(BackgroundPss|1|2),(BackgroundCount|1|1),(ServicePss|1|2),(ServiceCount|1|1),(VisiblePss|1|2),(VisibleCount|1|1),(ForegroundPss|1|2),(ForegroundCount|1|1),(NoPssCount|1|1) -2807 watchdog_proc_stats (DeathsInOne|1|1),(DeathsInTwo|1|1),(DeathsInThree|1|1),(DeathsInFour|1|1),(DeathsInFive|1|1) -2808 watchdog_scheduled_reboot (Now|2|1),(Interval|1|3),(StartTime|1|3),(Window|1|3),(Skip|3) -2809 watchdog_meminfo (MemFree|1|2),(Buffers|1|2),(Cached|1|2),(Active|1|2),(Inactive|1|2),(AnonPages|1|2),(Mapped|1|2),(Slab|1|2),(SReclaimable|1|2),(SUnreclaim|1|2),(PageTables|1|2) -2810 watchdog_vmstat (runtime|2|3),(pgfree|1|1),(pgactivate|1|1),(pgdeactivate|1|1),(pgfault|1|1),(pgmajfault|1|1) -2811 watchdog_requested_reboot (NoWait|1|1),(ScheduleInterval|1|3),(RecheckInterval|1|3),(StartTime|1|3),(Window|1|3),(MinScreenOff|1|3),(MinNextAlarm|1|3) - -2820 backup_data_changed (Package|3) -2821 backup_start (Transport|3) -2822 backup_transport_failure (Package|3) -2823 backup_agent_failure (Package|3),(Message|3) -2824 backup_package (Package|3),(Size|1|2) -2825 backup_success (Packages|1|1),(Time|1|3) -2826 backup_reset (Transport|3) -2827 backup_initialize - -2830 restore_start (Transport|3),(Source|2|5) -2831 restore_transport_failure -2832 restore_agent_failure (Package|3),(Message|3) -2833 restore_package (Package|3),(Size|1|2) -2834 restore_success (Packages|1|1),(Time|1|3) - -# Device boot timings. We include monotonic clock values because the -# intrinsic event log times are wall-clock. -# -# Runtime starts: -3000 boot_progress_start (time|2|3) -# SystemServer.run() starts: -3010 boot_progress_system_run (time|2|3) -# ZygoteInit class preloading starts: -3020 boot_progress_preload_start (time|2|3) -# ZygoteInit class preloading ends: -3030 boot_progress_preload_end (time|2|3) -# ActivityManagerService.systemReady() starts: -3040 boot_progress_ams_ready (time|2|3) -# ActivityManagerService calls enableScreenAfterBoot(): -3050 boot_progress_enable_screen (time|2|3) -# Package Manager starts: -3060 boot_progress_pms_start (time|2|3) -# Package Manager .apk scan starts: -3070 boot_progress_pms_system_scan_start (time|2|3) -# Package Manager .apk scan starts: -3080 boot_progress_pms_data_scan_start (time|2|3) -# Package Manager .apk scan ends: -3090 boot_progress_pms_scan_end (time|2|3) -# Package Manager ready: -3100 boot_progress_pms_ready (time|2|3) -# + check activity_launch_time for Home app - -# This event is logged when GTalk connection is closed. -# The status field is an int, but contains 2 different values, it's represented as -# -# (networkType << 8) + connection error -# -# the possible error values are -# -# no_error=0, no_network=1, connection_failed=2, unknown_host=3, auth_failed=4, -# auth_expired=5, heart_beat_timeout=6, server_error=7, server_reject_rate_limiting=8, unknown=10 -# -# duration is the connection duration. -4000 gtalk_conn_close (status|1),(duration|1) - -# This event is logged for GTalk heartbeat resets -# interval_and_nt contains both the heartbeat interval and the network type, It's represented as -# (networkType << 16) + interval -# interval is in seconds; network type can be 0 (mobile) or 1 (wifi); ip is the host ip addr. -4001 gtalk_heartbeat_reset (interval_and_nt|1),(ip|3) - -# dvm_gc_info: LIST (LONG, LONG, LONG) -# -# First LONG: -# -# [63] 1 -# [62-24] ASCII process identifier -# [23-12] GC time in ms -# [11- 0] Bytes freed -# -# Second LONG (aggregated heap info): -# -# [63-62] 10 -# [61-60] Reserved; must be zero -# [59-48] Objects freed -# [47-36] Actual size (current footprint) -# [35-24] Allowed size (current hard max) -# [23-12] Objects allocated -# [11- 0] Bytes allocated -# -# Third LONG (zygote heap info): -# -# [63-62] 11 -# [61-60] Reserved; must be zero -# [59-48] Soft limit -# [47-36] Actual size (current footprint) -# [35-24] Allowed size (current hard max) -# [23-12] Objects allocated -# [11- 0] Bytes allocated -# -# Fourth LONG: -# -# [63-48] Reserved; must be zero -# [47-36] dlmallocFootprint -# [35-24] mallinfo: total allocated space -# [23-12] External byte limit -# [11- 0] External bytes allocated -# -# See HeapDebug.c -# -20001 dvm_gc_info (custom|2),(custom|2),(custom|2),(custom|2) -20002 dvm_gc_madvise_info (total|1|2),(zygote|1|2) - -# Do not change these names without updating the checkin_events setting in -# google3/googledata/wireless/android/provisioning/gservices.config !! -# -# An activity is being finished: -30001 am_finish_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) -# A task is being brought to the front of the screen: -30002 am_task_to_front (Task|1|5) -# An existing activity is being given a new intent: -30003 am_new_intent (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) -# A new task is being created: -30004 am_create_task (Task ID|1|5) -# A new activity is being created in an existing task: -30005 am_create_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) -# An activity has been resumed into the foreground but was not already running: -30006 am_restart_activity (Token|1|5),(Task ID|1|5),(Component Name|3) -# An activity has been resumed and is now in the foreground: -30007 am_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) -# Application Not Responding -30008 anr (pid|1|5),(Package Name|3),(reason|3) -# Activity launch time -30009 activity_launch_time (Token|1|5),(Component Name|3),(time|2|3) -# Application process bound to work -30010 am_proc_bound (PID|1|5),(Process Name|3) -# Application process died -30011 am_proc_died (PID|1|5),(Process Name|3) -# The Activity Manager failed to pause the given activity. -30012 am_failed_to_pause (Token|1|5),(Wanting to pause|3),(Currently pausing|3) -# Attempting to pause the current activity -30013 am_pause_activity (Token|1|5),(Component Name|3) -# Application process has been started -30014 am_proc_start (PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3) -# An application process has been marked as bad -30015 am_proc_bad (UID|1|5),(Process Name|3) -# An application process that was bad is now marked as good -30016 am_proc_good (UID|1|5),(Process Name|3) -# Reporting to applications that memory is low -30017 am_low_memory (Num Processes|1|1) -# An activity is being destroyed: -30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3) -# An activity has been relaunched, resumed, and is now in the foreground: -30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) -# An activity has been relaunched: -30020 am_relaunch_activity (Token|1|5),(Task ID|1|5),(Component Name|3) -# The activity's onPause has been called. -30021 am_on_paused_called (Component Name|3) -# The activity's onResume has been called. -30022 am_on_resume_called (Component Name|3) -# Kill a process to reclaim memory. -30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5) -# Discard an undelivered serialized broadcast (timeout/ANR/crash) -30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5) -30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3) -# A service is being created -30030 am_create_service (Service Record|1|5),(Name|3),(Intent|3),(PID|1|5) -# A service is being destroyed -30031 am_destroy_service (Service Record|1|5),(Name|3),(PID|1|5) -# A process has crashed too many times, it is being cleared -30032 am_process_crashed_too_much (Name|3),(PID|1|5) -# An unknown process is trying to attach to the activity manager -30033 am_drop_process (PID|1|5) -# A service has crashed too many times, it is being stopped -30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5) -# A service is going to be restarted after its process went away -30035 am_schedule_service_restart (Component Name|3),(Time|2|3) -# A client was waiting for a content provider, but its process was lost -30036 am_provider_lost_process (Package Name|3),(UID|1|5),(Name|3) -# The activity manager gave up on a new process taking too long to start -30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3) - -# Out of memory for surfaces. -31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3) - -# Re-connecting to input method service because we haven't received its interface -32000 imf_force_reconnect_ime (IME|4),(Time Since Connect|2|3),(Showing|1|1) - -75000 sqlite_mem_alarm_current (current|1|2) -75001 sqlite_mem_alarm_max (max|1|2) -75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4) -75003 sqlite_mem_released (Memory released|1|2) -75004 sqlite_db_corrupt (Database file corrupt|3) - -40000 checkin (Check in time|2|3) - -50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3) -50001 menu_opened (Menu type where 0 is options and 1 is context|1|5) -# Connectivity state changed: -# [31-13] Reserved for future use -# [12- 9] Network subtype (for mobile network, as defined by TelephonyManager) -# [ 8- 3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) -# [ 2- 0] Network type (as defined by ConnectivityManager) -50020 connectivity_state_changed (custom|1|5) - -# Wi-Fi network state changed: -# [31- 6] Reserved for future use -# [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState) -50021 wifi_network_state_changed (network_state|1|5) - -# Wi-Fi supplicant state changed: -# [31- 6] Reserved for future use -# [ 5- 0] Supplicant state ordinal (as defined by SupplicantState) -50022 wifi_supplicant_state_changed (supplicant_state|1|5) - -# Wi-Fi driver state changed: -# [31- 1] Reserved for future use -# [ 0- 0] Driver start (1) or stopped (0) -50023 wifi_driver_state_changed (driver_state|1|5) - -# Wi-Fi interface configuration state changed: -# [31- 1] Reserved for future use -# [ 0- 0] Interface configuration succeeded (1) or failed (0) -50024 wifi_interface_configuration_state_changed (IP_configuration|1|5) - -# Wi-Fi supplicant connection state changed: -# [31- 2] Reserved for future use -# [ 1- 0] Connected to supplicant (1) or disconnected from supplicant (0), -# or supplicant died (2) -50025 wifi_supplicant_connection_state_changed (connected|1|5) - -# PDP Context has a bad DNS address -50100 pdp_bad_dns_address (dns_address|3) - -# For data connection on PDP context, reached the data-out-without-data-in -# packet count that triggers a countdown to radio restart -50101 pdp_radio_reset_countdown_triggered (out_packet_count|1|1) - -# Radio restart - timed out with no incoming packets. -50102 pdp_radio_reset (out_packet_count|1|1) - -# PDP context reset - timed out with no incoming packets. -50103 pdp_context_reset (out_packet_count|1|1) - -# Reregister to data network - timed out with no incoming packets. -50104 pdp_reregister_network (out_packet_count|1|1) - -# PDP Setup failures -50105 pdp_setup_fail (cause|1|5), (cid|1|5), (network_type|1|5) - -# Call drops -50106 call_drop (cause|1|5), (cid|1|5), (network_type|1|5) - -# Data network registration failed after successful voice registration -50107 data_network_registration_fail (op_numeric|1|5), (cid|1|5) - -# Suspicious status of data connection while radio poweroff -50108 data_network_status_on_radio_off (dc_state|3), (enable|1|5) - -# PDP drop caused by network -50109 pdp_network_drop (cid|1|5), (network_type|1|5) - -# CDMA data network setup failure -50110 cdma_data_setup_failed (cause|1|5), (cid|1|5), (network_type|1|5) - -# CDMA data network drop -50111 cdma_data_drop (cid|1|5), (network_type|1|5) - -# GSM radio access technology switched -50112 gsm_rat_switched (cid|1|5), (network_from|1|5), (network_to|1|5) - -# Do not change these names without updating tag in: -#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c -51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5) - -# db stats. 0 is query, 1 is write (may become more fine grained in the -# future) -52000 db_operation (name|3),(op_type|1|5),(time|2|3) - -# http request/response stats -52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2) -60000 viewroot_draw (Draw time|1|3) -60001 viewroot_layout (Layout time|1|3) -60002 view_build_drawing_cache (View created drawing cache|1|5) -60003 view_use_drawing_cache (View drawn using bitmap cache|1|5) - -# 0 for screen off, 1 for screen on, 2 for key-guard done -70000 screen_toggled (screen_state|1|5) - -# browser stats for diary study -70101 browser_zoom_level_change (start level|1|5),(end level|1|5),(time|2|3) -70102 browser_double_tap_duration (duration|1|3),(time|2|3) - -# aggregation service -70200 aggregation (aggregation time|2|3) -70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2) - -# NOTE - the range 1000000-2000000 is reserved for partners and others who -# want to define their own log tags without conflicting with the core platform. diff --git a/logcat/event.logtags b/logcat/event.logtags new file mode 100644 index 00000000000..2e814ff73bf --- /dev/null +++ b/logcat/event.logtags @@ -0,0 +1,138 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# TODO: generate ".java" and ".h" files with integer constants from this file. + +# These are used for testing, do not modify without updating +# tests/framework-tests/src/android/util/EventLogFunctionalTest.java. +42 answer (to life the universe etc|3) +314 pi +2718 e + +# "account" is the java hash of the account name +2720 sync (id|3),(event|1|5),(source|1|5),(account|1|5) + +# This event is logged when the location service uploads location data. +2740 location_controller +# This event is logged when someone is deciding to force a garbage collection +2741 force_gc (reason|3) +# This event is logged on each tickle +2742 tickle (authority|3) + +# contacts aggregation: time and number of contacts. +# count is negative for query phase, positive for merge phase +2747 contacts_aggregation (aggregation time|2|3), (count|1|1) + +# Device boot timings. We include monotonic clock values because the +# intrinsic event log times are wall-clock. +# +# Runtime starts: +3000 boot_progress_start (time|2|3) +# ZygoteInit class preloading starts: +3020 boot_progress_preload_start (time|2|3) +# ZygoteInit class preloading ends: +3030 boot_progress_preload_end (time|2|3) + +# Dalvik VM +20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6) + +75000 sqlite_mem_alarm_current (current|1|2) +75001 sqlite_mem_alarm_max (max|1|2) +75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4) +75003 sqlite_mem_released (Memory released|1|2) +75004 sqlite_db_corrupt (Database file corrupt|3) + +50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3) +50001 menu_opened (Menu type where 0 is options and 1 is context|1|5) + +# HSM wifi state change +# Hierarchical state class name (as defined in WifiStateTracker.java) +# Logged on every state change in the hierarchical state machine +50021 wifi_state_changed (wifi_state|3) +# HSM wifi event +# [31-16] Reserved for future use +# [15 - 0] HSM event (as defined in WifiStateTracker.java) +# Logged when an event is handled in a hierarchical state +50022 wifi_event_handled (wifi_event|1|5) +# Supplicant state change +# [31-13] Reserved for future use +# [8 - 0] Supplicant state (as defined in SupplicantState.java) +# Logged when the supplicant switches to a new state +50023 wifi_supplicant_state_changed (supplicant_state|1|5) + +# Do not change these names without updating tag in: +#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c +51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5) + +# Database operation samples. +# db: the filename of the database +# sql: the executed query (without query args) +# time: cpu time millis (not wall time), including lock acquisition +# blocking_package: if this is on a main thread, the package name, otherwise "" +# sample_percent: the percent likelihood this query was logged +52000 db_sample (db|3),(sql|3),(time|1|3),(blocking_package|3),(sample_percent|1|6) + +# http request/response stats +52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2) +60000 viewroot_draw (Draw time|1|3) +60001 viewroot_layout (Layout time|1|3) +60002 view_build_drawing_cache (View created drawing cache|1|5) +60003 view_use_drawing_cache (View drawn using bitmap cache|1|5) + +# graphics timestamp +60100 sf_app_dequeue_before (buffer|1),(identity|1),(time|2) +60101 sf_app_dequeue_after (buffer|1),(identity|1),(time|2) +60102 sf_app_lock_before (buffer|1),(identity|1),(time|2) +60103 sf_app_lock_after (buffer|1),(identity|1),(time|2) +60104 sf_app_queue (buffer|1),(identity|1),(time|2) +60105 sf_repaint (buffer|1),(time|2) +60106 sf_composition_complete (buffer|1),(time|2) +60107 sf_unlock_clients (buffer|1),(time|2) +60108 sf_swapbuffers (buffer|1),(time|2) +60109 sf_repaint_done (buffer|1),(time|2) +60110 sf_fb_post_before (buffer|1),(time|2) +60111 sf_fb_post_after (buffer|1),(time|2) +60112 sf_fb_dequeue_before (buffer|1),(time|2) +60113 sf_fb_dequeue_after (buffer|1),(time|2) +60114 sf_fb_lock_before (buffer|1),(time|2) +60115 sf_fb_lock_after (buffer|1),(time|2) + +# 0 for screen off, 1 for screen on, 2 for key-guard done +70000 screen_toggled (screen_state|1|5) + +# aggregation service +70200 aggregation (aggregation time|2|3) +70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2) + +# NOTE - the range 1000000-2000000 is reserved for partners and others who +# want to define their own log tags without conflicting with the core platform. diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 3130a1cce7a..22e46eca69e 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -24,12 +24,66 @@ #define DEFAULT_MAX_ROTATED_LOGS 4 static AndroidLogFormat * g_logformat; +static bool g_nonblock = false; +static int g_tail_lines = 0; /* logd prefixes records with a length field */ #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) #define LOG_FILE_DIR "/dev/log/" +struct queued_entry_t { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); + struct logger_entry entry __attribute__((aligned(4))); + }; + queued_entry_t* next; + + queued_entry_t() { + next = NULL; + } +}; + +static int cmp(queued_entry_t* a, queued_entry_t* b) { + int n = a->entry.sec - b->entry.sec; + if (n != 0) { + return n; + } + return a->entry.nsec - b->entry.nsec; +} + +struct log_device_t { + char* device; + bool binary; + int fd; + bool printed; + char label; + + queued_entry_t* queue; + log_device_t* next; + + log_device_t(char* d, bool b, char l) { + device = d; + binary = b; + label = l; + queue = NULL; + next = NULL; + printed = false; + } + + void enqueue(queued_entry_t* entry) { + if (this->queue == NULL) { + this->queue = entry; + } else { + queued_entry_t** e = &this->queue; + while (*e && cmp(entry, *e) >= 0) { + e = &((*e)->next); + } + entry->next = *e; + *e = entry; + } + } +}; namespace android { @@ -40,8 +94,8 @@ static int g_logRotateSizeKBytes = 0; // 0 means "no log rotat static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded" static int g_outFD = -1; static off_t g_outByteCount = 0; -static int g_isBinary = 0; static int g_printBinary = 0; +static int g_devCount = 0; static EventTagMap* g_eventTagMap = NULL; @@ -103,14 +157,14 @@ void printBinary(struct logger_entry *buf) } while (ret < 0 && errno == EINTR); } -static void processBuffer(struct logger_entry *buf) +static void processBuffer(log_device_t* dev, struct logger_entry *buf) { - int bytesWritten; + int bytesWritten = 0; int err; AndroidLogEntry entry; char binaryMsgBuf[1024]; - if (g_isBinary) { + if (dev->binary) { err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap, binaryMsgBuf, sizeof(binaryMsgBuf)); //printf(">>> pri=%d len=%d msg='%s'\n", @@ -118,15 +172,27 @@ static void processBuffer(struct logger_entry *buf) } else { err = android_log_processLogBuffer(buf, &entry); } - if (err < 0) + if (err < 0) { goto error; + } - bytesWritten = android_log_filterAndPrintLogLine( - g_logformat, g_outFD, &entry); + if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) { + if (false && g_devCount > 1) { + binaryMsgBuf[0] = dev->label; + binaryMsgBuf[1] = ' '; + bytesWritten = write(g_outFD, binaryMsgBuf, 2); + if (bytesWritten < 0) { + perror("output error"); + exit(-1); + } + } - if (bytesWritten < 0) { - perror("output error"); - exit(-1); + bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry); + + if (bytesWritten < 0) { + perror("output error"); + exit(-1); + } } g_outByteCount += bytesWritten; @@ -142,36 +208,147 @@ static void processBuffer(struct logger_entry *buf) return; } -static void readLogLines(int logfd) -{ - while (1) { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry *entry = (struct logger_entry *) buf; - int ret; +static void chooseFirst(log_device_t* dev, log_device_t** firstdev) { + for (*firstdev = NULL; dev != NULL; dev = dev->next) { + if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) { + *firstdev = dev; + } + } +} - ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) - break; - perror("logcat read"); - exit(EXIT_FAILURE); +static void maybePrintStart(log_device_t* dev) { + if (!dev->printed) { + dev->printed = true; + if (g_devCount > 1 && !g_printBinary) { + char buf[1024]; + snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device); + if (write(g_outFD, buf, strlen(buf)) < 0) { + perror("output error"); + exit(-1); + } } - else if (!ret) { - fprintf(stderr, "read: Unexpected EOF!\n"); - exit(EXIT_FAILURE); + } +} + +static void skipNextEntry(log_device_t* dev) { + maybePrintStart(dev); + queued_entry_t* entry = dev->queue; + dev->queue = entry->next; + delete entry; +} + +static void printNextEntry(log_device_t* dev) { + maybePrintStart(dev); + if (g_printBinary) { + printBinary(&dev->queue->entry); + } else { + processBuffer(dev, &dev->queue->entry); + } + skipNextEntry(dev); +} + +static void readLogLines(log_device_t* devices) +{ + log_device_t* dev; + int max = 0; + int ret; + int queued_lines = 0; + bool sleep = true; + + int result; + fd_set readset; + + for (dev=devices; dev; dev = dev->next) { + if (dev->fd > max) { + max = dev->fd; } + } + + while (1) { + do { + timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR. + FD_ZERO(&readset); + for (dev=devices; dev; dev = dev->next) { + FD_SET(dev->fd, &readset); + } + result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); + } while (result == -1 && errno == EINTR); + + if (result >= 0) { + for (dev=devices; dev; dev = dev->next) { + if (FD_ISSET(dev->fd, &readset)) { + queued_entry_t* entry = new queued_entry_t(); + /* NOTE: driver guarantees we read exactly one full entry */ + ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); + if (ret < 0) { + if (errno == EINTR) { + delete entry; + goto next; + } + if (errno == EAGAIN) { + delete entry; + break; + } + perror("logcat read"); + exit(EXIT_FAILURE); + } + else if (!ret) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + else if (entry->entry.len != ret - sizeof(struct logger_entry)) { + fprintf(stderr, "read: unexpected length. Expected %d, got %d\n", + entry->entry.len, ret - sizeof(struct logger_entry)); + exit(EXIT_FAILURE); + } - /* NOTE: driver guarantees we read exactly one full entry */ + entry->entry.msg[entry->entry.len] = '\0'; - entry->msg[entry->len] = '\0'; + dev->enqueue(entry); + ++queued_lines; + } + } - if (g_printBinary) { - printBinary(entry); - } else { - (void) processBuffer(entry); + if (result == 0) { + // we did our short timeout trick and there's nothing new + // print everything we have and wait for more data + sleep = true; + while (true) { + chooseFirst(devices, &dev); + if (dev == NULL) { + break; + } + if (g_tail_lines == 0 || queued_lines <= g_tail_lines) { + printNextEntry(dev); + } else { + skipNextEntry(dev); + } + --queued_lines; + } + + // the caller requested to just dump the log and exit + if (g_nonblock) { + return; + } + } else { + // print all that aren't the last in their list + sleep = false; + while (g_tail_lines == 0 || queued_lines > g_tail_lines) { + chooseFirst(devices, &dev); + if (dev == NULL || dev->queue->next == NULL) { + break; + } + if (g_tail_lines == 0) { + printNextEntry(dev); + } else { + skipNextEntry(dev); + } + --queued_lines; + } + } } +next: + ; } } @@ -228,10 +405,13 @@ static void show_help(const char *cmd) " brief process tag thread raw time threadtime long\n\n" " -c clear (flush) the entire log and exit\n" " -d dump the log and then exit (don't block)\n" + " -t print only the most recent lines (implies -d)\n" " -g get the size of the log's ring buffer and exit\n" - " -b request alternate ring buffer\n" - " ('main' (default), 'radio', 'events')\n" - " -B output the log in binary"); + " -b Request alternate ring buffer, 'main', 'system', 'radio'\n" + " or 'events'. Multiple -b parameters are allowed and the\n" + " results are interleaved. The default is -b main -b system.\n" + " -B output the log in binary\n" + " -C colored output"); fprintf(stderr,"\nfilterspecs are a series of \n" @@ -273,18 +453,24 @@ static int setLogFormat(const char * formatString) return 0; } +static void setColoredOutput() +{ + android_log_setColoredOutput(g_logformat); +} + extern "C" void logprint_run_tests(void); -int main (int argc, char **argv) +int main(int argc, char **argv) { - int logfd; int err; int hasSetLogFormat = 0; int clearLog = 0; int getLogSize = 0; int mode = O_RDONLY; - char *log_device = strdup("/dev/"LOGGER_LOG_MAIN); const char *forceFilters = NULL; + log_device_t* devices = NULL; + log_device_t* dev; + bool needBinary = false; g_logformat = android_log_format_new(); @@ -301,7 +487,7 @@ int main (int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "cdgsQf:r::n:v:b:B"); + ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:BC"); if (ret < 0) { break; @@ -319,21 +505,43 @@ int main (int argc, char **argv) break; case 'd': - mode |= O_NONBLOCK; + g_nonblock = true; + break; + + case 't': + g_nonblock = true; + g_tail_lines = atoi(optarg); break; case 'g': getLogSize = 1; break; - case 'b': - free(log_device); - log_device = - (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); - strcpy(log_device, LOG_FILE_DIR); - strcat(log_device, optarg); + case 'C': + setColoredOutput(); + break; + + case 'b': { + char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); + strcpy(buf, LOG_FILE_DIR); + strcat(buf, optarg); + + bool binary = strcmp(optarg, "events") == 0; + if (binary) { + needBinary = true; + } - android::g_isBinary = (strcmp(optarg, "events") == 0); + if (devices) { + dev = devices; + while (dev->next) { + dev = dev->next; + } + dev->next = new log_device_t(buf, binary, optarg[0]); + } else { + devices = new log_device_t(buf, binary, optarg[0]); + } + android::g_devCount++; + } break; case 'B': @@ -460,6 +668,19 @@ int main (int argc, char **argv) } } + if (!devices) { + devices = new log_device_t(strdup("/dev/" LOGGER_LOG_MAIN), false, 'm'); + android::g_devCount = 1; + int accessmode = + (mode & O_RDONLY) ? R_OK : 0 + | (mode & O_WRONLY) ? W_OK : 0; + // only add this if it's available + if (0 == access("/dev/" LOGGER_LOG_SYSTEM, accessmode)) { + devices->next = new log_device_t(strdup("/dev/" LOGGER_LOG_SYSTEM), false, 's'); + android::g_devCount++; + } + } + if (android::g_logRotateSizeKBytes != 0 && android::g_outputFileName == NULL ) { @@ -516,53 +737,63 @@ int main (int argc, char **argv) } } - logfd = open(log_device, mode); - if (logfd < 0) { - fprintf(stderr, "Unable to open log device '%s': %s\n", - log_device, strerror(errno)); - exit(EXIT_FAILURE); - } - - if (clearLog) { - int ret; - ret = android::clearLog(logfd); - if (ret) { - perror("ioctl"); + dev = devices; + while (dev) { + dev->fd = open(dev->device, mode); + if (dev->fd < 0) { + fprintf(stderr, "Unable to open log device '%s': %s\n", + dev->device, strerror(errno)); exit(EXIT_FAILURE); } - return 0; - } - if (getLogSize) { - int size, readable; - - size = android::getLogSize(logfd); - if (size < 0) { - perror("ioctl"); - exit(EXIT_FAILURE); + if (clearLog) { + int ret; + ret = android::clearLog(dev->fd); + if (ret) { + perror("ioctl"); + exit(EXIT_FAILURE); + } } - readable = android::getLogReadableSize(logfd); - if (readable < 0) { - perror("ioctl"); - exit(EXIT_FAILURE); + if (getLogSize) { + int size, readable; + + size = android::getLogSize(dev->fd); + if (size < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + readable = android::getLogReadableSize(dev->fd); + if (readable < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + printf("%s: ring buffer is %dKb (%dKb consumed), " + "max entry is %db, max payload is %db\n", dev->device, + size / 1024, readable / 1024, + (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); } - printf("ring buffer is %dKb (%dKb consumed), " - "max entry is %db, max payload is %db\n", - size / 1024, readable / 1024, - (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); - return 0; + dev = dev->next; + } + + if (getLogSize) { + exit(0); + } + if (clearLog) { + exit(0); } //LOG_EVENT_INT(10, 12345); //LOG_EVENT_LONG(11, 0x1122334455667788LL); //LOG_EVENT_STRING(0, "whassup, doc?"); - if (android::g_isBinary) + if (needBinary) android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); - android::readLogLines(logfd); + android::readLogLines(devices); return 0; } diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c index f00bfbf3d0b..7ec9bfa80e0 100644 --- a/logwrapper/logwrapper.c +++ b/logwrapper/logwrapper.c @@ -22,25 +22,26 @@ #include #include #include +#include #include "private/android_filesystem_config.h" #include "cutils/log.h" void fatal(const char *msg) { - fprintf(stderr, msg); - LOG(LOG_ERROR, "logwrapper", msg); + fprintf(stderr, "%s", msg); + LOG(LOG_ERROR, "logwrapper", "%s", msg); exit(-1); } void usage() { fatal( - "Usage: logwrapper [-x] BINARY [ARGS ...]\n" + "Usage: logwrapper [-d] BINARY [ARGS ...]\n" "\n" "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n" "the Android logging system. Tag is set to BINARY, priority is\n" "always LOG_INFO.\n" "\n" - "-x: Causes logwrapper to SIGSEGV when BINARY terminates\n" + "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n" " fault address is set to the status of wait()\n"); } @@ -51,6 +52,10 @@ void parent(const char *tag, int seg_fault_on_exit, int parent_read) { int a = 0; // start index of unprocessed data int b = 0; // end index of unprocessed data int sz; + + char *btag = basename(tag); + if (!strlen(btag)) btag = (char*) tag; + while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { sz += b; @@ -60,7 +65,7 @@ void parent(const char *tag, int seg_fault_on_exit, int parent_read) { buffer[b] = '\0'; } else if (buffer[b] == '\n') { buffer[b] = '\0'; - LOG(LOG_INFO, tag, &buffer[a]); + LOG(LOG_INFO, btag, "%s", &buffer[a]); a = b + 1; } } @@ -68,7 +73,7 @@ void parent(const char *tag, int seg_fault_on_exit, int parent_read) { if (a == 0 && b == sizeof(buffer) - 1) { // buffer is full, flush buffer[b] = '\0'; - LOG(LOG_INFO, tag, &buffer[a]); + LOG(LOG_INFO, btag, "%s", &buffer[a]); b = 0; } else if (a != b) { // Keep left-overs @@ -84,11 +89,11 @@ void parent(const char *tag, int seg_fault_on_exit, int parent_read) { // Flush remaining data if (a != b) { buffer[b] = '\0'; - LOG(LOG_INFO, tag, &buffer[a]); + LOG(LOG_INFO, btag, "%s", &buffer[a]); } status = 0xAAAA; if (wait(&status) != -1) { // Wait for child - if (WIFEXITED(status)) + if (WIFEXITED(status) && WEXITSTATUS(status)) LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk index 18f0ff3060f..153b25ea13f 100644 --- a/mkbootimg/Android.mk +++ b/mkbootimg/Android.mk @@ -9,4 +9,35 @@ LOCAL_MODULE := mkbootimg include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := eng +LOCAL_SRC_FILES := unpackbootimg.c +LOCAL_MODULE := unpackbootimg +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := mkbootimg.c +LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libc +LOCAL_MODULE := utility_mkbootimg +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_STEM := mkbootimg +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := unpackbootimg.c +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE := utility_unpackbootimg +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_STEM := unpackbootimg +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) + diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index 36426471101..424041ecb8e 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -64,6 +64,8 @@ int usage(void) " [ --cmdline ]\n" " [ --board ]\n" " [ --base
]\n" + " [ --pagesize ]\n" + " [ --ramdiskaddr
]\n" " -o|--output \n" ); return 1; @@ -71,7 +73,7 @@ int usage(void) -static unsigned char padding[2048] = { 0, }; +static unsigned char padding[4096] = { 0, }; int write_padding(int fd, unsigned pagesize, unsigned itemsize) { @@ -120,8 +122,6 @@ int main(int argc, char **argv) hdr.second_addr = 0x10F00000; hdr.tags_addr = 0x10000100; - hdr.page_size = pagesize; - while(argc > 0){ char *arg = argv[0]; char *val = argv[1]; @@ -146,12 +146,22 @@ int main(int argc, char **argv) hdr.ramdisk_addr = base + 0x01000000; hdr.second_addr = base + 0x00F00000; hdr.tags_addr = base + 0x00000100; + } else if(!strcmp(arg, "--ramdiskaddr")) { + hdr.ramdisk_addr = strtoul(val, 0, 16); } else if(!strcmp(arg, "--board")) { board = val; + } else if(!strcmp(arg,"--pagesize")) { + pagesize = strtoul(val, 0, 10); + if ((pagesize != 2048) && (pagesize != 4096) && (pagesize != 8192)) { + fprintf(stderr,"error: unsupported page size %d\n", pagesize); + return -1; + } } else { return usage(); } } + hdr.page_size = pagesize; + if(bootimg == 0) { fprintf(stderr,"error: no output filename specified\n"); diff --git a/mkbootimg/unpackbootimg.c b/mkbootimg/unpackbootimg.c new file mode 100644 index 00000000000..14721a99fd0 --- /dev/null +++ b/mkbootimg/unpackbootimg.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mincrypt/sha.h" +#include "bootimg.h" + +typedef unsigned char byte; + +int read_padding(FILE* f, unsigned itemsize, int pagesize) +{ + byte* buf = (byte*)malloc(sizeof(byte) * pagesize); + unsigned pagemask = pagesize - 1; + unsigned count; + + if((itemsize & pagemask) == 0) { + free(buf); + return 0; + } + + count = pagesize - (itemsize & pagemask); + + fread(buf, count, 1, f); + free(buf); + return count; +} + +void write_string_to_file(char* file, char* string) +{ + FILE* f = fopen(file, "w"); + fwrite(string, strlen(string), 1, f); + fwrite("\n", 1, 1, f); + fclose(f); +} + +int usage() { + printf("usage: unpackbootimg\n"); + printf("\t-i|--input boot.img\n"); + printf("\t[ -o|--output output_directory]\n"); + printf("\t[ -p|--pagesize ]\n"); + return 0; +} + +int main(int argc, char** argv) +{ + char tmp[PATH_MAX]; + char* directory = "./"; + char* filename = NULL; + int pagesize = 0; + + argc--; + argv++; + while(argc > 0){ + char *arg = argv[0]; + char *val = argv[1]; + argc -= 2; + argv += 2; + if(!strcmp(arg, "--input") || !strcmp(arg, "-i")) { + filename = val; + } else if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) { + directory = val; + } else if(!strcmp(arg, "--pagesize") || !strcmp(arg, "-p")) { + pagesize = strtoul(val, 0, 16); + } else { + return usage(); + } + } + + if (filename == NULL) { + return usage(); + } + + int total_read = 0; + FILE* f = fopen(filename, "rb"); + boot_img_hdr header; + + //printf("Reading header...\n"); + int i; + for (i = 0; i <= 512; i++) { + fseek(f, i, SEEK_SET); + fread(tmp, BOOT_MAGIC_SIZE, 1, f); + if (memcmp(tmp, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) + break; + } + total_read = i; + if (i > 512) { + printf("Android boot magic not found.\n"); + return 1; + } + fseek(f, i, SEEK_SET); + printf("Android magic found at: %d\n", i); + + fread(&header, sizeof(header), 1, f); + printf("BOARD_KERNEL_CMDLINE %s\n", header.cmdline); + printf("BOARD_KERNEL_BASE %08x\n", header.kernel_addr - 0x00008000); + printf("BOARD_PAGE_SIZE %d\n", header.page_size); + + if (pagesize == 0) { + pagesize = header.page_size; + } + + //printf("cmdline...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-cmdline"); + write_string_to_file(tmp, header.cmdline); + + //printf("base...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-base"); + char basetmp[200]; + sprintf(basetmp, "%08x", header.kernel_addr - 0x00008000); + write_string_to_file(tmp, basetmp); + + //printf("pagesize...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-pagesize"); + char pagesizetmp[200]; + sprintf(pagesizetmp, "%d", header.page_size); + write_string_to_file(tmp, pagesizetmp); + + total_read += sizeof(header); + //printf("total read: %d\n", total_read); + total_read += read_padding(f, sizeof(header), pagesize); + + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-zImage"); + FILE *k = fopen(tmp, "wb"); + byte* kernel = (byte*)malloc(header.kernel_size); + //printf("Reading kernel...\n"); + fread(kernel, header.kernel_size, 1, f); + total_read += header.kernel_size; + fwrite(kernel, header.kernel_size, 1, k); + fclose(k); + + //printf("total read: %d\n", header.kernel_size); + total_read += read_padding(f, header.kernel_size, pagesize); + + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-ramdisk.gz"); + FILE *r = fopen(tmp, "wb"); + byte* ramdisk = (byte*)malloc(header.ramdisk_size); + //printf("Reading ramdisk...\n"); + fread(ramdisk, header.ramdisk_size, 1, f); + total_read += header.ramdisk_size; + fwrite(ramdisk, header.ramdisk_size, 1, r); + fclose(r); + + fclose(f); + + //printf("Total Read: %d\n", total_read); + return 0; +} \ No newline at end of file diff --git a/netcfg/netcfg.c b/netcfg/netcfg.c index fc9cf48b041..3738f249560 100644 --- a/netcfg/netcfg.c +++ b/netcfg/netcfg.c @@ -19,17 +19,14 @@ #include #include #include +#include +#include + +#include +#include static int verbose = 0; -int ifc_init(); -void ifc_close(); -int ifc_up(char *iname); -int ifc_down(char *iname); -int ifc_remove_host_routes(char *iname); -int ifc_remove_default_route(char *iname); -int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags); -int do_dhcp(char *iname); void die(const char *reason) { @@ -37,36 +34,42 @@ void die(const char *reason) exit(1); } -const char *ipaddr(unsigned addr) +const char *ipaddr(in_addr_t addr) { - static char buf[32]; - - sprintf(buf,"%d.%d.%d.%d", - addr & 255, - ((addr >> 8) & 255), - ((addr >> 16) & 255), - (addr >> 24)); - return buf; + struct in_addr in_addr; + + in_addr.s_addr = addr; + return inet_ntoa(in_addr); } void usage(void) { fprintf(stderr,"usage: netcfg [ {dhcp|up|down}]\n"); - exit(1); + exit(1); } int dump_interface(const char *name) { - unsigned addr, mask, flags; - - if(ifc_get_info(name, &addr, &mask, &flags)) { + unsigned addr, flags; + unsigned char hwbuf[ETH_ALEN]; + int prefixLength; + + if(ifc_get_info(name, &addr, &prefixLength, &flags)) { return 0; } printf("%-8s %s ", name, flags & 1 ? "UP " : "DOWN"); - printf("%-16s", ipaddr(addr)); - printf("%-16s", ipaddr(mask)); - printf("0x%08x\n", flags); + printf("%40s", ipaddr(addr)); + printf("/%-4d", prefixLength); + printf("0x%08x ", flags); + if (!ifc_get_hwaddr(name, hwbuf)) { + int i; + for(i=0; i < (ETH_ALEN-1); i++) + printf("%02x:", hwbuf[i]); + printf("%02x\n", hwbuf[i]); + } else { + printf("\n"); + } return 0; } @@ -74,10 +77,10 @@ int dump_interfaces(void) { DIR *d; struct dirent *de; - + d = opendir("/sys/class/net"); if(d == 0) return -1; - + while((de = readdir(d))) { if(de->d_name[0] == '.') continue; dump_interface(de->d_name); @@ -86,6 +89,15 @@ int dump_interfaces(void) return 0; } +int set_hwaddr(const char *name, const char *asc) { + struct ether_addr *addr = ether_aton(asc); + if (!addr) { + printf("Failed to parse '%s'\n", asc); + return -1; + } + return ifc_set_hwaddr(name, addr->ether_addr_octet); +} + struct { const char *name; @@ -97,6 +109,7 @@ struct { "down", 1, ifc_down }, { "flhosts", 1, ifc_remove_host_routes }, { "deldefault", 1, ifc_remove_default_route }, + { "hwaddr", 2, set_hwaddr }, { 0, 0, 0 }, }; diff --git a/nexus/DhcpClient.cpp b/nexus/DhcpClient.cpp index a5654d28d58..713059d88f3 100644 --- a/nexus/DhcpClient.cpp +++ b/nexus/DhcpClient.cpp @@ -27,35 +27,15 @@ #include +#include +#include + #include "DhcpClient.h" #include "DhcpState.h" #include "DhcpListener.h" #include "IDhcpEventHandlers.h" #include "Controller.h" -extern "C" { -int ifc_disable(const char *ifname); -int ifc_add_host_route(const char *ifname, uint32_t addr); -int ifc_remove_host_routes(const char *ifname); -int ifc_set_default_route(const char *ifname, uint32_t gateway); -int ifc_get_default_route(const char *ifname); -int ifc_remove_default_route(const char *ifname); -int ifc_reset_connections(const char *ifname); -int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); - -int dhcp_do_request(const char *ifname, - in_addr_t *ipaddr, - in_addr_t *gateway, - in_addr_t *mask, - in_addr_t *dns1, - in_addr_t *dns2, - in_addr_t *server, - uint32_t *lease); -int dhcp_stop(const char *ifname); -int dhcp_release_lease(const char *ifname); -char *dhcp_get_errmsg(); -} - DhcpClient::DhcpClient(IDhcpEventHandlers *handlers) : mState(DhcpState::INIT), mHandlers(handlers) { mServiceManager = new ServiceManager(); diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 3bb226211e0..1a9e06f6d41 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -7,8 +7,12 @@ copy_from := \ etc/dbus.conf \ etc/hosts -ifeq ($(TARGET_PRODUCT),generic) -copy_from += etc/vold.conf +ifeq ($(TARGET_PRODUCT),full) +copy_from += etc/vold.fstab +endif + +ifeq ($(TARGET_PRODUCT),full_x86) +copy_from += etc/vold.fstab endif # the /system/etc/init.goldfish.sh is needed to enable emulator support @@ -37,8 +41,15 @@ file := $(TARGET_ROOT_OUT)/init.rc $(file) : $(LOCAL_PATH)/init.rc | $(ACP) $(transform-prebuilt-to-target) ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) endif +file := $(TARGET_ROOT_OUT)/ueventd.rc +$(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP) + $(transform-prebuilt-to-target) +ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) + # Just like /system/etc/init.goldfish.sh, the /init.godlfish.rc is here # to allow -user builds to properly run the dex pre-optimization pass in # the emulator. @@ -46,6 +57,13 @@ file := $(TARGET_ROOT_OUT)/init.goldfish.rc $(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP) $(transform-prebuilt-to-target) ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) + +file := $(TARGET_ROOT_OUT)/ueventd.goldfish.rc +$(file) : $(LOCAL_PATH)/etc/ueventd.goldfish.rc | $(ACP) + $(transform-prebuilt-to-target) +ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) # create some directories (some are mount points) DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \ diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc index 569555c652b..83b7f8ada3b 100644 --- a/rootdir/etc/init.goldfish.rc +++ b/rootdir/etc/init.goldfish.rc @@ -1,5 +1,12 @@ +on early-init + export EXTERNAL_STORAGE /mnt/sdcard + mkdir /mnt/sdcard 0000 system system + # for backwards compatibility + symlink /mnt/sdcard /sdcard + on boot setprop ARGH ARGH + setprop net.eth0.gw 10.0.2.2 setprop net.eth0.dns1 10.0.2.3 setprop net.gprs.local-ip 10.0.2.15 setprop ro.radio.use-ppp no @@ -16,6 +23,11 @@ on boot stop dund stop akmd +# start essential services + start qemud + start goldfish-logcat + start goldfish-setup + setprop ro.setupwizard.mode EMULATOR # enable Google-specific location features, @@ -36,6 +48,20 @@ on boot # something else. service goldfish-setup /system/etc/init.goldfish.sh + user root + group root + oneshot + +# The qemu-props program is used to set various system +# properties on boot. It must be run early during the boot +# process to avoid race conditions with other daemons that +# might read them (e.g. surface flinger), so define it in +# class 'core' +# +service qemu-props /system/bin/qemu-props + class core + user root + group root oneshot service qemud /system/bin/qemud @@ -46,7 +72,7 @@ service qemud /system/bin/qemud # program to check wether it runs on the emulator # if it does, it redirects its output to the device # named by the androidboot.console kernel option -# if not, is simply exit immediately +# if not, is simply exits immediately service goldfish-logcat /system/bin/logcat -Q oneshot diff --git a/rootdir/etc/init.goldfish.sh b/rootdir/etc/init.goldfish.sh index f1b801d9185..ece75b4298c 100755 --- a/rootdir/etc/init.goldfish.sh +++ b/rootdir/etc/init.goldfish.sh @@ -1,30 +1,53 @@ #!/system/bin/sh +# Setup networking when boot starts ifconfig eth0 10.0.2.15 netmask 255.255.255.0 up route add default gw 10.0.2.2 dev eth0 -qemud=`getprop.ro.kernel.android.qemud` -if test -z "$qemud"; then +# ro.kernel.android.qemud is normally set when we +# want the RIL (radio interface layer) to talk to +# the emulated modem through qemud. +# +# However, this will be undefined in two cases: +# +# - When we want the RIL to talk directly to a guest +# serial device that is connected to a host serial +# device by the emulator. +# +# - We don't want to use the RIL but the VM-based +# modem emulation that runs inside the guest system +# instead. +# +# The following detects the latter case and sets up the +# system for it. +# +qemud=`getprop ro.kernel.android.qemud` +case "$qemud" in + "") radio_ril=`getprop ro.kernel.android.ril` - if test -z "$radio_ril"; then + case "$radio_ril" in + "") # no need for the radio interface daemon # telephony is entirely emulated in Java setprop ro.radio.noril yes stop ril-daemon - fi -fi + ;; + esac + ;; +esac -num_dns=`getprop ro.kernel.android.ndns` +# Setup additionnal DNS servers if needed +num_dns=`getprop ro.kernel.ndns` case "$num_dns" in 2) setprop net.eth0.dns2 10.0.2.4 - ;; + ;; 3) setprop net.eth0.dns2 10.0.2.4 - setprop net.eth0.dns3 10.0.2.5 - ;; + setprop net.eth0.dns3 10.0.2.5 + ;; 4) setprop net.eth0.dns2 10.0.2.4 - setprop net.eth0.dns3 10.0.2.5 - setprop net.eth0.dns4 10.0.2.6 - ;; + setprop net.eth0.dns3 10.0.2.5 + setprop net.eth0.dns4 10.0.2.6 + ;; esac # disable boot animation for a faster boot sequence when needed @@ -34,10 +57,12 @@ case "$boot_anim" in ;; esac -# call 'qemu-props' to set system properties from the emulator. -# -/system/bin/qemu-props - -# this line doesn't really do anything useful. however without it the -# previous setprop doesn't seem to apply for some really odd reason -setprop ro.qemu.init.completed 1 +# set up the second interface (for inter-emulator connections) +# if required +my_ip=`getprop net.shared_net_ip` +case "$my_ip" in + "") + ;; + *) ifconfig eth1 "$my_ip" netmask 255.255.255.0 up + ;; +esac diff --git a/rootdir/etc/ueventd.goldfish.rc b/rootdir/etc/ueventd.goldfish.rc new file mode 100644 index 00000000000..8de70496231 --- /dev/null +++ b/rootdir/etc/ueventd.goldfish.rc @@ -0,0 +1,5 @@ +# These settings are specific to running under the Android emulator +/dev/qemu_trace 0666 system system +/dev/qemu_pipe 0666 system system +/dev/ttyS* 0666 system system +/proc 0666 system system diff --git a/rootdir/etc/vold.conf b/rootdir/etc/vold.conf deleted file mode 100644 index 4e045090ebf..00000000000 --- a/rootdir/etc/vold.conf +++ /dev/null @@ -1,10 +0,0 @@ -## vold configuration file for the 'generic' target - -volume_sdcard { - ## This is the direct uevent device path to the SD slot on the device - media_path /devices/platform/goldfish_mmc.0/mmc_host/mmc0 - - media_type mmc - mount_point /sdcard - ums_path /devices/platform/usb_mass_storage/lun0 -} diff --git a/rootdir/etc/vold.fstab b/rootdir/etc/vold.fstab new file mode 100644 index 00000000000..4aad8dca2e9 --- /dev/null +++ b/rootdir/etc/vold.fstab @@ -0,0 +1,24 @@ +## Vold 2.0 Generic fstab +## - San Mehat (san@android.com) +## + +####################### +## Regular device mount +## +## Format: dev_mount